Skip to content

Commit

Permalink
Merge pull request #48 from yeger00/pydantic-4
Browse files Browse the repository at this point in the history
Change the rest to pydantic
  • Loading branch information
yeger00 authored Mar 9, 2024
2 parents 83ef037 + b0cb007 commit 6749abe
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 460 deletions.
2 changes: 1 addition & 1 deletion pylspclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
from pylspclient.json_rpc_endpoint import JsonRpcEndpoint
from pylspclient.lsp_client import LspClient
from pylspclient.lsp_endpoint import LspEndpoint
from pylspclient import lsp_structs
from pylspclient import lsp_errors
10 changes: 5 additions & 5 deletions pylspclient/json_rpc_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import print_function
import json
from pylspclient import lsp_structs
from pylspclient.lsp_errors import ErrorCodes, ResponseError
import threading

JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}"
Expand Down Expand Up @@ -70,7 +70,7 @@ def recv_response(self):
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")
raise ResponseError(ErrorCodes.ParseError, "Bad header: missing newline")
#remove the "\r\n"
line = line[:-2]
if line == "":
Expand All @@ -79,15 +79,15 @@ def recv_response(self):
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")
raise ResponseError(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")
raise ResponseError(ErrorCodes.ParseError, "Bad header: unkown header")
if not message_size:
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.ParseError, "Bad header: missing size")
raise ResponseError(ErrorCodes.ParseError, "Bad header: missing size")

jsonrpc_res = self.stdout.read(message_size).decode("utf-8")
return json.loads(jsonrpc_res)
30 changes: 20 additions & 10 deletions pylspclient/lsp_client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import Optional

from pydantic import ValidationError
from pylspclient import lsp_structs
from pylspclient.lsp_endpoint import LspEndpoint
from pylspclient.lsp_pydantic_strcuts import TextDocumentItem, TextDocumentIdentifier, DocumentSymbol, SymbolInformation, LocationLink, Location
from pylspclient.lsp_pydantic_strcuts import Position
from pylspclient.lsp_pydantic_strcuts import Position, SignatureHelp, CompletionContext, CompletionItem, CompletionList

class LspClient(object):
def __init__(self, lsp_endpoint: LspEndpoint):
Expand Down Expand Up @@ -89,10 +88,10 @@ def didOpen(self, textDocument: TextDocumentItem):
:param TextDocumentItem textDocument: The document that was opened.
"""
return self.lsp_endpoint.send_notification("textDocument/didOpen", textDocument=textDocument)
self.lsp_endpoint.send_notification("textDocument/didOpen", textDocument=textDocument)


def didChange(self, textDocument, contentChanges):
def didChange(self, textDocument: TextDocumentItem, contentChanges):
"""
The document change notification is sent from the client to the server to signal changes to a text document.
In 2.0 the shape of the params has changed to include proper version numbers and language ids.
Expand Down Expand Up @@ -134,31 +133,42 @@ def typeDefinition(
return [Location.parse_obj(result) for result in result_dict]


def signatureHelp(self, textDocument, position):
def signatureHelp(
self,
textDocument: TextDocumentIdentifier,
position: Position,
workDoneToken: Optional[str] = None,
partialResultToken: Optional[str] = None
) -> SignatureHelp:
"""
The signature help request is sent from the client to the server to request signature information at a given cursor position.
:param TextDocumentItem textDocument: The text document.
:param Position position: The position inside the text document.
"""
result_dict = self.lsp_endpoint.call_method("textDocument/signatureHelp", textDocument=textDocument, position=position)
return lsp_structs.SignatureHelp(**result_dict)
return SignatureHelp.parse_obj(result_dict)


def completion(self, textDocument, position, context):
def completion(
self,
textDocument: TextDocumentIdentifier,
position: Position,
context: CompletionContext
) -> CompletionItem | CompletionList:
"""
The signature help request is sent from the client to the server to request signature information at a given cursor position.
:param TextDocumentItem textDocument: The text document.
:param Position position: The position inside the text document.
:param CompletionContext context: The completion context. This is only available if the client specifies
to send this using `ClientCapabilities.textDocument.completion.contextSupport === true`
CompletionContext to send this using `ClientCapabilities.textDocument.completion.contextSupport === true`
"""
result_dict = self.lsp_endpoint.call_method("textDocument/completion", textDocument=textDocument, position=position, context=context)
if "isIncomplete" in result_dict:
return lsp_structs.CompletionList(**result_dict)
return CompletionList.parse_obj(result_dict)

return [lsp_structs.CompletionItem(**result) for result in result_dict]
return [CompletionItem.parse_obj(result) for result in result_dict]


def declaration(
Expand Down
8 changes: 4 additions & 4 deletions pylspclient/lsp_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import print_function
import threading
from pylspclient import lsp_structs
from pylspclient.lsp_errors import ErrorCodes, ResponseError


class LspEndpoint(threading.Thread):
Expand Down Expand Up @@ -45,7 +45,7 @@ def run(self):
if rpc_id:
# a call for method
if method not in self.method_callbacks:
raise lsp_structs.ResponseError(lsp_structs.ErrorCodes.MethodNotFound, "Method not found: {method}".format(method=method))
raise ResponseError(ErrorCodes.MethodNotFound, "Method not found: {method}".format(method=method))
result = self.method_callbacks[method](params)
self.send_response(rpc_id, result, None)
else:
Expand All @@ -57,7 +57,7 @@ def run(self):
self.notify_callbacks[method](params)
else:
self.handle_result(rpc_id, result, error)
except lsp_structs.ResponseError as e:
except ResponseError as e:
self.send_response(rpc_id, None, e)


Expand Down Expand Up @@ -101,7 +101,7 @@ def call_method(self, method_name, **kwargs):
self.event_dict.pop(current_id)
result, error = self.response_dict.pop(current_id)
if error:
raise lsp_structs.ResponseError(error.get("code"), error.get("message"), error.get("data"))
raise ResponseError(error.get("code"), error.get("message"), error.get("data"))
return result


Expand Down
27 changes: 27 additions & 0 deletions pylspclient/lsp_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Any
from enum import IntEnum


class ErrorCodes(IntEnum):
# Defined by JSON RPC
ParseError = -32700
InvalidRequest = -32600
MethodNotFound = -32601
InvalidParams = -32602
InternalError = -32603
serverErrorStart = -32099
serverErrorEnd = -32000
ServerNotInitialized = -32002
UnknownErrorCode = -32001

# Defined by the protocol.
RequestCancelled = -32800
ContentModified = -32801


class ResponseError(Exception):
def __init__(self, code: ErrorCodes, message: str, data: Any = None):
self.code = code
self.message = message
if data:
self.data = data
71 changes: 69 additions & 2 deletions pylspclient/lsp_pydantic_strcuts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, List
from typing import Optional, List, Union
from enum import Enum, IntEnum
from pydantic import BaseModel, HttpUrl

Expand Down Expand Up @@ -119,7 +119,7 @@ class SymbolKind(IntEnum):
Operator = 25
TypeParameter = 26

class SymbolTag(Enum):
class SymbolTag(IntEnum):
"""Represents additional information about the symbol."""
# Define the symbol tags as per your specification, for example:
Deprecated = 1
Expand Down Expand Up @@ -173,3 +173,70 @@ class LocationLink(BaseModel):
targetUri: HttpUrl
targetRange: Range
targetSelectionRange: Range

class SignatureInformation(BaseModel):
label: str
documentation: Optional[Union[str, dict]] = None
parameters: Optional[List[dict]] = None

class SignatureHelp(BaseModel):
signatures: List[SignatureInformation]
activeSignature: Optional[int] = None
activeParameter: Optional[int] = None

class CompletionTriggerKind(IntEnum):
"""
Specifies how the completion was triggered.
"""
Invoked = 1 # Completion was triggered by typing an identifier, manual invocation, etc.
TriggerCharacter = 2 # Completion was triggered by a trigger character.
TriggerForIncompleteCompletions = 3 # Completion was re-triggered as the current completion list is incomplete.

class CompletionContext(BaseModel):
"""
Contains additional information about the context in which a completion request is triggered.
"""
triggerKind: CompletionTriggerKind
triggerCharacter: Optional[str] = None # Character that triggered the completion request.

class CompletionItemKind(IntEnum):
"""
Defines the kind of completion item.
"""
Text = 1
Method = 2
Function = 3
Constructor = 4
Field = 5
Variable = 6
Class = 7
Interface = 8
Module = 9
Property = 10
Unit = 11
Value = 12
Enum = 13
Keyword = 14
Snippet = 15
Color = 16
File = 17
Reference = 18
Folder = 19
EnumMember = 20
Constant = 21
Struct = 22
Event = 23
Operator = 24
TypeParameter = 25

class CompletionItem(BaseModel):
label: str
kind: Optional[CompletionItemKind] = None
detail: Optional[str] = None
documentation: Optional[Union[str, dict]] = None
insertText: Optional[str] = None
insertTextFormat: Optional[int] = None # 1: PlainText, 2: Snippet

class CompletionList(BaseModel):
isIncomplete: bool
items: List[CompletionItem]
Loading

0 comments on commit 6749abe

Please sign in to comment.