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

langserver.py: Update to use jsonrpc #32

Merged
merged 2 commits into from
May 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ coverage:
project:
default:
enabled: true
target: 83%
target: 95%
148 changes: 0 additions & 148 deletions coala_langserver/jsonrpc.py

This file was deleted.

111 changes: 63 additions & 48 deletions coala_langserver/langserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,52 @@
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do not use the preferred quotation marks.

Origin: QuotesBear, Section: python.

The issue can be fixed by applying the following patch:

--- a/tmp/tmp7wx9j6yx/coala_langserver/langserver.py
+++ b/tmp/tmp7wx9j6yx/coala_langserver/langserver.py
@@ -111,7 +111,7 @@
     try:
         server = socketserver.TCPServer((bind_addr, port), wrapper_class)
     except Exception as e:
-        log("Fatal Exception: {}".format(e))
+        log('Fatal Exception: {}'.format(e))
         sys.exit(1)
     try:
         log('Serving %s on (%s, %s)', handler_class.__name__, bind_addr, port)

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, **params):
"""
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'])
Expand All @@ -70,17 +60,19 @@ def serve_initialize(self, request):
}
}

def serve_did_save(self, request):
def m_text_document__did_save(self, **params):
"""
Serve for did_change request.
"""
params = request['params']
uri = params['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__))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new log call, also redundant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the reasons mentioned in the previous change request and maintaining uniformity I think I'll have to retain this log and remove the other redundant log call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the code is from Python Language Server, do we need to test it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an internal module of pyls, we should check for some form of primary breaking changes in API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM


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