diff --git a/pylspclient/json_rpc_endpoint.py b/pylspclient/json_rpc_endpoint.py index 73ba20b..749fc19 100644 --- a/pylspclient/json_rpc_endpoint.py +++ b/pylspclient/json_rpc_endpoint.py @@ -2,6 +2,7 @@ import json from pylspclient.lsp_errors import ErrorCodes, ResponseError import threading +from typing import BinaryIO JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}" LEN_HEADER = "Content-Length: " @@ -15,7 +16,7 @@ class MyEncoder(json.JSONEncoder): """ Encodes an object in JSON """ - def default(self, o): # pylint: disable=E0202 + def default(self, o: object): # pylint: disable=E0202 return o.__dict__ @@ -24,14 +25,14 @@ class JsonRpcEndpoint(object): Thread safe JSON RPC endpoint implementation. Responsible to recieve and send JSON RPC messages, as described in the protocol. More information can be found: https://www.jsonrpc.org/ ''' - def __init__(self, stdin, stdout): + def __init__(self, stdin: BinaryIO, stdout: BinaryIO): self.stdin = stdin self.stdout = stdout self.read_lock = threading.Lock() self.write_lock = threading.Lock() @staticmethod - def __add_header(json_string): + def __add_header(json_string: str) -> str: ''' Adds a header for the given json string @@ -41,7 +42,7 @@ def __add_header(json_string): return JSON_RPC_REQ_FORMAT.format(json_string_len=len(json_string), json_string=json_string) - def send_request(self, message): + def send_request(self, message: any) -> None: ''' Sends the given message. @@ -54,7 +55,7 @@ def send_request(self, message): self.stdin.flush() - def recv_response(self): + def recv_response(self) -> any: ''' Recives a message. diff --git a/pylspclient/lsp_client.py b/pylspclient/lsp_client.py index b690c96..d669eba 100644 --- a/pylspclient/lsp_client.py +++ b/pylspclient/lsp_client.py @@ -15,7 +15,16 @@ def __init__(self, lsp_endpoint: LspEndpoint): self.lsp_endpoint = lsp_endpoint - def initialize(self, processId, rootPath, rootUri, initializationOptions, capabilities, trace, workspaceFolders): + def initialize( + self, + processId: Optional[int] = None, + rootPath: Optional[str] = None, + rootUri: Optional[str] = None, + initializationOptions: any = None, + capabilities: dict = None, + trace: Optional[str] = None, + workspaceFolders: Optional[list] = None + ) -> any: """ The initialize request is sent as the first request from the client to the server. If the server receives a request or notification before the initialize request it should act as follows: @@ -33,7 +42,7 @@ def initialize(self, processId, rootPath, rootUri, initializationOptions, capabi :param int processId: The process Id of the parent process that started the server. Is null if the process has not been started by another process. If the parent process is not alive then the server should exit (see exit notification) its process. :param str rootPath: The rootPath of the workspace. Is null if no folder is open. Deprecated in favour of rootUri. - :param DocumentUri rootUri: The rootUri of the workspace. Is null if no folder is open. If both `rootPath` and `rootUri` are set + :param str rootUri: The rootUri of the workspace. Is null if no folder is open. If both `rootPath` and `rootUri` are set `rootUri` wins. :param any initializationOptions: User provided initialization options. :param ClientCapabilities capabilities: The capabilities provided by the client (editor or tool). @@ -41,6 +50,8 @@ def initialize(self, processId, rootPath, rootUri, initializationOptions, capabi :param list workspaceFolders: The workspace folders configured in the client when the server starts. This property is only available if the client supports workspace folders. It can be `null` if the client supports workspace folders but none are configured. """ + if not capabilities: + raise ValueError("capabilities must be provided") self.lsp_endpoint.start() return self.lsp_endpoint.call_method("initialize", processId=processId, rootPath=rootPath, rootUri=rootUri, initializationOptions=initializationOptions, capabilities=capabilities, trace=trace, workspaceFolders=workspaceFolders) diff --git a/pylspclient/lsp_endpoint.py b/pylspclient/lsp_endpoint.py index 184ceaa..8a5b73f 100644 --- a/pylspclient/lsp_endpoint.py +++ b/pylspclient/lsp_endpoint.py @@ -1,10 +1,18 @@ from __future__ import print_function import threading from pylspclient.lsp_errors import ErrorCodes, ResponseError +from pylspclient import JsonRpcEndpoint +from typing import Dict, Callable, Any, Union class LspEndpoint(threading.Thread): - def __init__(self, json_rpc_endpoint, method_callbacks={}, notify_callbacks={}, timeout=2): + def __init__( + self, + json_rpc_endpoint: JsonRpcEndpoint, + method_callbacks: Dict[str, Callable[[Any], Any]]={}, + notify_callbacks: Dict[str, Callable[[Any], Any]]={}, + timeout: int=2 + ): threading.Thread.__init__(self) self.json_rpc_endpoint = json_rpc_endpoint self.notify_callbacks = notify_callbacks @@ -24,7 +32,7 @@ def handle_result(self, rpc_id, result, error): cond.release() - def stop(self): + def stop(self) -> None: self.shutdown_flag = True @@ -35,11 +43,11 @@ def run(self): if jsonrpc_message is None: print("server quit") break - method = jsonrpc_message.get("method") - result = jsonrpc_message.get("result") - error = jsonrpc_message.get("error") - rpc_id = jsonrpc_message.get("id") - params = jsonrpc_message.get("params") + method: str = jsonrpc_message.get("method") + result: any = jsonrpc_message.get("result") + error: dict = jsonrpc_message.get("error") + rpc_id: Union[str, int] = jsonrpc_message.get("id") + params: Union[dict, list] = jsonrpc_message.get("params") if method: if rpc_id: @@ -72,7 +80,7 @@ def send_response(self, id, result, error): self.json_rpc_endpoint.send_request(message_dict) - def send_message(self, method_name, params, id = None): + def send_message(self, method_name: str, params: dict, id: int=None): message_dict = {} message_dict["jsonrpc"] = "2.0" if id is not None: