Skip to content
This repository was archived by the owner on Feb 1, 2019. It is now read-only.

Commit bd12657

Browse files
committed
Update to jsonrpc lib
This updates the project to use jsonrpc module from python-language-server. Closes #27
1 parent de0098b commit bd12657

File tree

7 files changed

+510
-302
lines changed

7 files changed

+510
-302
lines changed

coala_langserver/jsonrpc.py

Lines changed: 0 additions & 148 deletions
This file was deleted.

coala_langserver/langserver.py

Lines changed: 63 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,62 +3,52 @@
33
import socketserver
44
import traceback
55

6-
from .jsonrpc import JSONRPC2Connection, ReadWriter, TCPReadWriter
6+
from pyls.jsonrpc.endpoint import Endpoint
7+
from pyls.jsonrpc.dispatchers import MethodDispatcher
8+
from pyls.jsonrpc.streams import JsonRpcStreamReader
9+
from pyls.jsonrpc.streams import JsonRpcStreamWriter
10+
from coala_utils.decorators import enforce_signature
711
from .log import log
812
from .coalashim import run_coala_with_specific_file
913
from .uri import path_from_uri
1014
from .diagnostic import output_to_diagnostics
1115

1216

13-
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
14-
pass
17+
class _StreamHandlerWrapper(socketserver.StreamRequestHandler, object):
18+
"""
19+
A wrapper class that is used to construct a custom handler class.
20+
"""
1521

22+
delegate = None
1623

17-
class LangserverTCPTransport(socketserver.StreamRequestHandler):
24+
def setup(self):
25+
super(_StreamHandlerWrapper, self).setup()
26+
self.delegate = self.DELEGATE_CLASS(self.rfile, self.wfile)
1827

1928
def handle(self):
20-
s = LangServer(conn=TCPReadWriter(self.rfile, self.wfile))
21-
try:
22-
s.listen()
23-
except Exception as e:
24-
tb = traceback.format_exc()
25-
log('ERROR: {} {}'.format(e, tb))
29+
self.delegate.start()
2630

2731

28-
class LangServer(JSONRPC2Connection):
32+
class LangServer(MethodDispatcher):
2933
"""
3034
Language server for coala base on JSON RPC.
3135
"""
3236

33-
def __init__(self, conn=None):
34-
super().__init__(conn=conn)
37+
def __init__(self, rx, tx):
3538
self.root_path = None
39+
self._jsonrpc_stream_reader = JsonRpcStreamReader(rx)
40+
self._jsonrpc_stream_writer = JsonRpcStreamWriter(tx)
41+
self._endpoint = Endpoint(self, self._jsonrpc_stream_writer.write)
42+
self._dispatchers = []
43+
self._shutdown = False
3644

37-
def handle(self, _id, request):
38-
"""
39-
Handle the request from language client.
40-
"""
41-
log('REQUEST: ', request)
42-
resp = None
43-
44-
if request['method'] == 'initialize':
45-
resp = self.serve_initialize(request)
46-
# TODO: Support did_change and did_change_watched_files.
47-
# elif request["method"] == "textDocument/didChange":
48-
# resp = self.serve_change(request)
49-
# elif request["method"] == "workspace/didChangeWatchedFiles":
50-
# resp = self.serve_did_change_watched_files(request)
51-
elif request['method'] == 'textDocument/didSave':
52-
self.serve_did_save(request)
53-
54-
if resp is not None:
55-
self.write_response(request['id'], resp)
56-
57-
def serve_initialize(self, request):
45+
def start(self):
46+
self._jsonrpc_stream_reader.listen(self._endpoint.consume)
47+
48+
def m_initialize(self, **params):
5849
"""
5950
Serve for the initialization request.
6051
"""
61-
params = request['params']
6252
# Notice that the root_path could be None.
6353
if 'rootUri' in params:
6454
self.root_path = path_from_uri(params['rootUri'])
@@ -70,17 +60,19 @@ def serve_initialize(self, request):
7060
}
7161
}
7262

73-
def serve_did_save(self, request):
63+
def m_text_document__did_save(self, **params):
7464
"""
7565
Serve for did_change request.
7666
"""
77-
params = request['params']
7867
uri = params['textDocument']['uri']
7968
path = path_from_uri(uri)
8069
diagnostics = output_to_diagnostics(
8170
run_coala_with_specific_file(self.root_path, path))
8271
self.send_diagnostics(path, diagnostics)
8372

73+
def m_shutdown(self, **_kwargs):
74+
self._shutdown = True
75+
8476
# TODO: Support did_change and did_change_watched_files.
8577
# def serve_change(self, request):
8678
# '""Serve for the request of documentation changed.""'
@@ -110,7 +102,38 @@ def send_diagnostics(self, path, diagnostics):
110102
'uri': 'file://{0}'.format(path),
111103
'diagnostics': _diagnostics,
112104
}
113-
self.send_notification('textDocument/publishDiagnostics', params)
105+
self._endpoint.notify('textDocument/publishDiagnostics', params=params)
106+
107+
108+
@enforce_signature
109+
def start_tcp_lang_server(handler_class: LangServer, bind_addr, port):
110+
# Construct a custom wrapper class around the user's handler_class
111+
wrapper_class = type(
112+
handler_class.__name__ + 'Handler',
113+
(_StreamHandlerWrapper,),
114+
{'DELEGATE_CLASS': handler_class},
115+
)
116+
117+
try:
118+
server = socketserver.TCPServer((bind_addr, port), wrapper_class)
119+
except Exception as e:
120+
log('Fatal Exception: {}'.format(e))
121+
sys.exit(1)
122+
123+
log('Serving {} on ({}, {})'.format(
124+
handler_class.__name__, bind_addr, port))
125+
try:
126+
server.serve_forever()
127+
finally:
128+
log('Shutting down')
129+
server.server_close()
130+
131+
132+
@enforce_signature
133+
def start_io_lang_server(handler_class: LangServer, rstream, wstream):
134+
log('Starting {} IO language server'.format(handler_class.__name__))
135+
server = handler_class(rstream, wstream)
136+
server.start()
114137

115138

116139
def main():
@@ -123,18 +146,10 @@ def main():
123146
args = parser.parse_args()
124147

125148
if args.mode == 'stdio':
126-
log('Reading on stdin, writing on stdout')
127-
s = LangServer(conn=ReadWriter(sys.stdin, sys.stdout))
128-
s.listen()
149+
start_io_lang_server(LangServer, sys.stdin.buffer, sys.stdout.buffer)
129150
elif args.mode == 'tcp':
130151
host, addr = '0.0.0.0', args.addr
131-
log('Accepting TCP connections on {}:{}'.format(host, addr))
132-
ThreadingTCPServer.allow_reuse_address = True
133-
s = ThreadingTCPServer((host, addr), LangserverTCPTransport)
134-
try:
135-
s.serve_forever()
136-
finally:
137-
s.shutdown()
152+
start_tcp_lang_server(LangServer, host, addr)
138153

139154

140155
if __name__ == '__main__':

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
coala>=0.10.0.dev20170213201648
22
typing>=3.5.3.0
33
coala-bears>=0.10.0.dev20170215041744
4+
python-language-server~=0.18.0

tests/server.features/jsonrpc.feature

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
11
Feature: jsonrpc module
22
jsonrpc is a module of language-server.
33

4-
Scenario: Test ReadWriter
5-
Given the string
6-
When I write it to ReadWriter
7-
Then it should read from ReadWriter
4+
Scenario: Test JsonRpcStreamWriter and JsonRpcStreamReader
5+
Given the message
6+
When I write it to JsonRpcStreamWriter
7+
Then it should read from JsonRpcStreamReader
88

9-
Scenario: Test ReadWriter
10-
Given the string
11-
When I write it to ReadWriter
12-
Then it should readline from ReadWriter
9+
Scenario: Test notification and disptacher
10+
Given a notification type rpc request
11+
When I send rpc request using JsonRpcStreamWriter
12+
Then it should invoke the notification consumer with args
1313

14-
Scenario: Test TCPReadWriter
15-
Given the string
16-
When I write it to TCPReadWriter
17-
Then it should read from TCPReadWriter
18-
19-
Scenario: Test TCPReadWriter
20-
Given the string
21-
When I write it to TCPReadWriter
22-
Then it should readline from TCPReadWriter
23-
24-
Scenario: Test send_notification and read_message
25-
Given the JSONRPC2Connection instance
26-
When I write a notification to the JSONRPC2Connection
27-
Then it should return the notification from JSONRPC2Connection
28-
29-
Scenario: Test write_response
30-
Given the JSONRPC2Connection instance
31-
When I write a response to the JSONRPC2Connection
32-
Then it should return the response from JSONRPC2Connection
14+
Scenario: Test rpc request and response
15+
Given a request type rpc request
16+
When I send rpc request using JsonRpcStreamWriter
17+
Then it should invoke consumer and return response
3318

3419
# TODO: block until we have generantee the unique request.
3520
# Scenario: Test send_request

0 commit comments

Comments
 (0)