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

Commit

Permalink
Update to jsonrpc lib
Browse files Browse the repository at this point in the history
This updates the project to use jsonrpc
module from python-language-server.

Closes #27
  • Loading branch information
ksdme committed May 11, 2018
1 parent de0098b commit 351c97d
Show file tree
Hide file tree
Showing 7 changed files with 515 additions and 307 deletions.
148 changes: 0 additions & 148 deletions coala_langserver/jsonrpc.py

This file was deleted.

121 changes: 68 additions & 53 deletions coala_langserver/langserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,84 +3,76 @@
import socketserver
import traceback

from .jsonrpc import JSONRPC2Connection, ReadWriter, TCPReadWriter
from pyls.jsonrpc.endpoint import Endpoint
from pyls.jsonrpc.dispatchers import MethodDispatcher
from pyls.jsonrpc.streams import JsonRpcStreamReader
from pyls.jsonrpc.streams import JsonRpcStreamWriter
from coala_utils.decorators import enforce_signature
from .log import log
from .coalashim import run_coala_with_specific_file
from .uri import path_from_uri
from .diagnostic import output_to_diagnostics


class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class _StreamHandlerWrapper(socketserver.StreamRequestHandler, object):
"""
A wrapper class that is used to construct a custom handler class.
"""

delegate = None

class LangserverTCPTransport(socketserver.StreamRequestHandler):
def setup(self):
super(_StreamHandlerWrapper, self).setup()
self.delegate = self.DELEGATE_CLASS(self.rfile, self.wfile)

def handle(self):
s = LangServer(conn=TCPReadWriter(self.rfile, self.wfile))
try:
s.listen()
except Exception as e:
tb = traceback.format_exc()
log('ERROR: {} {}'.format(e, tb))
self.delegate.start()


class LangServer(JSONRPC2Connection):
class LangServer(MethodDispatcher):
"""
Language server for coala base on JSON RPC.
"""

def __init__(self, conn=None):
super().__init__(conn=conn)
def __init__(self, rx, tx):
self.root_path = None
self._jsonrpc_stream_reader = JsonRpcStreamReader(rx)
self._jsonrpc_stream_writer = JsonRpcStreamWriter(tx)
self._endpoint = Endpoint(self, self._jsonrpc_stream_writer.write)
self._dispatchers = []
self._shutdown = False

def handle(self, _id, request):
"""
Handle the request from language client.
"""
log('REQUEST: ', request)
resp = None

if request['method'] == 'initialize':
resp = self.serve_initialize(request)
# TODO: Support did_change and did_change_watched_files.
# elif request["method"] == "textDocument/didChange":
# resp = self.serve_change(request)
# elif request["method"] == "workspace/didChangeWatchedFiles":
# resp = self.serve_did_change_watched_files(request)
elif request['method'] == 'textDocument/didSave':
self.serve_did_save(request)

if resp is not None:
self.write_response(request['id'], resp)

def serve_initialize(self, request):
def start(self):
self._jsonrpc_stream_reader.listen(self._endpoint.consume)

def m_initialize(self, rootUri=None, rootPath=None, **kargs):
"""
Serve for the initialization request.
"""
params = request['params']
# Notice that the root_path could be None.
if 'rootUri' in params:
self.root_path = path_from_uri(params['rootUri'])
elif 'rootPath' in params:
self.root_path = path_from_uri(params['rootPath'])
if rootUri is not None:
self.root_path = path_from_uri(rootUri)
elif rootPath is not None:
self.root_path = path_from_uri(rootPath)
return {
'capabilities': {
'textDocumentSync': 1
}
}

def serve_did_save(self, request):
def m_text_document__did_save(self, textDocument=None, **kargs):
"""
Serve for did_change request.
"""
params = request['params']
uri = params['textDocument']['uri']
uri = textDocument['uri']
path = path_from_uri(uri)
diagnostics = output_to_diagnostics(
run_coala_with_specific_file(self.root_path, path))
self.send_diagnostics(path, diagnostics)

def m_shutdown(self, **_kwargs):
self._shutdown = True

# TODO: Support did_change and did_change_watched_files.
# def serve_change(self, request):
# '""Serve for the request of documentation changed.""'
Expand Down Expand Up @@ -110,7 +102,38 @@ def send_diagnostics(self, path, diagnostics):
'uri': 'file://{0}'.format(path),
'diagnostics': _diagnostics,
}
self.send_notification('textDocument/publishDiagnostics', params)
self._endpoint.notify('textDocument/publishDiagnostics', params=params)


@enforce_signature
def start_tcp_lang_server(handler_class: LangServer, bind_addr, port):
# Construct a custom wrapper class around the user's handler_class
wrapper_class = type(
handler_class.__name__ + 'Handler',
(_StreamHandlerWrapper,),
{'DELEGATE_CLASS': handler_class},
)

try:
server = socketserver.TCPServer((bind_addr, port), wrapper_class)
except Exception as e:
log('Fatal Exception: {}'.format(e))
sys.exit(1)

log('Serving {} on ({}, {})'.format(
handler_class.__name__, bind_addr, port))
try:
server.serve_forever()
finally:
log('Shutting down')
server.server_close()


@enforce_signature
def start_io_lang_server(handler_class: LangServer, rstream, wstream):
log('Starting {} IO language server'.format(handler_class.__name__))
server = handler_class(rstream, wstream)
server.start()


def main():
Expand All @@ -123,18 +146,10 @@ def main():
args = parser.parse_args()

if args.mode == 'stdio':
log('Reading on stdin, writing on stdout')
s = LangServer(conn=ReadWriter(sys.stdin, sys.stdout))
s.listen()
start_io_lang_server(LangServer, sys.stdin.buffer, sys.stdout.buffer)
elif args.mode == 'tcp':
host, addr = '0.0.0.0', args.addr
log('Accepting TCP connections on {}:{}'.format(host, addr))
ThreadingTCPServer.allow_reuse_address = True
s = ThreadingTCPServer((host, addr), LangserverTCPTransport)
try:
s.serve_forever()
finally:
s.shutdown()
start_tcp_lang_server(LangServer, host, addr)


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
coala>=0.10.0.dev20170213201648
typing>=3.5.3.0
coala-bears>=0.10.0.dev20170215041744
python-language-server~=0.18.0
39 changes: 12 additions & 27 deletions tests/server.features/jsonrpc.feature
Original file line number Diff line number Diff line change
@@ -1,35 +1,20 @@
Feature: jsonrpc module
jsonrpc is a module of language-server.

Scenario: Test ReadWriter
Given the string
When I write it to ReadWriter
Then it should read from ReadWriter
Scenario: Test JsonRpcStreamWriter and JsonRpcStreamReader
Given the message
When I write it to JsonRpcStreamWriter
Then it should read from JsonRpcStreamReader

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

Scenario: Test TCPReadWriter
Given the string
When I write it to TCPReadWriter
Then it should read from TCPReadWriter

Scenario: Test TCPReadWriter
Given the string
When I write it to TCPReadWriter
Then it should readline from TCPReadWriter

Scenario: Test send_notification and read_message
Given the JSONRPC2Connection instance
When I write a notification to the JSONRPC2Connection
Then it should return the notification from JSONRPC2Connection

Scenario: Test write_response
Given the JSONRPC2Connection instance
When I write a response to the JSONRPC2Connection
Then it should return the response from JSONRPC2Connection
Scenario: Test rpc request and response
Given a request type rpc request
When I send rpc request using JsonRpcStreamWriter
Then it should invoke consumer and return response

# TODO: block until we have generantee the unique request.
# Scenario: Test send_request
Expand Down
Loading

0 comments on commit 351c97d

Please sign in to comment.