8000 Provide a way for acting on responses to client->server requests (#178) · mirror-dump/python-lsp-server@12d437f · GitHub
[go: up one dir, main page]

Skip to content

Commit 12d437f

Browse files
authored
Provide a way for acting on responses to client->server requests (python-lsp#178)
* Responses * Responses * Responses * Responses * Responses
1 parent 106e760 commit 12d437f

File tree

2 files changed

+53
-26
lines changed

2 files changed

+53
-26
lines changed

pyls/server.py

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import uuid
55

6-
import jsonrpc
6+
from jsonrpc import jsonrpc2, JSONRPCResponseManager
77

88
log = logging.getLogger(__name__)
99

@@ -14,6 +14,8 @@ class JSONRPCServer(object):
1414
def __init__(self, rfile, wfile):
1515
self.rfile = rfile
1616
self.wfile = wfile
17+
18+
self._callbacks = {}
1719
self._shutdown = False
1820

1921
def exit(self):
@@ -27,56 +29,68 @@ def shutdown(self):
2729
log.debug("Server shut down, awaiting exit notification")
2830

2931
def handle(self):
30-
# VSCode wants us to keep the connection open, so let's handle messages in a loop
3132
while True:
3233
try:
3334
data = self._read_message()
3435
log.debug("Got message: %s", data)
3536

3637
if self._shutdown:
3738
# Handle only the exit notification when we're shut down
38-
jsonrpc.JSONRPCResponseManager.handle(data, {'exit': self.exit})
39+
JSONRPCResponseManager.handle(data, {'exit': self.exit})
3940
break
4041

41-
response = jsonrpc.JSONRPCResponseManager.handle(data, self)
42-
43-
if response is not None:
44-
self._write_message(response.data)
42+
if isinstance(data, bytes):
43+
data = data.decode("utf-8")
44+
45+
msg = json.loads(data)
46+
if 'method' in msg:
47+
# It's a notification or request
48+
# Dispatch to the thread pool for handling
49+
response = JSONRPCResponseManager.handle(data, self)
50+
if response is not None:
51+
self._write_message(response.data)
52+
else:
53+
# Otherwise, it's a response message
54+
on_result, on_error = self._callbacks.pop(msg['id'])
55+
if 'result' in msg and on_result:
56+
on_result(msg['result'])
57+
elif 'error' in msg and on_error:
58+
on_error(msg['error'])
4559
except Exception:
4660
log.exception("Language server exiting due to uncaught exception")
4761
break
4862

49-
def call(self, method, params=None):
50-
""" Call a method on the client. TODO: return the result. """
51-
log.debug("Sending request %s: %s", method, params)
52-
req = jsonrpc.jsonrpc2.JSONRPC20Request(method=method, params=params)
53-
req._id = str(uuid.uuid4())
63+
def call(self, method, params=None, on_result=None, on_error=None):
64+
"""Call a method on the client."""
65+
msg_id = str(uuid.uuid4())
66+
log.debug("Sending request %s: %s: %s", msg_id, method, params)
67+
req = jsonrpc2.JSONRPC20Request(method=method, params=params)
68+
req._id = msg_id
69+
70+
def _default_on_error(error):
71+
log.error("Call to %s failed with %s", method, error)
72+
73+
if not on_error:
74+
on_error = _default_on_error
75+
76+
self._callbacks[msg_id] = (on_result, on_error)
5477
self._write_message(req.data)
5578

5679
def notify(self, method, params=None):
5780
""" Send a notification to the client, expects no response. """
5881
log.debug("Sending notification %s: %s", method, params)
59-
req = jsonrpc.jsonrpc2.JSONRPC20Request(
82+
req = jsonrpc2.JSONRPC20Request(
6083
method=method, params=params, is_notification=True
6184
)
6285
self._write_message(req.data)
6386

64-
def _content_length(self, line):
65-
if line.startswith(b'Content-Length: '):
66-
_, value = line.split(b'Content-Length: ')
67-
value = value.strip()
68-
try:
69-
return int(value)
70-
except ValueError:
71-
raise ValueError("Invalid Content-Length header: {}".format(value))
72-
7387
def _read_message(self):
7488
line = self.rfile.readline()
7589

7690
if not line:
7791
raise EOFError()
7892

79-
content_length = self._content_length(line)
93+
content_length = _content_length(line)
8094

8195
# Blindly consume all header lines
6293 8296
while line and line.strip():
@@ -98,3 +112,14 @@ def _write_message(self, msg):
98112
)
99113
self.wfile.write(response.encode('utf-8'))
100114
self.wfile.flush()
115+
116+
117+
def _content_length(line):
118+
"""Extract the content length from an input line."""
119+
if line.startswith(b'Content-Length: '):
120+
_, value = line.split(b'Content-Length: ')
121+
value = value.strip()
122+
try:
123+
return int(value)
124+
except ValueError:
125+
raise ValueError("Invalid Content-Length header: {}".format(value))

pyls/workspace.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ def update_document(self, doc_uri, change, version=None):
6060
self._docs[doc_uri].apply_change(change)
6161
self._docs[doc_uri].version = version
6262

63-
def apply_edit(self, edit):
64-
# Note that lang_server.call currently doesn't return anything
65-
return self._lang_server.call(self.M_APPLY_EDIT, {'edit': edit})
63+
def apply_edit(self, edit, on_result=None, on_error=None):
64+
return self._lang_server.call(
65+
self.M_APPLY_EDIT, {'edit': edit},
66+
on_result=on_result, on_error=on_error
67+
)
6668

6769
def publish_diagnostics(self, doc_uri, diagnostics):
6870
params = {'uri': doc_uri, 'diagnostics': diagnostics}

0 commit comments

Comments
 (0)
0