|
4 | 4 | # Licensed under the terms of the MIT License |
5 | 5 | # (see spyder/__init__.py for details) |
6 | 6 |
|
7 | | -"""Spyder Language Server Protocol Client auxiliar decorators.""" |
| 7 | +""" |
| 8 | +Spyder Language Server Protocol Client auxiliary decorators. |
| 9 | +
|
| 10 | +send_request / send_notification / send_response are now *marker* decorators: |
| 11 | +they tag a method with the LSP method name it corresponds to and its kind |
| 12 | +('request', 'notification', 'response'). The actual sending is done by |
| 13 | +LSPClient.perform_request(), which inspects these attributes via the |
| 14 | +sender_registry built by @class_register. |
| 15 | +
|
| 16 | +Each tagged method is expected to: |
| 17 | + - Accept Spyder's internal params dict. |
| 18 | + - Return a lsprotocol typed object (the LSP params to send), or None to |
| 19 | + cancel the operation. |
| 20 | +
|
| 21 | +@handles(method_name) marks a method as the handler for an LSP response or |
| 22 | +server-initiated notification carrying that method name. |
| 23 | +
|
| 24 | +@class_register scans a class at definition time and builds: |
| 25 | + - handler_registry : {method_name: handler_method_name} |
| 26 | + - sender_registry : {method_name: sender_method_name} |
| 27 | + - notification_registry : {method_name}; subset of sender methods that are |
| 28 | + notifications (no response expected). |
| 29 | +""" |
8 | 30 |
|
9 | 31 | import functools |
10 | | -from spyder.plugins.completion.providers.languageserver.transport import ( |
11 | | - MessageKind) |
12 | 32 |
|
13 | 33 |
|
14 | 34 | def send_request(req=None, method=None): |
15 | | - """Send message as a proper JSON-RPC request.""" |
| 35 | + """Mark *req* as a method that builds params for an LSP request.""" |
16 | 36 | if req is None: |
17 | 37 | return functools.partial(send_request, method=method) |
18 | | - |
19 | | - return send_message(req, method, kind=MessageKind.REQUEST) |
| 38 | + req._sends = method |
| 39 | + req._kind = 'request' |
| 40 | + return req |
20 | 41 |
|
21 | 42 |
|
22 | 43 | def send_notification(req=None, method=None): |
23 | | - """Send message as a proper JSON-RPC notification.""" |
| 44 | + """Mark *req* as a method that builds params for an LSP notification.""" |
24 | 45 | if req is None: |
25 | 46 | return functools.partial(send_notification, method=method) |
26 | | - |
27 | | - return send_message(req, method, kind=MessageKind.NOTIFICATION) |
| 47 | + req._sends = method |
| 48 | + req._kind = 'notification' |
| 49 | + return req |
28 | 50 |
|
29 | 51 |
|
30 | 52 | def send_response(req=None, method=None): |
31 | | - """Send message as a proper JSON-RPC response.""" |
| 53 | + """ |
| 54 | + Mark *req* as a handler for a server-request that requires a response. |
| 55 | +
|
| 56 | + With pygls the response is sent automatically by the feature manager when |
| 57 | + the registered handler returns a value, so this decorator is retained |
| 58 | + only for semantic clarity and registry bookkeeping. |
| 59 | + """ |
32 | 60 | if req is None: |
33 | 61 | return functools.partial(send_response, method=method) |
34 | | - |
35 | | - return send_message(req, method, kind=MessageKind.RESPONSE) |
36 | | - |
37 | | - |
38 | | -def send_message(req=None, method=None, kind=MessageKind.REQUEST): |
39 | | - """Call function req and then send its results via ZMQ.""" |
40 | | - @functools.wraps(req) |
41 | | - def wrapper(self, *args, **kwargs): |
42 | | - params = req(self, *args, **kwargs) |
43 | | - _id = self.send(method, params, kind) |
44 | | - return _id |
45 | | - wrapper._sends = method |
46 | | - return wrapper |
| 62 | + req._sends = method |
| 63 | + req._kind = 'response' |
| 64 | + return req |
47 | 65 |
|
48 | 66 |
|
49 | 67 | def class_register(cls): |
50 | | - """Class decorator that allows to map LSP method names to class methods.""" |
| 68 | + """ |
| 69 | + Class decorator that builds handler and sender registries from decorated |
| 70 | + methods, enabling dynamic dispatch in LSPClient. |
| 71 | + """ |
51 | 72 | cls.handler_registry = {} |
52 | 73 | cls.sender_registry = {} |
| 74 | + cls.notification_registry = set() |
| 75 | + |
53 | 76 | for method_name in dir(cls): |
54 | 77 | method = getattr(cls, method_name) |
55 | 78 | if hasattr(method, '_handle'): |
56 | | - cls.handler_registry.update({method._handle: method_name}) |
| 79 | + cls.handler_registry[method._handle] = method_name |
57 | 80 | if hasattr(method, '_sends'): |
58 | | - cls.sender_registry.update({method._sends: method_name}) |
| 81 | + cls.sender_registry[method._sends] = method_name |
| 82 | + if getattr(method, '_kind', 'request') in ('notification', 'response'): |
| 83 | + cls.notification_registry.add(method._sends) |
| 84 | + |
59 | 85 | return cls |
60 | 86 |
|
61 | 87 |
|
62 | 88 | def handles(method_name): |
63 | | - """Assign an LSP method name to a python handler.""" |
| 89 | + """Tag a method as the handler for LSP *method_name* responses/notifications.""" |
64 | 90 | def wrapper(func): |
65 | 91 | func._handle = method_name |
66 | 92 | return func |
|
0 commit comments