Merge pull request #4 from yeger00/small-fixes

Small fixes
This commit is contained in:
Yeger 2019-06-09 23:48:47 +03:00 committed by GitHub
commit 3ee6fd666c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 86 additions and 58 deletions

View File

@ -9,8 +9,8 @@ python:
# - "3.6-dev" # 3.6 development branch
# - "3.7-dev" # 3.7 development branch
# command to install dependencies
#install:
# - pip install -r requirements.txt
install:
- pip install -r requirements.txt
# command to run tests
script:
- pytest

View File

@ -1,7 +1,7 @@
import pylspclient
import subprocess
import threading
import argparse
class ReadPipe(threading.Thread):
def __init__(self, pipe):
@ -15,8 +15,11 @@ class ReadPipe(threading.Thread):
line = self.pipe.readline().decode('utf-8')
if __name__ == "__main__":
clangd_path = "/usr/bin/clangd-6.0"
p = subprocess.Popen(clangd_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
parser = argparse.ArgumentParser(description='pylspclient example with clangd')
parser.add_argument('clangd_path', type=str, default="/usr/bin/clangd-6.0",
help='the clangd path', nargs="?")
args = parser.parse_args()
p = subprocess.Popen([args.clangd_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
read_pipe = ReadPipe(p.stderr)
read_pipe.start()
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(p.stdin, p.stdout)
@ -136,7 +139,7 @@ if __name__ == "__main__":
25,
26]}},'workspaceEdit': {'documentChanges': True},
'workspaceFolders': True}}
root_uri = 'file:///home/osboxes/projects/ctest'
root_uri = 'file:///home/osboxes/projects/ctest/'
workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}]
print(lsp_client.initialize(p.pid, None, root_uri, None, capabilities, "off", workspace_folders))
print(lsp_client.initialized())
@ -147,11 +150,16 @@ if __name__ == "__main__":
languageId = pylspclient.lsp_structs.LANGUAGE_IDENTIFIER.C
version = 1
lsp_client.didOpen(pylspclient.lsp_structs.TextDocumentItem(uri, languageId, version, text))
# documentSymbol is supported from version 8.
#lsp_client.documentSymbol(pylspclient.lsp_structs.TextDocumentIdentifier(uri))
try:
symbols = lsp_client.documentSymbol(pylspclient.lsp_structs.TextDocumentIdentifier(uri))
for symbol in symbols:
print(symbol.name)
except pylspclient.lsp_structs.ResponseError:
# documentSymbol is supported from version 8.
print("Failed to document symbols")
lsp_client.definition(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(15, 4))
lsp_client.signatureHelp(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(15, 4))
lsp_client.completion(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(15, 4), pylspclient.lsp_structs.CompletionContext(pylspclient.lsp_structs.CompletionTriggerKind.Invoked))
lsp_client.definition(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4))
lsp_client.signatureHelp(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4))
lsp_client.completion(pylspclient.lsp_structs.TextDocumentIdentifier(uri), pylspclient.lsp_structs.Position(14, 4), pylspclient.lsp_structs.CompletionContext(pylspclient.lsp_structs.CompletionTriggerKind.Invoked))
lsp_client.shutdown()
lsp_client.exit()

View File

@ -5,15 +5,18 @@ from pylspclient import lsp_structs
import threading
JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}"
JSON_RPC_RES_REGEX = "Content-Length: ([0-9]*)\r\n"
LEN_HEADER = "Content-Length: "
TYPE_HEADER = "Content-Type: "
# TODO: add content-type
class MyEncoder(json.JSONEncoder):
class MyEncoder(json.JSONEncoder):
"""
Encodes an object in JSON
"""
def default(self, o):
def default(self, o): # pylint: disable=E0202
return o.__dict__
@ -59,20 +62,33 @@ class JsonRpcEndpoint(object):
:return: a message
'''
with self.read_lock:
line = self.stdout.readline()
if not line:
return None
line = line.decode("utf-8")
# TODO: handle content type as well.
match = re.match(JSON_RPC_RES_REGEX, line)
if match is None or not match.groups():
raise RuntimeError("Bad header: " + line)
size = int(match.groups()[0])
line = self.stdout.readline()
if not line:
return None
line = line.decode("utf-8")
if line != "\r\n":
raise RuntimeError("Bad header: missing newline")
jsonrpc_res = self.stdout.read(size).decode("utf-8")
message_size = None
while True:
#read header
line = self.stdout.readline()
if not line:
# server quit
return None
line = line.decode("utf-8")
if not line.endswith("\r\n"):
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.ParseError, "Bad header: missing newline")
#remove the "\r\n"
line = line[:-2]
if line == "":
# done with the headers
break
elif line.startswith(LEN_HEADER):
line = line[len(LEN_HEADER):]
if not line.isdigit():
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.ParseError, "Bad header: size is not int")
message_size = int(line)
elif line.startswith(TYPE_HEADER):
# nothing todo with type for now.
pass
else:
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.ParseError, "Bad header: unkown header")
if not message_size:
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.ParseError, "Bad header: missing size")
jsonrpc_res = self.stdout.read(message_size).decode("utf-8")
return json.loads(jsonrpc_res)

View File

@ -46,15 +46,16 @@ class LspEndpoint(threading.Thread):
if rpc_id:
# a call for method
if method not in self.method_callbacks:
raise lsp_structs.ResponseError("Method not found: {method}".format(method=method), lsp_structs.ErrorCodes.MethodNotFound)
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.MethodNotFound, "Method not found: {method}".format(method=method))
result = self.method_callbacks[method](params)
self.send_response(rpc_id, result, None)
else:
# a call for notify
if method not in self.notify_callbacks:
raise lsp_structs.ResponseError("Method not found: {method}".format(method=method), lsp_structs.ErrorCodes.MethodNotFound)
self.notify_callbacks[method](params)
# Have nothing to do with this.
print("Notify method not found: {method}.".format(method=method))
else:
self.notify_callbacks[method](params)
else:
self.handle_result(rpc_id, result, error)
except lsp_structs.ResponseError as e:

View File

@ -1,3 +1,6 @@
import enum
def to_type(o, new_type):
'''
Helper funciton that receives an object or a dict and convert it to a new given type.
@ -149,7 +152,7 @@ class TextDocumentPositionParams(object):
self.position = position
class LANGUAGE_IDENTIFIER:
class LANGUAGE_IDENTIFIER(object):
BAT="bat"
BIBTEX="bibtex"
CLOJURE="clojure"
@ -201,7 +204,7 @@ class LANGUAGE_IDENTIFIER:
YAML="yaml"
class SymbolKind(object):
class SymbolKind(enum.Enum):
File = 1
Module = 2
Namespace = 3
@ -256,7 +259,7 @@ class SymbolInformation(object):
:param bool deprecated: Indicates if this symbol is deprecated.
"""
self.name = name
self.kind = kind
self.kind = SymbolKind(kind)
self.deprecated = deprecated
self.location = to_type(location, Location)
self.containerName = containerName
@ -426,7 +429,7 @@ class CompletionList(object):
self.isIncomplete = isIncomplete
self.items = [to_type(i, CompletionItem) for i in items]
class ErrorCodes(object):
class ErrorCodes(enum.Enum):
# Defined by JSON RPC
ParseError = -32700
InvalidRequest = -32600
@ -447,4 +450,4 @@ class ResponseError(Exception):
self.code = code
self.message = message
if data:
self.data = data
self.data = data

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
enum ; python_version < '3.4'

View File

@ -20,6 +20,7 @@ class PyTest(TestCommand):
errno = pytest.main(self.pytest_args)
sys.exit(errno)
setup(
name="pylspclient",
version="0.0.2",

View File

@ -1,29 +1,13 @@
import os
#from pytest_mock import mocker
import pytest
import pylspclient
class StdinMock(object):
def write(self, s):
pass
def flush(self):
pass
class StdoutMock(object):
def readline(self):
pass
def read(self):
pass
import pytest
JSON_RPC_RESULT_LIST = [
'Content-Length: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"),
'Content-Length: 40\r\n\r\n{"key_num": 1, "key_str": "some_string"}'.encode("utf-8")
]
def test_send_sanity():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
@ -67,8 +51,21 @@ def test_recv_wrong_header():
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(None, pipein)
pipeout.write('Contentength: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"))
pipeout.flush()
with pytest.raises(RuntimeError):
with pytest.raises(pylspclient.lsp_structs.ResponseError):
result = json_rpc_endpoint.recv_response()
print("should never get here", result)
def test_recv_missing_size():
pipein, pipeout = os.pipe()
pipein = os.fdopen(pipein, "rb")
pipeout = os.fdopen(pipeout, "wb")
json_rpc_endpoint = pylspclient.JsonRpcEndpoint(None, pipein)
pipeout.write('Content-Type: 40\r\n\r\n{"key_str": "some_string", "key_num": 1}'.encode("utf-8"))
pipeout.flush()
with pytest.raises(pylspclient.lsp_structs.ResponseError):
result = json_rpc_endpoint.recv_response()
print("should never get here", result)
def test_recv_close_pipe():

View File

@ -2,5 +2,6 @@
envlist = py27,py36
[testenv]
deps=-r{toxinidir}/requirements.txt
commands =
python setup.py test