Skip to content

Commit

Permalink
feat: add decorators for event handler registration
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <jfcherng@gmail.com>
  • Loading branch information
jfcherng committed Nov 18, 2020
1 parent 98f3d67 commit 6f9caf4
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 2 deletions.
4 changes: 4 additions & 0 deletions st3/lsp_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ._client_handler import ClientHandler
from ._client_handler import as_notification_handler, as_request_handler
from .api_wrapper_interface import ApiWrapperInterface
from .generic_client_handler import GenericClientHandler
from .npm_client_handler import NpmClientHandler
Expand All @@ -14,4 +15,7 @@
'ServerResourceInterface',
'ServerStatus'
'ServerNpmResource',
# decorator-related
'as_notification_handler',
'as_request_handler',
]
4 changes: 4 additions & 0 deletions st3/lsp_utils/_client_handler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .decorator import as_notification_handler, as_request_handler
from LSP.plugin import __version__ as lsp_version


Expand All @@ -8,4 +9,7 @@

__all__ = [
'ClientHandler',
# decorator-related
'as_notification_handler',
'as_request_handler',
]
27 changes: 26 additions & 1 deletion st3/lsp_utils/_client_handler/abstract_plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ..api_wrapper_interface import ApiWrapperInterface
from ..server_resource_interface import ServerStatus
from .decorator import HANDLER_MARKS
from .interface import ClientHandlerInterface
from LSP.plugin import AbstractPlugin
from LSP.plugin import ClientConfig
Expand All @@ -12,6 +13,7 @@
from LSP.plugin import WorkspaceFolder
from LSP.plugin.core.rpc import method2attr
from LSP.plugin.core.typing import Any, Callable, Dict, List, Optional, Tuple
import inspect
import sublime
import weakref

Expand Down Expand Up @@ -158,4 +160,27 @@ def _upgrade_languages_list(cls, languages):

def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.on_ready(ApiWrapper(self))
api = ApiWrapper(self)
self._register_custom_server_event_handlers(api)
self.on_ready(api)

def _register_custom_server_event_handlers(self, api: ApiWrapperInterface) -> None:
"""
Register decorator-style custom event handlers.
This method works as following steps:
1. Scan through all methods of this object.
2. If a method is decorated, it will has a "handler mark" attribute which is put by the decorator.
3. Register the method with wanted events, which are stored in the "handler mark" attribute.
:param api: The API instance for interacting with the server.
"""
for _, func in inspect.getmembers(self, predicate=inspect.isroutine):
# client_event is like "notification", "request"
for client_event, handler_mark in HANDLER_MARKS.items():
event_registrator = getattr(api, "on_" + client_event, None)
if callable(event_registrator):
server_events = getattr(func, handler_mark, []) # type: List[str]
for server_event in server_events:
event_registrator(server_event, func)
40 changes: 40 additions & 0 deletions st3/lsp_utils/_client_handler/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from LSP.plugin.core.typing import Any, Callable, Iterable, Union

__all__ = [
"HANDLER_MARKS",
"as_notification_handler",
"as_request_handler",
]

# the first argument is always "self"
T_HANDLER = Callable[[Any, Any], None]
T_SERVER_EVENTS = Union[str, Iterable[str]]

HANDLER_MARKS = {
"notification": "__handle_notification_events",
"request": "__handle_request_events",
}


def as_notification_handler(server_events: T_SERVER_EVENTS) -> Callable[[T_HANDLER], T_HANDLER]:
""" Marks the decorated function as a "notification" event handler. """

return _as_handler("notification", server_events)


def as_request_handler(server_events: T_SERVER_EVENTS) -> Callable[[T_HANDLER], T_HANDLER]:
""" Marks the decorated function as a "request" event handler. """

return _as_handler("request", server_events)


def _as_handler(client_event: str, server_events: T_SERVER_EVENTS) -> Callable[[T_HANDLER], T_HANDLER]:
""" Marks the decorated function as a event handler. """

server_events = [server_events] if isinstance(server_events, str) else list(server_events)

def decorator(func: T_HANDLER) -> T_HANDLER:
setattr(func, HANDLER_MARKS[client_event], server_events)
return func

return decorator
27 changes: 26 additions & 1 deletion st3/lsp_utils/_client_handler/language_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ..api_wrapper_interface import ApiWrapperInterface
from ..helpers import log_and_show_message
from ..server_resource_interface import ServerStatus
from .decorator import HANDLER_MARKS
from .interface import ClientHandlerInterface
from LSP.plugin import ClientConfig
from LSP.plugin import LanguageHandler
Expand All @@ -10,6 +11,7 @@
from LSP.plugin import Response
from LSP.plugin.core.typing import Any, Callable, Dict, Optional
from sublime_lib import ActivityIndicator
import inspect
import sublime

__all__ = ['ClientHandler']
Expand Down Expand Up @@ -82,7 +84,9 @@ def on_start(cls, window: sublime.Window) -> bool:
return True

def on_initialized(self, client) -> None:
self.on_ready(ApiWrapper(client))
api = ApiWrapper(client)
self._register_custom_server_event_handlers(api)
self.on_ready(api)

# --- ClientHandlerInterface --------------------------------------------------------------------------------------

Expand Down Expand Up @@ -144,3 +148,24 @@ def __init__(self):
# Will be a no-op if already ran.
# See https://github.com/sublimelsp/LSP/issues/899
self.setup()

def _register_custom_server_event_handlers(self, api: ApiWrapperInterface) -> None:
"""
Register decorator-style custom event handlers.
This method works as following steps:
1. Scan through all methods of this object.
2. If a method is decorated, it will has a "handler mark" attribute which is put by the decorator.
3. Register the method with wanted events, which are stored in the "handler mark" attribute.
:param api: The API instance for interacting with the server.
"""
for _, func in inspect.getmembers(self, predicate=inspect.isroutine):
# client_event is like "notification", "request"
for client_event, handler_mark in HANDLER_MARKS.items():
event_registrator = getattr(api, "on_" + client_event, None)
if callable(event_registrator):
server_events = getattr(func, handler_mark, []) # type: List[str]
for server_event in server_events:
event_registrator(server_event, func)

0 comments on commit 6f9caf4

Please sign in to comment.