commit
3ee6fd666c
@ -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
|
||||
|
@ -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))
|
||||
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.
|
||||
#lsp_client.documentSymbol(pylspclient.lsp_structs.TextDocumentIdentifier(uri))
|
||||
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()
|
||||
|
@ -5,7 +5,10 @@ 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
|
||||
|
||||
|
||||
@ -13,7 +16,7 @@ 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:
|
||||
message_size = None
|
||||
while True:
|
||||
#read header
|
||||
line = self.stdout.readline()
|
||||
if not line:
|
||||
# server quit
|
||||
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")
|
||||
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)
|
||||
|
@ -46,14 +46,15 @@ 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)
|
||||
|
||||
# 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)
|
||||
|
@ -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
|
||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
enum ; python_version < '3.4'
|
1
setup.py
1
setup.py
@ -20,6 +20,7 @@ class PyTest(TestCommand):
|
||||
errno = pytest.main(self.pytest_args)
|
||||
sys.exit(errno)
|
||||
|
||||
|
||||
setup(
|
||||
name="pylspclient",
|
||||
version="0.0.2",
|
||||
|
@ -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():
|
||||
|
Loading…
Reference in New Issue
Block a user