From 69ba3d1c8856b6a4cce25571f680164816bb7672 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 15:16:36 +0100 Subject: [PATCH 01/20] Remove pygls' built in type definitions --- pygls/lsp/methods.py | 120 ----- pygls/lsp/types/__init__.py | 10 - pygls/lsp/types/basic_structures.py | 479 ------------------ pygls/lsp/types/client.py | 48 -- pygls/lsp/types/diagnostics.py | 47 -- pygls/lsp/types/file_operations.py | 79 --- pygls/lsp/types/general_messages.py | 280 ---------- pygls/lsp/types/language_features/__init__.py | 27 - .../types/language_features/call_hierarchy.py | 81 --- .../types/language_features/code_action.py | 100 ---- .../lsp/types/language_features/code_lens.py | 53 -- .../language_features/color_presentation.py | 44 -- .../lsp/types/language_features/completion.py | 165 ------ .../types/language_features/declaration.py | 51 -- .../lsp/types/language_features/definition.py | 43 -- .../types/language_features/document_color.py | 62 --- .../language_features/document_highlight.py | 60 --- .../types/language_features/document_link.py | 51 -- .../language_features/document_symbol.py | 116 ----- .../types/language_features/folding_range.py | 67 --- .../lsp/types/language_features/formatting.py | 51 -- pygls/lsp/types/language_features/hover.py | 57 --- .../types/language_features/implementation.py | 52 -- .../language_features/linked_editing_range.py | 53 -- pygls/lsp/types/language_features/monikers.py | 70 --- .../language_features/on_type_formatting.py | 45 -- .../types/language_features/prepare_rename.py | 37 -- .../language_features/range_formatting.py | 45 -- .../lsp/types/language_features/references.py | 47 -- pygls/lsp/types/language_features/rename.py | 50 -- .../language_features/selection_range.py | 60 --- .../language_features/semantic_tokens.py | 151 ------ .../types/language_features/signature_help.py | 88 ---- .../language_features/type_definition.py | 51 -- pygls/lsp/types/text_synchronization.py | 46 -- pygls/lsp/types/window.py | 91 ---- pygls/lsp/types/workspace.py | 202 -------- 37 files changed, 3179 deletions(-) delete mode 100644 pygls/lsp/methods.py delete mode 100644 pygls/lsp/types/__init__.py delete mode 100644 pygls/lsp/types/basic_structures.py delete mode 100644 pygls/lsp/types/client.py delete mode 100644 pygls/lsp/types/diagnostics.py delete mode 100644 pygls/lsp/types/file_operations.py delete mode 100644 pygls/lsp/types/general_messages.py delete mode 100644 pygls/lsp/types/language_features/__init__.py delete mode 100644 pygls/lsp/types/language_features/call_hierarchy.py delete mode 100644 pygls/lsp/types/language_features/code_action.py delete mode 100644 pygls/lsp/types/language_features/code_lens.py delete mode 100644 pygls/lsp/types/language_features/color_presentation.py delete mode 100644 pygls/lsp/types/language_features/completion.py delete mode 100644 pygls/lsp/types/language_features/declaration.py delete mode 100644 pygls/lsp/types/language_features/definition.py delete mode 100644 pygls/lsp/types/language_features/document_color.py delete mode 100644 pygls/lsp/types/language_features/document_highlight.py delete mode 100644 pygls/lsp/types/language_features/document_link.py delete mode 100644 pygls/lsp/types/language_features/document_symbol.py delete mode 100644 pygls/lsp/types/language_features/folding_range.py delete mode 100644 pygls/lsp/types/language_features/formatting.py delete mode 100644 pygls/lsp/types/language_features/hover.py delete mode 100644 pygls/lsp/types/language_features/implementation.py delete mode 100644 pygls/lsp/types/language_features/linked_editing_range.py delete mode 100644 pygls/lsp/types/language_features/monikers.py delete mode 100644 pygls/lsp/types/language_features/on_type_formatting.py delete mode 100644 pygls/lsp/types/language_features/prepare_rename.py delete mode 100644 pygls/lsp/types/language_features/range_formatting.py delete mode 100644 pygls/lsp/types/language_features/references.py delete mode 100644 pygls/lsp/types/language_features/rename.py delete mode 100644 pygls/lsp/types/language_features/selection_range.py delete mode 100644 pygls/lsp/types/language_features/semantic_tokens.py delete mode 100644 pygls/lsp/types/language_features/signature_help.py delete mode 100644 pygls/lsp/types/language_features/type_definition.py delete mode 100644 pygls/lsp/types/text_synchronization.py delete mode 100644 pygls/lsp/types/window.py delete mode 100644 pygls/lsp/types/workspace.py diff --git a/pygls/lsp/methods.py b/pygls/lsp/methods.py deleted file mode 100644 index fdfd41b2..00000000 --- a/pygls/lsp/methods.py +++ /dev/null @@ -1,120 +0,0 @@ -############################################################################ -# Copyright(c) Open Law Library. All rights reserved. # -# See ThirdPartyNotices.txt in the project root for additional notices. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains all methods supported by Language Server Protocol - -LSP Specification: - https://microsoft.github.io/language-server-protocol/specification -""" - -# Special methods -CANCEL_REQUEST = '$/cancelRequest' -PROGRESS_NOTIFICATION = '$/progress' -LOG_TRACE_NOTIFICATION = '$/logTrace' -SET_TRACE_NOTIFICATION = '$/setTrace' - -# Client -CLIENT_REGISTER_CAPABILITY = 'client/registerCapability' -CLIENT_UNREGISTER_CAPABILITY = 'client/unregisterCapability' - -# Diagnostics -TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS = 'textDocument/publishDiagnostics' - -# General -EXIT = 'exit' -INITIALIZE = 'initialize' -INITIALIZED = 'initialized' -SHUTDOWN = 'shutdown' - -# Language Features -CODE_ACTION = 'textDocument/codeAction' -CODE_ACTION_RESOLVE = 'codeAction/resolve' -CODE_LENS = 'textDocument/codeLens' -CODE_LENS_RESOLVE = 'codeLens/resolve' -COLOR_PRESENTATION = 'textDocument/colorPresentation' -COMPLETION = 'textDocument/completion' -COMPLETION_ITEM_RESOLVE = 'completionItem/resolve' -DECLARATION = 'textDocument/declaration' -DEFINITION = 'textDocument/definition' -DOCUMENT_COLOR = 'textDocument/documentColor' -DOCUMENT_HIGHLIGHT = 'textDocument/documentHighlight' -DOCUMENT_LINK = 'textDocument/documentLink' -DOCUMENT_LINK_RESOLVE = 'documentLink/resolve' -DOCUMENT_SYMBOL = 'textDocument/documentSymbol' -FOLDING_RANGE = 'textDocument/foldingRange' -FORMATTING = 'textDocument/formatting' -HOVER = 'textDocument/hover' -IMPLEMENTATION = 'textDocument/implementation' -ON_TYPE_FORMATTING = 'textDocument/onTypeFormatting' -PREPARE_RENAME = 'textDocument/prepareRename' -RANGE_FORMATTING = 'textDocument/rangeFormatting' -REFERENCES = 'textDocument/references' -RENAME = 'textDocument/rename' -SELECTION_RANGE = 'textDocument/selectionRange' -SIGNATURE_HELP = 'textDocument/signatureHelp' -TYPE_DEFINITION = 'textDocument/typeDefinition' - -# Telemetry -TELEMETRY_EVENT = 'telemetry/event' - -# Text Synchronization -TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE = 'textDocument/prepareCallHierarchy' -TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS = 'callHierarchy/incomingCalls' -TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS = 'callHierarchy/outgoingCalls' -TEXT_DOCUMENT_DID_CHANGE = 'textDocument/didChange' -TEXT_DOCUMENT_DID_CLOSE = 'textDocument/didClose' -TEXT_DOCUMENT_DID_OPEN = 'textDocument/didOpen' -TEXT_DOCUMENT_DID_SAVE = 'textDocument/didSave' -TEXT_DOCUMENT_LINKED_EDITING_RANGE = 'textDocument/linkedEditingRange' -TEXT_DOCUMENT_MONIKER = 'textDocument/moniker' -# NOTE: From official specs regarding semantic tokens: -# Since the registration option handles range, full and delta requests the method used to -# register for semantic tokens requests is textDocument/semanticTokens -# and not one of the specific methods described below. -TEXT_DOCUMENT_SEMANTIC_TOKENS = 'textDocument/semanticTokens' -TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL = 'textDocument/semanticTokens/full' -TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA = 'textDocument/semanticTokens/full/delta' -TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE = 'textDocument/semanticTokens/range' -TEXT_DOCUMENT_WILL_SAVE = 'textDocument/willSave' -TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL = 'textDocument/willSaveWaitUntil' - -# Window -WINDOW_LOG_MESSAGE = 'window/logMessage' -WINDOW_SHOW_DOCUMENT = 'window/showDocument' -WINDOW_SHOW_MESSAGE = 'window/showMessage' -WINDOW_SHOW_MESSAGE_REQUEST = 'window/showMessageRequest' -WINDOW_WORK_DONE_PROGRESS_CANCEL = 'window/workDoneProgress/cancel' -WINDOW_WORK_DONE_PROGRESS_CREATE = 'window/workDoneProgress/create' - -# Workspace -WORKSPACE_APPLY_EDIT = 'workspace/applyEdit' -WORKSPACE_CODE_LENS_REFRESH = 'workspace/codeLens/refresh' -WORKSPACE_CONFIGURATION = 'workspace/configuration' -WORKSPACE_DID_CHANGE_CONFIGURATION = 'workspace/didChangeConfiguration' -WORKSPACE_DID_CHANGE_WATCHED_FILES = 'workspace/didChangeWatchedFiles' -WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS = 'workspace/didChangeWorkspaceFolders' -WORKSPACE_EXECUTE_COMMAND = 'workspace/executeCommand' -WORKSPACE_FOLDERS = 'workspace/workspaceFolders' -WORKSPACE_SEMANTIC_TOKENS_REFRESH = 'workspace/semanticTokens/refresh' -WORKSPACE_SYMBOL = 'workspace/symbol' - -# File Operations -WORKSPACE_WILL_CREATE_FILES = 'workspace/willCreateFiles' -WORKSPACE_DID_CREATE_FILES = 'workspace/didCreateFiles' -WORKSPACE_WILL_RENAME_FILES = 'workspace/willRenameFiles' -WORKSPACE_DID_RENAME_FILES = 'workspace/didRenameFiles' -WORKSPACE_WILL_DELETE_FILES = 'workspace/willDeleteFiles' -WORKSPACE_DID_DELETE_FILES = 'workspace/didDeleteFiles' diff --git a/pygls/lsp/types/__init__.py b/pygls/lsp/types/__init__.py deleted file mode 100644 index 0da5ba46..00000000 --- a/pygls/lsp/types/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# flake8: noqa -from pygls.lsp.types.basic_structures import * -from pygls.lsp.types.client import * -from pygls.lsp.types.diagnostics import * -from pygls.lsp.types.file_operations import * -from pygls.lsp.types.general_messages import * -from pygls.lsp.types.language_features import * -from pygls.lsp.types.text_synchronization import * -from pygls.lsp.types.window import * -from pygls.lsp.types.workspace import * diff --git a/pygls/lsp/types/basic_structures.py b/pygls/lsp/types/basic_structures.py deleted file mode 100644 index 2b28b597..00000000 --- a/pygls/lsp/types/basic_structures.py +++ /dev/null @@ -1,479 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Basic Structures -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Any, Callable, Dict, List, NewType, Optional, TypeVar, Union - -from pydantic import BaseModel, root_validator -from typeguard import check_type - -ChangeAnnotationIdentifier = NewType('ChangeAnnotationIdentifier', str) -NumType = Union[int, float] -ProgressToken = Union[int, str] -URI = NewType('URI', str) -T = TypeVar('T') - -ConfigCallbackType = Callable[[List[Any]], None] - - -def snake_to_camel(string: str) -> str: - return ''.join( - word.capitalize() if idx > 0 else word - for idx, word in enumerate(string.split('_')) - ) - - -class Model(BaseModel): - class Config: - alias_generator = snake_to_camel - allow_population_by_field_name = True - fields = { - 'from_': 'from' - } - - def __init__(self, **data: Any) -> None: - super().__init__(**data) - - # Serialize (.json()) fields that has default value which is not None - for name, field in self.__fields__.items(): - if getattr(field, 'default', None) is not None: - self.__fields_set__.add(name) - - -class JsonRpcMessage(Model): - """A base json rpc message defined by LSP.""" - jsonrpc: str - - -class JsonRPCNotification(JsonRpcMessage): - """A class that represents json rpc notification message.""" - method: str - params: Any - - -class JsonRPCRequestMessage(JsonRpcMessage): - """A class that represents json rpc request message.""" - id: Any - method: str - params: Any - - @root_validator - def check_result_or_error(cls, values): - # Workaround until pydantic supports StrictUnion - # https://github.com/samuelcolvin/pydantic/pull/2092 - id_val = values.get('id') - check_type('', id_val, Union[int, str]) - - return values - - -class JsonRPCResponseMessage(JsonRpcMessage): - """A class that represents json rpc response message.""" - id: Any - result: Any - error: Any - - @root_validator - def check_result_or_error(cls, values): - # Workaround until pydantic supports StrictUnion - # https://github.com/samuelcolvin/pydantic/pull/2092 - id_val = values.get('id') - check_type('', id_val, Union[int, str]) - - result_val, error_val = values.get('result'), values.get('error') - - if result_val is not None and error_val is not None: - raise ValueError('Fields "result" and "error" are both set!') - - return values - - -class Position(Model): - line: int - character: int - - def __eq__(self, other): - return ( - isinstance(other, Position) - and self.line == other.line - and self.character == other.character) - - def __ge__(self, other): - line_gt = self.line > other.line - - if line_gt: - return line_gt - - if self.line == other.line: - return self.character >= other.character - - return False - - def __gt__(self, other): - line_gt = self.line > other.line - - if line_gt: - return line_gt - - if self.line == other.line: - return self.character > other.character - - return False - - def __le__(self, other): - line_lt = self.line < other.line - - if line_lt: - return line_lt - - if self.line == other.line: - return self.character <= other.character - - return False - - def __lt__(self, other): - line_lt = self.line < other.line - - if line_lt: - return line_lt - - if self.line == other.line: - return self.character < other.character - - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return hash((self.line, self.character)) - - def __iter__(self): - return iter((self.line, self.character)) - - def __repr__(self): - return f'{self.line}:{self.character}' - - -class Range(Model): - start: Position - end: Position - - def __eq__(self, other): - return ( - isinstance(other, Range) - and self.start == other.start - and self.end == other.end) - - def __hash__(self): - return hash((self.start, self.end)) - - def __iter__(self): - return iter((self.start, self.end)) - - def __repr__(self): - return f'{self.start!r}-{self.end!r}' - - -class Location(Model): - uri: str - range: Range - - def __eq__(self, other): - return ( - isinstance(other, Location) - and self.uri == other.uri - and self.range == other.range) - - def __repr__(self): - return f"{self.uri}:{self.range!r}" - - -class Trace(str, enum.Enum): - Off = 'off' - Messages = 'messages' - Verbose = 'verbose' - - -class CancelParams(Model): - id: Union[int, str] - - -class ProgressParams(Model): - token: ProgressToken - value: Any - - -class LogTraceParams(Model): - message: str - verbose: Optional[str] - - -class SetTraceParams(Model): - value: Trace - - -class RegularExpressionsClientCapabilities(Model): - engine: str - version: Optional[str] - - -class ResolveSupportClientCapabilities(Model): - properties: List[str] - - -class LocationLink(Model): - target_uri: str - target_range: Range - target_selection_range: Range - origin_selection_range: Optional[Range] - - -class DiagnosticSeverity(enum.IntEnum): - Error = 1 - Warning = 2 - Information = 3 - Hint = 4 - - -class DiagnosticTag(enum.IntEnum): - Unnecessary = 1 - Deprecated = 2 - - -class DiagnosticRelatedInformation(Model): - location: Location - message: str - - -class CodeDescription(Model): - href: URI - - -class Diagnostic(Model): - range: Range - message: str - severity: Optional[DiagnosticSeverity] - code: Optional[Union[int, str]] - code_description: Optional[CodeDescription] - source: Optional[str] - related_information: Optional[List[DiagnosticRelatedInformation]] - tags: Optional[List[DiagnosticTag]] - data: Optional[Any] - - -class Command(Model): - title: str - command: str - arguments: Optional[List[Any]] - - -class TextEdit(Model): - range: Range - new_text: str - - -class AnnotatedTextEdit(TextEdit): - annotation_id: ChangeAnnotationIdentifier - - -class ChangeAnnotation(Model): - label: str - needs_confirmation: Optional[bool] - description: Optional[str] - - -class ResourceOperationKind(str, enum.Enum): - Create = 'create' - Rename = 'rename' - Delete = 'delete' - - -class CreateFileOptions(Model): - overwrite: Optional[bool] - ignore_if_exists: Optional[bool] - - -class CreateFile(Model): - kind: ResourceOperationKind = ResourceOperationKind.Create - uri: str - options: Optional[CreateFileOptions] - annotation_id: Optional[ChangeAnnotationIdentifier] - - -class RenameFileOptions(Model): - overwrite: Optional[bool] - ignore_if_exists: Optional[bool] - - -class RenameFile(Model): - kind: ResourceOperationKind = ResourceOperationKind.Rename - old_uri: str - new_uri: str - options: Optional[RenameFileOptions] - annotation_id: Optional[ChangeAnnotationIdentifier] - - -class DeleteFileOptions(Model): - recursive: Optional[bool] - ignore_if_exists: Optional[bool] - - -class DeleteFile(Model): - kind: ResourceOperationKind = ResourceOperationKind.Delete - uri: str - options: Optional[DeleteFileOptions] - annotation_id: Optional[ChangeAnnotationIdentifier] - - -class FailureHandlingKind(str, enum.Enum): - Abort = 'abort' - Transactional = 'transactional' - TextOnlyTransactional = 'textOnlyTransactional' - Undo = 'undo' - - -class ChangeAnnotationSupport(Model): - groups_on_label: Optional[bool] - - -class WorkspaceEditClientCapabilities(Model): - document_changes: Optional[bool] - resource_operations: Optional[List[ResourceOperationKind]] - failure_handling: Optional[FailureHandlingKind] - normalizes_line_endings: Optional[bool] - change_annotation_support: Optional[ChangeAnnotationSupport] - - -class TextDocumentIdentifier(Model): - uri: str - - -class TextDocumentItem(Model): - uri: str - language_id: str - version: NumType - text: str - - -class VersionedTextDocumentIdentifier(TextDocumentIdentifier): - version: NumType - - -class OptionalVersionedTextDocumentIdentifier(TextDocumentIdentifier): - version: Optional[NumType] - - -class TextDocumentEdit(Model): - text_document: OptionalVersionedTextDocumentIdentifier - edits: List[Union[TextEdit, AnnotatedTextEdit]] - - -class TextDocumentPositionParams(Model): - text_document: TextDocumentIdentifier - position: Position - - -class DocumentFilter(Model): - language: Optional[str] - scheme: Optional[str] - pattern: Optional[str] - - -DocumentSelector = List[DocumentFilter] - - -class StaticRegistrationOptions(Model): - id: Optional[str] - - -class TextDocumentRegistrationOptions(Model): - document_selector: Optional[DocumentSelector] - - -class MarkupKind(str, enum.Enum): - PlainText = 'plaintext' - Markdown = 'markdown' - - -class MarkupContent(Model): - kind: MarkupKind - value: str - - -class WorkspaceEdit(Model): - changes: Optional[Dict[str, List[TextEdit]]] - document_changes: Optional[Any] - change_annotations: Optional[Dict[ChangeAnnotationIdentifier, ChangeAnnotation]] - - @root_validator - def check_result_or_error(cls, values): - # Workaround until pydantic supports StrictUnion - # https://github.com/samuelcolvin/pydantic/pull/2092 - - document_changes_val = values.get('document_changes') - check_type( - '', - document_changes_val, - Optional[Union[ - List[TextDocumentEdit], - List[Union[TextDocumentEdit, CreateFile, RenameFile, DeleteFile]], - ]] - ) - - return values - - -class WorkDoneProgressBegin(Model): - kind: str = 'begin' - title: str - cancellable: Optional[bool] - message: Optional[str] - percentage: Optional[NumType] - - -class WorkDoneProgressReport(Model): - kind: str = 'report' - cancellable: Optional[bool] - message: Optional[str] - percentage: Optional[NumType] - - -class WorkDoneProgressEnd(Model): - kind: str = 'end' - message: Optional[str] - - -class WorkDoneProgressParams(Model): - work_done_token: Optional[ProgressToken] - - -class WorkDoneProgressOptions(Model): - work_done_progress: Optional[ProgressToken] - - -class PartialResultParams(Model): - partial_result_token: Optional[ProgressToken] diff --git a/pygls/lsp/types/client.py b/pygls/lsp/types/client.py deleted file mode 100644 index 14c979cb..00000000 --- a/pygls/lsp/types/client.py +++ /dev/null @@ -1,48 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Client -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Any, List, Optional - -from pygls.lsp.types.basic_structures import Model - - -class Registration(Model): - id: str - method: str - register_options: Optional[Any] - - -class RegistrationParams(Model): - registrations: List[Registration] - - -class Unregistration(Model): - id: str - method: str - - -class UnregistrationParams(Model): - unregisterations: List[Unregistration] diff --git a/pygls/lsp/types/diagnostics.py b/pygls/lsp/types/diagnostics.py deleted file mode 100644 index 1b4e7c1a..00000000 --- a/pygls/lsp/types/diagnostics.py +++ /dev/null @@ -1,47 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Diagnostics -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional - -from pygls.lsp.types.basic_structures import Diagnostic, DiagnosticTag, Model, NumType - - -class PublishDiagnosticsTagSupportClientCapabilities(Model): - value_set: Optional[List[DiagnosticTag]] - - -class PublishDiagnosticsClientCapabilities(Model): - related_information: Optional[bool] - tag_support: Optional[PublishDiagnosticsTagSupportClientCapabilities] - version_support: Optional[bool] - code_description_support: Optional[bool] - data_support: Optional[bool] - - -class PublishDiagnosticsParams(Model): - uri: str - diagnostics: List[Diagnostic] - version: Optional[NumType] diff --git a/pygls/lsp/types/file_operations.py b/pygls/lsp/types/file_operations.py deleted file mode 100644 index 477d77a7..00000000 --- a/pygls/lsp/types/file_operations.py +++ /dev/null @@ -1,79 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- File Operations -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import List, Optional - -from pygls.lsp.types.basic_structures import Model - - -class FileOperationPatternKind(str, enum.Enum): - File = 'file' - Folder = 'folder' - - -class FileOperationPatternOptions(Model): - ignore_case: Optional[bool] - - -class FileOperationPattern(Model): - glob: str - matches: Optional[FileOperationPatternKind] - options: Optional[FileOperationPatternOptions] - - -class FileOperationFilter(Model): - scheme: Optional[str] - pattern: FileOperationPattern - - -class FileOperationRegistrationOptions(Model): - filters: List[FileOperationFilter] - - -class FileCreate(Model): - uri: str - - -class CreateFilesParams(Model): - files: List[FileCreate] - - -class FileRename(Model): - oldUri: str - newUri: str - - -class RenameFilesParams(Model): - files: List[FileRename] - - -class FileDelete(Model): - uri: str - - -class DeleteFilesParams(Model): - files: List[FileDelete] diff --git a/pygls/lsp/types/general_messages.py b/pygls/lsp/types/general_messages.py deleted file mode 100644 index f26f74fb..00000000 --- a/pygls/lsp/types/general_messages.py +++ /dev/null @@ -1,280 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- General Messages -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from functools import reduce -from typing import Any, List, Optional, Union - -from pygls.lsp.types.basic_structures import (Model, NumType, RegularExpressionsClientCapabilities, - Trace, WorkDoneProgressParams, - WorkspaceEditClientCapabilities) -from pygls.lsp.types.diagnostics import PublishDiagnosticsClientCapabilities -from pygls.lsp.types.file_operations import FileOperationRegistrationOptions -from pygls.lsp.types.language_features import (CallHierarchyClientCapabilities, - CallHierarchyOptions, - CallHierarchyRegistrationOptions, - CodeActionClientCapabilities, CodeActionOptions, - CodeLensClientCapabilities, CodeLensOptions, - CodeLensWorkspaceClientCapabilities, - CompletionClientCapabilities, CompletionOptions, - DeclarationClientCapabilities, DeclarationOptions, - DeclarationRegistrationOptions, - DefinitionClientCapabilities, DefinitionOptions, - DocumentColorClientCapabilities, - DocumentColorOptions, - DocumentColorRegistrationOptions, - DocumentFormattingClientCapabilities, - DocumentFormattingOptions, - DocumentHighlightClientCapabilities, - DocumentHighlightOptions, - DocumentLinkClientCapabilities, DocumentLinkOptions, - DocumentOnTypeFormattingClientCapabilities, - DocumentOnTypeFormattingOptions, - DocumentRangeFormattingClientCapabilities, - DocumentRangeFormattingOptions, - DocumentSymbolClientCapabilities, - DocumentSymbolOptions, - FoldingRangeClientCapabilities, FoldingRangeOptions, - FoldingRangeRegistrationOptions, - HoverClientCapabilities, HoverOptions, - ImplementationClientCapabilities, - ImplementationOptions, - ImplementationRegistrationOptions, - LinkedEditingRangeClientCapabilities, - LinkedEditingRangeOptions, - LinkedEditingRangeRegistrationOptions, - MonikerClientCapabilities, MonikerOptions, - MonikerRegistrationOptions, - ReferenceClientCapabilities, ReferenceOptions, - RenameClientCapabilities, RenameOptions, - SelectionRangeClientCapabilities, - SelectionRangeOptions, - SelectionRangeRegistrationOptions, - SemanticTokensClientCapabilities, - SemanticTokensOptions, - SemanticTokensRegistrationOptions, - SemanticTokensWorkspaceClientCapabilities, - SignatureHelpClientCapabilities, - SignatureHelpOptions, - TypeDefinitionClientCapabilities, - TypeDefinitionOptions, - TypeDefinitionRegistrationOptions) -from pygls.lsp.types.text_synchronization import TextDocumentSyncKind -from pygls.lsp.types.window import (ShowDocumentClientCapabilities, - ShowMessageRequestClientCapabilities) -from pygls.lsp.types.workspace import (DidChangeConfigurationClientCapabilities, - DidChangeWatchedFilesClientCapabilities, - ExecuteCommandClientCapabilities, ExecuteCommandOptions, - SaveOptions, TextDocumentSyncClientCapabilities, - WorkspaceFolder, WorkspaceFoldersServerCapabilities, - WorkspaceSymbolClientCapabilities) - - -class ClientInfo(Model): - name: str - version: Optional[str] - - -class ServerInfo(Model): - name: str - version: Optional[str] - - -class TextDocumentClientCapabilities(Model): - synchronization: Optional[TextDocumentSyncClientCapabilities] - completion: Optional[CompletionClientCapabilities] - hover: Optional[HoverClientCapabilities] - signature_help: Optional[SignatureHelpClientCapabilities] - declaration: Optional[DeclarationClientCapabilities] - definition: Optional[DefinitionClientCapabilities] - type_definition: Optional[TypeDefinitionClientCapabilities] - implementation: Optional[ImplementationClientCapabilities] - references: Optional[ReferenceClientCapabilities] - document_highlight: Optional[DocumentHighlightClientCapabilities] - document_symbol: Optional[DocumentSymbolClientCapabilities] - code_action: Optional[CodeActionClientCapabilities] - code_lens: Optional[CodeLensClientCapabilities] - document_link: Optional[DocumentLinkClientCapabilities] - color_provider: Optional[DocumentColorClientCapabilities] - formatting: Optional[DocumentFormattingClientCapabilities] - range_formatting: Optional[DocumentRangeFormattingClientCapabilities] - on_type_formatting: Optional[DocumentOnTypeFormattingClientCapabilities] - rename: Optional[RenameClientCapabilities] - publish_diagnostics: Optional[PublishDiagnosticsClientCapabilities] - folding_range: Optional[FoldingRangeClientCapabilities] - selection_range: Optional[SelectionRangeClientCapabilities] - linked_editing_range: Optional[LinkedEditingRangeClientCapabilities] - call_hierarchy: Optional[CallHierarchyClientCapabilities] - semantic_tokens: Optional[SemanticTokensClientCapabilities] - moniker: Optional[MonikerClientCapabilities] - - -class FileOperationsClientCapabilities(Model): - dynamic_registration: Optional[bool] - did_create: Optional[bool] - will_create: Optional[bool] - did_rename: Optional[bool] - will_rename: Optional[bool] - did_delete: Optional[bool] - will_delete: Optional[bool] - - -class WorkspaceClientCapabilities(Model): - apply_edit: Optional[bool] - workspace_edit: Optional[WorkspaceEditClientCapabilities] - did_change_configuration: Optional[DidChangeConfigurationClientCapabilities] - did_change_watched_files: Optional[DidChangeWatchedFilesClientCapabilities] - symbol: Optional[WorkspaceSymbolClientCapabilities] - execute_command: Optional[ExecuteCommandClientCapabilities] - workspace_folders: Optional[bool] - configuration: Optional[bool] - semantic_tokens: Optional[SemanticTokensWorkspaceClientCapabilities] - code_lens: Optional[CodeLensWorkspaceClientCapabilities] - file_operations: Optional[FileOperationsClientCapabilities] - - -class WindowClientCapabilities(Model): - work_done_progress: Optional[bool] - show_message: Optional[ShowMessageRequestClientCapabilities] - show_document: Optional[ShowDocumentClientCapabilities] - - -class MarkdownClientCapabilities(Model): - parser: str - version: Optional[str] - - -class GeneralClientCapabilities(Model): - regular_expressions: Optional[RegularExpressionsClientCapabilities] - markdown: Optional[MarkdownClientCapabilities] - - -class TextDocumentSyncOptionsServerCapabilities(Model): - open_close: Optional[bool] - change: Optional[TextDocumentSyncKind] - will_save: Optional[bool] - will_save_wait_until: Optional[bool] - save: Optional[Union[bool, SaveOptions]] - - -class WorkspaceFileOperationsServerCapabilities(Model): - did_create: Optional[FileOperationRegistrationOptions] - will_create: Optional[FileOperationRegistrationOptions] - did_rename: Optional[FileOperationRegistrationOptions] - will_rename: Optional[FileOperationRegistrationOptions] - did_delete: Optional[FileOperationRegistrationOptions] - will_delete: Optional[FileOperationRegistrationOptions] - - -class WorkspaceServerCapabilities(Model): - workspace_folders: Optional[WorkspaceFoldersServerCapabilities] - file_operations: Optional[WorkspaceFileOperationsServerCapabilities] - - -class ClientCapabilities(Model): - workspace: Optional[WorkspaceClientCapabilities] - text_document: Optional[TextDocumentClientCapabilities] - window: Optional[WindowClientCapabilities] - general: Optional[GeneralClientCapabilities] - experimental: Optional[Any] - - def get_capability(self, field: str, default: Any = None) -> Any: - """Check if ClientCapabilities has some nested value without raising - AttributeError. - - e.g. get_capability('text_document.synchronization.will_save') - """ - try: - value = reduce(getattr, field.split("."), self) - except AttributeError: - return default - - # If we reach the desired leaf value but it's None, return the default. - value = default if value is None else value - return value - - -class InitializeParams(WorkDoneProgressParams): - process_id: Optional[int] - root_uri: Optional[str] - capabilities: ClientCapabilities - client_info: Optional[ClientInfo] - locale: Optional[str] - root_path: Optional[str] - initialization_options: Optional[Any] - trace: Optional[Trace] - workspace_folders: Optional[List[WorkspaceFolder]] - - -class ServerCapabilities(Model): - text_document_sync: Optional[Union[TextDocumentSyncOptionsServerCapabilities, NumType]] - completion_provider: Optional[CompletionOptions] - hover_provider: Optional[Union[bool, HoverOptions]] - signature_help_provider: Optional[SignatureHelpOptions] - declaration_provider: Optional[Union[bool, DeclarationOptions, - DeclarationRegistrationOptions]] - definition_provider: Optional[Union[bool, DefinitionOptions]] - type_definition_provider: Optional[Union[bool, TypeDefinitionOptions, - TypeDefinitionRegistrationOptions]] - implementation_provider: Optional[Union[bool, ImplementationOptions, - ImplementationRegistrationOptions]] - references_provider: Optional[Union[bool, ReferenceOptions]] - document_highlight_provider: Optional[Union[bool, DocumentHighlightOptions]] - document_symbol_provider: Optional[Union[bool, DocumentSymbolOptions]] - code_action_provider: Optional[Union[bool, CodeActionOptions]] - code_lens_provider: Optional[CodeLensOptions] - document_link_provider: Optional[DocumentLinkOptions] - color_provider: Optional[Union[bool, DocumentColorOptions, - DocumentColorRegistrationOptions]] - document_formatting_provider: Optional[Union[bool, DocumentFormattingOptions]] - document_range_formatting_provider: Optional[Union[bool, - DocumentRangeFormattingOptions]] - document_on_type_formatting_provider: Optional[DocumentOnTypeFormattingOptions] - rename_provider: Optional[Union[bool, RenameOptions]] - folding_range_provider: Optional[Union[bool, FoldingRangeOptions, - FoldingRangeRegistrationOptions]] - execute_command_provider: Optional[ExecuteCommandOptions] - selection_range_provider: Optional[Union[bool, SelectionRangeOptions, - SelectionRangeRegistrationOptions]] - linked_editing_range_provider: Optional[Union[bool, LinkedEditingRangeOptions, - LinkedEditingRangeRegistrationOptions]] - call_hierarchy_provider: Optional[Union[bool, CallHierarchyOptions, - CallHierarchyRegistrationOptions]] - semantic_tokens_provider: Optional[Union[SemanticTokensOptions, - SemanticTokensRegistrationOptions]] - moniker_provider: Optional[Union[bool, MonikerOptions, - MonikerRegistrationOptions]] - workspace_symbol_provider: Optional[bool] - workspace: Optional[WorkspaceServerCapabilities] - experimental: Optional[Any] - - -class InitializeResult(Model): - capabilities: ServerCapabilities - server_info: Optional[ServerInfo] - - -class InitializedParams(Model): - pass diff --git a/pygls/lsp/types/language_features/__init__.py b/pygls/lsp/types/language_features/__init__.py deleted file mode 100644 index 118f7469..00000000 --- a/pygls/lsp/types/language_features/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# flake8: noqa -from pygls.lsp.types.language_features.call_hierarchy import * -from pygls.lsp.types.language_features.code_action import * -from pygls.lsp.types.language_features.code_lens import * -from pygls.lsp.types.language_features.color_presentation import * -from pygls.lsp.types.language_features.completion import * -from pygls.lsp.types.language_features.declaration import * -from pygls.lsp.types.language_features.definition import * -from pygls.lsp.types.language_features.document_color import * -from pygls.lsp.types.language_features.document_highlight import * -from pygls.lsp.types.language_features.document_link import * -from pygls.lsp.types.language_features.document_symbol import * -from pygls.lsp.types.language_features.folding_range import * -from pygls.lsp.types.language_features.formatting import * -from pygls.lsp.types.language_features.hover import * -from pygls.lsp.types.language_features.implementation import * -from pygls.lsp.types.language_features.linked_editing_range import * -from pygls.lsp.types.language_features.monikers import * -from pygls.lsp.types.language_features.on_type_formatting import * -from pygls.lsp.types.language_features.prepare_rename import * -from pygls.lsp.types.language_features.range_formatting import * -from pygls.lsp.types.language_features.references import * -from pygls.lsp.types.language_features.rename import * -from pygls.lsp.types.language_features.selection_range import * -from pygls.lsp.types.language_features.semantic_tokens import * -from pygls.lsp.types.language_features.signature_help import * -from pygls.lsp.types.language_features.type_definition import * diff --git a/pygls/lsp/types/language_features/call_hierarchy.py b/pygls/lsp/types/language_features/call_hierarchy.py deleted file mode 100644 index c00c4d81..00000000 --- a/pygls/lsp/types/language_features/call_hierarchy.py +++ /dev/null @@ -1,81 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Call Hierarchy -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Any, List, Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - StaticRegistrationOptions, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) -from pygls.lsp.types.language_features.document_symbol import SymbolKind, SymbolTag - - -class CallHierarchyClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class CallHierarchyOptions(WorkDoneProgressOptions): - - def __eq__(self, other: Any) -> bool: - return self.__dict__ == other.__dict__ - - -class CallHierarchyRegistrationOptions(TextDocumentRegistrationOptions, CallHierarchyOptions, StaticRegistrationOptions): - pass - - -class CallHierarchyPrepareParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass - - -class CallHierarchyItem(Model): - name: str - kind: SymbolKind - uri: str - range: Range - selection_range: Range - tags: Optional[List[SymbolTag]] - detail: Optional[str] - data: Optional[Any] - - -class CallHierarchyIncomingCallsParams(WorkDoneProgressParams, PartialResultParams): - item: CallHierarchyItem - - -class CallHierarchyIncomingCall(Model): - from_: CallHierarchyItem - from_ranges: List[Range] - - -class CallHierarchyOutgoingCallsParams(WorkDoneProgressParams, PartialResultParams): - item: CallHierarchyItem - - -class CallHierarchyOutgoingCall(Model): - to: CallHierarchyItem - from_ranges: List[Range] diff --git a/pygls/lsp/types/language_features/code_action.py b/pygls/lsp/types/language_features/code_action.py deleted file mode 100644 index 0a067f0b..00000000 --- a/pygls/lsp/types/language_features/code_action.py +++ /dev/null @@ -1,100 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Code Action -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Any, List, Optional, Union - -from pygls.lsp.types.basic_structures import (Command, Diagnostic, Model, PartialResultParams, - Range, ResolveSupportClientCapabilities, - TextDocumentIdentifier, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams, - WorkspaceEdit) - - -class CodeActionKind(str, enum.Enum): - Empty = '' - QuickFix = 'quickfix' - Refactor = 'refactor' - RefactorExtract = 'refactor.extract' - RefactorInline = 'refactor.inline' - RefactorRewrite = 'refactor.rewrite' - Source = 'source' - SourceOrganizeImports = 'source.organizeImports' - SourceFixAll = 'source.fixAll' - - -class CodeActionLiteralSupportActionKindClientCapabilities(Model): - value_set: Optional[List[Union[str, CodeActionKind]]] - - -class CodeActionLiteralSupportClientCapabilities(Model): - code_action_kind: Optional[CodeActionLiteralSupportActionKindClientCapabilities] - - -class CodeActionClientCapabilities(Model): - dynamic_registration: Optional[bool] - code_action_literal_support: Optional[CodeActionLiteralSupportClientCapabilities] - is_preferred_support: Optional[bool] - disabled_support: Optional[bool] - data_support: Optional[bool] - resolve_support: Optional[ResolveSupportClientCapabilities] - honors_change_annotations: Optional[bool] - - -class CodeActionOptions(WorkDoneProgressOptions): - code_action_kinds: Optional[List[CodeActionKind]] - resolve_provider: Optional[bool] - - -class CodeActionRegistrationOptions(TextDocumentRegistrationOptions, CodeActionOptions): - pass - - -class CodeActionContext(Model): - diagnostics: List[Diagnostic] - only: Optional[List[CodeActionKind]] - - -class CodeActionParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - range: Range - context: CodeActionContext - - -class CodeActionDisabled(Model): - reason: str - - -class CodeAction(Model): - title: str - kind: Optional[CodeActionKind] - diagnostics: Optional[List[Diagnostic]] - is_preferred: Optional[bool] - disabled: Optional[CodeActionDisabled] - edit: Optional[WorkspaceEdit] - command: Optional[Command] - data: Optional[Any] diff --git a/pygls/lsp/types/language_features/code_lens.py b/pygls/lsp/types/language_features/code_lens.py deleted file mode 100644 index 62aa3dca..00000000 --- a/pygls/lsp/types/language_features/code_lens.py +++ /dev/null @@ -1,53 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Code Lens -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Any, Optional - -from pygls.lsp.types.basic_structures import (Command, Model, PartialResultParams, Range, - TextDocumentIdentifier, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class CodeLensClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class CodeLensWorkspaceClientCapabilities(Model): - refresh_support: Optional[bool] - - -class CodeLensOptions(WorkDoneProgressOptions): - resolve_provider: Optional[bool] - - -class CodeLensParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class CodeLens(Model): - range: Range - command: Optional[Command] - data: Optional[Any] diff --git a/pygls/lsp/types/language_features/color_presentation.py b/pygls/lsp/types/language_features/color_presentation.py deleted file mode 100644 index 8c86b44f..00000000 --- a/pygls/lsp/types/language_features/color_presentation.py +++ /dev/null @@ -1,44 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Color Presentation -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - TextDocumentIdentifier, TextEdit, - WorkDoneProgressParams) -from pygls.lsp.types.language_features.document_color import Color - - -class ColorPresentationParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - color: Color - range: Range - - -class ColorPresentation(Model): - label: str - text_edit: Optional[TextEdit] - additional_text_edits: Optional[List[TextEdit]] diff --git a/pygls/lsp/types/language_features/completion.py b/pygls/lsp/types/language_features/completion.py deleted file mode 100644 index 83a38d8e..00000000 --- a/pygls/lsp/types/language_features/completion.py +++ /dev/null @@ -1,165 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Completion -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Any, List, Optional, Union - -from pygls.lsp.types.basic_structures import (Command, MarkupContent, MarkupKind, Model, - PartialResultParams, Range, - ResolveSupportClientCapabilities, - TextDocumentPositionParams, TextEdit, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class CompletionTriggerKind(enum.IntEnum): - Invoked = 1 - TriggerCharacter = 2 - TriggerForIncompleteCompletions = 3 - - -class CompletionContext(Model): - trigger_kind: CompletionTriggerKind - trigger_character: Optional[str] - - -class InsertTextFormat(enum.IntEnum): - PlainText = 1 - Snippet = 2 - - -class CompletionItemTag(enum.IntEnum): - Deprecated = 1 - - -class CompletionItemKind(enum.IntEnum): - 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 CompletionOptions(WorkDoneProgressOptions): - trigger_characters: Optional[List[str]] - all_commit_characters: Optional[List[str]] - resolve_provider: Optional[bool] - - -class CompletionParams(TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams): - context: Optional['CompletionContext'] - - -class CompletionItemKindClientCapabilities(Model): - value_set: Optional[List[CompletionItemKind]] - - -class CompletionTagSupportClientCapabilities(Model): - value_set: Optional[List[CompletionItemTag]] - - -class InsertTextMode(enum.IntEnum): - AsIs = 1 - AdjustIndentation = 2 - - -class InsertTextModeSupportClientCapabilities(Model): - value_set: List[InsertTextMode] - - -class CompletionItemClientCapabilities(Model): - snippet_support: Optional[bool] - commit_characters_support: Optional[bool] - documentation_format: Optional[List[MarkupKind]] - deprecated_support: Optional[bool] - preselect_support: Optional[bool] - tag_support: Optional[CompletionTagSupportClientCapabilities] - insert_replace_support: Optional[bool] - resolve_support: Optional[ResolveSupportClientCapabilities] - insert_text_mode_support: Optional[InsertTextModeSupportClientCapabilities] - - -class CompletionClientCapabilities(Model): - dynamic_registration: Optional[bool] - completion_item: Optional[CompletionItemClientCapabilities] - completion_item_kind: Optional[CompletionItemKindClientCapabilities] - context_support: Optional[bool] - - -class InsertReplaceEdit(Model): - new_text: str - insert: Range - replace: Range - - -class CompletionItem(Model): - label: str - kind: Optional[CompletionItemKind] - tags: Optional[List[CompletionItemTag]] - detail: Optional[str] - documentation: Optional[Union[str, MarkupContent]] - deprecated: Optional[bool] - preselect: Optional[bool] - sort_text: Optional[str] - filter_text: Optional[str] - insert_text: Optional[str] - insert_text_format: Optional[InsertTextFormat] - insert_text_mode: Optional[InsertTextMode] - text_edit: Optional[Union[TextEdit, InsertReplaceEdit]] - additional_text_edits: Optional[List[TextEdit]] - commit_characters: Optional[List[str]] - command: Optional[Command] - data: Optional[Any] - - -class CompletionList(Model): - is_incomplete: bool - items: List[CompletionItem] - - def add_item(self, completion_item): - self.items.append(completion_item) - - def add_items(self, completion_items): - self.items.extend(completion_items) diff --git a/pygls/lsp/types/language_features/declaration.py b/pygls/lsp/types/language_features/declaration.py deleted file mode 100644 index 80531f8c..00000000 --- a/pygls/lsp/types/language_features/declaration.py +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Declaration -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, StaticRegistrationOptions, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class DeclarationClientCapabilities(Model): - dynamic_registration: Optional[bool] - link_support: Optional[bool] - - -class DeclarationOptions(WorkDoneProgressOptions): - pass - - -class DeclarationRegistrationOptions(DeclarationOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class DeclarationParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass diff --git a/pygls/lsp/types/language_features/definition.py b/pygls/lsp/types/language_features/definition.py deleted file mode 100644 index b809aa66..00000000 --- a/pygls/lsp/types/language_features/definition.py +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Definition -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, TextDocumentPositionParams, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class DefinitionClientCapabilities(Model): - dynamic_registration: Optional[bool] - link_support: Optional[bool] - - -class DefinitionOptions(WorkDoneProgressOptions): - pass - - -class DefinitionParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass diff --git a/pygls/lsp/types/language_features/document_color.py b/pygls/lsp/types/language_features/document_color.py deleted file mode 100644 index a4a980db..00000000 --- a/pygls/lsp/types/language_features/document_color.py +++ /dev/null @@ -1,62 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Document Color -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - StaticRegistrationOptions, TextDocumentIdentifier, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class DocumentColorClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DocumentColorOptions(WorkDoneProgressOptions): - pass - - -class DocumentColorRegistrationOptions(DocumentColorOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class DocumentColorParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class Color(Model): - red: float - green: float - blue: float - alpha: float - - -class ColorInformation(Model): - range: Range - color: Color diff --git a/pygls/lsp/types/language_features/document_highlight.py b/pygls/lsp/types/language_features/document_highlight.py deleted file mode 100644 index 76d55482..00000000 --- a/pygls/lsp/types/language_features/document_highlight.py +++ /dev/null @@ -1,60 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Document Highlight -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class DocumentHighlightClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DocumentHighlightOptions(WorkDoneProgressOptions): - pass - - -class DocumentHighlightRegistrationOptions(TextDocumentRegistrationOptions, DocumentHighlightOptions): - pass - - -class DocumentHighlightParams(TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams): - pass - - -class DocumentHighlightKind(enum.IntEnum): - Text = 1 - Read = 2 - Write = 3 - - -class DocumentHighlight(Model): - range: Range - kind: Optional[DocumentHighlightKind] diff --git a/pygls/lsp/types/language_features/document_link.py b/pygls/lsp/types/language_features/document_link.py deleted file mode 100644 index 87b03c4f..00000000 --- a/pygls/lsp/types/language_features/document_link.py +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Document Link -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Any, Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - TextDocumentIdentifier, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class DocumentLinkClientCapabilities(Model): - dynamic_registration: Optional[bool] - tooltip_support: Optional[bool] - - -class DocumentLinkOptions(WorkDoneProgressOptions): - resolve_provider: Optional[bool] - - -class DocumentLinkParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class DocumentLink(Model): - range: Range - target: Optional[str] - tooltip: Optional[str] - data: Optional[Any] diff --git a/pygls/lsp/types/language_features/document_symbol.py b/pygls/lsp/types/language_features/document_symbol.py deleted file mode 100644 index ada0b674..00000000 --- a/pygls/lsp/types/language_features/document_symbol.py +++ /dev/null @@ -1,116 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Document Symbol -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import List, Optional - -from pygls.lsp.types.basic_structures import (Location, Model, PartialResultParams, Range, - TextDocumentIdentifier, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class SymbolKind(enum.IntEnum): - File = 1 - Module = 2 - Namespace = 3 - Package = 4 - Class = 5 - Method = 6 - Property = 7 - Field = 8 - Constructor = 9 - Enum = 10 - Interface = 11 - Function = 12 - Variable = 13 - Constant = 14 - String = 15 - Number = 16 - Boolean = 17 - Array = 18 - Object = 19 - Key = 20 - Null = 21 - EnumMember = 22 - Struct = 23 - Event = 24 - Operator = 25 - TypeParameter = 26 - - -class SymbolTag(enum.IntEnum): - Deprecated = 1 - - -class WorkspaceCapabilitiesSymbolKind(Model): - value_set: Optional[List[SymbolKind]] - - -class WorkspaceCapabilitiesTagSupport(Model): - value_set: List[SymbolKind] - - -class DocumentSymbolCapabilitiesTagSupport(Model): - value_set: List[SymbolTag] - - -class DocumentSymbolClientCapabilities(Model): - dynamic_registration: Optional[bool] - symbol_kind: Optional[WorkspaceCapabilitiesSymbolKind] - hierarchical_document_symbol_support: Optional[bool] - tag_support: Optional[WorkspaceCapabilitiesTagSupport] - label_support: Optional[bool] - - -class DocumentSymbolOptions(WorkDoneProgressOptions): - label: Optional[str] - - -class DocumentSymbolParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class DocumentSymbol(Model): - name: str - kind: SymbolKind - range: Range - selection_range: Range - detail: Optional[str] - children: Optional[List['DocumentSymbol']] - tags: Optional[List[SymbolTag]] - deprecated: Optional[bool] - - -DocumentSymbol.update_forward_refs() - - -class SymbolInformation(Model): - name: str - kind: SymbolKind - location: Location - container_name: Optional[str] - tags: Optional[List[SymbolTag]] - deprecated: Optional[bool] diff --git a/pygls/lsp/types/language_features/folding_range.py b/pygls/lsp/types/language_features/folding_range.py deleted file mode 100644 index 222a69b4..00000000 --- a/pygls/lsp/types/language_features/folding_range.py +++ /dev/null @@ -1,67 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Folding Range -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, NumType, PartialResultParams, - StaticRegistrationOptions, TextDocumentIdentifier, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class FoldingRangeClientCapabilities(Model): - dynamic_registration: Optional[bool] - range_limit: Optional[NumType] - line_folding_only: Optional[bool] - - -class FoldingRangeOptions(WorkDoneProgressOptions): - pass - - -class FoldingRangeRegistrationOptions(FoldingRangeOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class FoldingRangeParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class FoldingRangeKind(str, enum.Enum): - Comment = 'comment' - Imports = 'imports' - Region = 'region' - - -class FoldingRange(Model): - start_line: int - end_line: int - start_character: Optional[int] - end_character: Optional[int] - kind: Optional[FoldingRangeKind] diff --git a/pygls/lsp/types/language_features/formatting.py b/pygls/lsp/types/language_features/formatting.py deleted file mode 100644 index c1e29358..00000000 --- a/pygls/lsp/types/language_features/formatting.py +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Formatting -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, TextDocumentIdentifier, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class DocumentFormattingClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DocumentFormattingOptions(WorkDoneProgressOptions): - pass - - -class FormattingOptions(Model): - tab_size: int - insert_spaces: bool - trim_trailing_whitespace: Optional[bool] - insert_final_newline: Optional[bool] - trim_final_newlines: Optional[bool] - - -class DocumentFormattingParams(WorkDoneProgressParams): - text_document: TextDocumentIdentifier - options: FormattingOptions diff --git a/pygls/lsp/types/language_features/hover.py b/pygls/lsp/types/language_features/hover.py deleted file mode 100644 index ebf0d37f..00000000 --- a/pygls/lsp/types/language_features/hover.py +++ /dev/null @@ -1,57 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Hover -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional, Union - -from pygls.lsp.types.basic_structures import (MarkupContent, MarkupKind, Model, Range, - TextDocumentPositionParams, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class HoverClientCapabilities(Model): - dynamic_registration: Optional[bool] - content_format: Optional[List[MarkupKind]] - - -class HoverOptions(WorkDoneProgressOptions): - pass - - -class HoverParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass - - -class MarkedString(Model): - language: str - value: str - - -MarkedStringType = Union[str, MarkedString] - - -class Hover(Model): - contents: Union[MarkedStringType, List[MarkedStringType], MarkupContent] - range: Optional[Range] diff --git a/pygls/lsp/types/language_features/implementation.py b/pygls/lsp/types/language_features/implementation.py deleted file mode 100644 index d03102b4..00000000 --- a/pygls/lsp/types/language_features/implementation.py +++ /dev/null @@ -1,52 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Implementation -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, - StaticRegistrationOptions, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class ImplementationClientCapabilities(Model): - dynamic_registration: Optional[bool] - link_support: Optional[bool] - - -class ImplementationOptions(WorkDoneProgressOptions): - pass - - -class ImplementationRegistrationOptions(ImplementationOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class ImplementationParams(TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams): - pass diff --git a/pygls/lsp/types/language_features/linked_editing_range.py b/pygls/lsp/types/language_features/linked_editing_range.py deleted file mode 100644 index ed19b24d..00000000 --- a/pygls/lsp/types/language_features/linked_editing_range.py +++ /dev/null @@ -1,53 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Linked Editing Range -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional - -from pygls.lsp.types.basic_structures import (Model, Range, StaticRegistrationOptions, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class LinkedEditingRangeClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class LinkedEditingRangeOptions(WorkDoneProgressOptions): - pass - - -class LinkedEditingRangeRegistrationOptions(TextDocumentRegistrationOptions, LinkedEditingRangeOptions, StaticRegistrationOptions): - pass - - -class LinkedEditingRangeParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass - - -class LinkedEditingRanges(Model): - ranges: List[Range] - word_pattern: Optional[str] diff --git a/pygls/lsp/types/language_features/monikers.py b/pygls/lsp/types/language_features/monikers.py deleted file mode 100644 index 45fa1fec..00000000 --- a/pygls/lsp/types/language_features/monikers.py +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Monikers -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class MonikerClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class MonikerOptions(WorkDoneProgressOptions): - pass - - -class MonikerRegistrationOptions(TextDocumentRegistrationOptions, MonikerOptions): - pass - - -class MonikerParams(TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams): - pass - - -class UniquenessLevel(str, enum.Enum): - Document = 'document' - Project = 'project' - Group = 'group' - Scheme = 'scheme' - Global = 'global' - - -class MonikerKind(str, enum.Enum): - Import = 'import' - Export = 'export' - Local = 'local' - - -class Moniker(Model): - scheme: str - identifier: str - unique: UniquenessLevel - kind: Optional[MonikerKind] diff --git a/pygls/lsp/types/language_features/on_type_formatting.py b/pygls/lsp/types/language_features/on_type_formatting.py deleted file mode 100644 index 24d2c36e..00000000 --- a/pygls/lsp/types/language_features/on_type_formatting.py +++ /dev/null @@ -1,45 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - On Type Formatting -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional - -from pygls.lsp.types.basic_structures import (Model, TextDocumentPositionParams, - WorkDoneProgressOptions) -from pygls.lsp.types.language_features.formatting import FormattingOptions - - -class DocumentOnTypeFormattingClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DocumentOnTypeFormattingOptions(WorkDoneProgressOptions): - first_trigger_character: str - more_trigger_character: Optional[List[str]] - - -class DocumentOnTypeFormattingParams(TextDocumentPositionParams): - ch: str - options: FormattingOptions diff --git a/pygls/lsp/types/language_features/prepare_rename.py b/pygls/lsp/types/language_features/prepare_rename.py deleted file mode 100644 index c8fd5c66..00000000 --- a/pygls/lsp/types/language_features/prepare_rename.py +++ /dev/null @@ -1,37 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Prepare Rename -- - -Class attributes are named with camel case notation because client is expecting -that. -""" - -from pygls.lsp.types.basic_structures import Model, Range, TextDocumentPositionParams - - -class PrepareRenameParams(TextDocumentPositionParams): - pass - - -class PrepareRename(Model): - range: Range - placeholder: str diff --git a/pygls/lsp/types/language_features/range_formatting.py b/pygls/lsp/types/language_features/range_formatting.py deleted file mode 100644 index 346b4a9b..00000000 --- a/pygls/lsp/types/language_features/range_formatting.py +++ /dev/null @@ -1,45 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Range Formatting -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, Range, TextDocumentIdentifier, - WorkDoneProgressOptions, WorkDoneProgressParams) -from pygls.lsp.types.language_features.formatting import FormattingOptions - - -class DocumentRangeFormattingClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DocumentRangeFormattingOptions(WorkDoneProgressOptions): - pass - - -class DocumentRangeFormattingParams(WorkDoneProgressParams): - text_document: TextDocumentIdentifier - range: Range - options: FormattingOptions diff --git a/pygls/lsp/types/language_features/references.py b/pygls/lsp/types/language_features/references.py deleted file mode 100644 index 51d22042..00000000 --- a/pygls/lsp/types/language_features/references.py +++ /dev/null @@ -1,47 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - References -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, - TextDocumentPositionParams, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class ReferenceClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class ReferenceOptions(WorkDoneProgressOptions): - pass - - -class ReferenceContext(Model): - include_declaration: bool - - -class ReferenceParams(TextDocumentPositionParams, WorkDoneProgressParams, PartialResultParams): - context: ReferenceContext diff --git a/pygls/lsp/types/language_features/rename.py b/pygls/lsp/types/language_features/rename.py deleted file mode 100644 index d927efe1..00000000 --- a/pygls/lsp/types/language_features/rename.py +++ /dev/null @@ -1,50 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Rename -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, TextDocumentPositionParams, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class PrepareSupportDefaultBehavior(enum.IntEnum): - Identifier = 1 - - -class RenameClientCapabilities(Model): - dynamic_registration: Optional[bool] - prepare_support: Optional[bool] - prepare_support_default_behavior: Optional[PrepareSupportDefaultBehavior] - honors_change_annotations: Optional[bool] - - -class RenameOptions(WorkDoneProgressOptions): - prepare_provider: Optional[bool] - - -class RenameParams(TextDocumentPositionParams, WorkDoneProgressParams): - new_name: str diff --git a/pygls/lsp/types/language_features/selection_range.py b/pygls/lsp/types/language_features/selection_range.py deleted file mode 100644 index c17c4844..00000000 --- a/pygls/lsp/types/language_features/selection_range.py +++ /dev/null @@ -1,60 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Selection Range -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import List, Optional - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Position, Range, - StaticRegistrationOptions, TextDocumentIdentifier, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class SelectionRangeClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class SelectionRangeOptions(WorkDoneProgressOptions): - pass - - -class SelectionRangeRegistrationOptions(SelectionRangeOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class SelectionRangeParams(WorkDoneProgressParams, PartialResultParams): - query: str - text_document: TextDocumentIdentifier - positions: List[Position] - - -class SelectionRange(Model): - range: Range - parent: Optional['SelectionRange'] - - -SelectionRange.update_forward_refs() diff --git a/pygls/lsp/types/language_features/semantic_tokens.py b/pygls/lsp/types/language_features/semantic_tokens.py deleted file mode 100644 index 9540fb89..00000000 --- a/pygls/lsp/types/language_features/semantic_tokens.py +++ /dev/null @@ -1,151 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Semantic Tokens -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Dict, List, Optional, Union - -from pygls.lsp.types.basic_structures import (Model, PartialResultParams, Range, - StaticRegistrationOptions, TextDocumentIdentifier, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class SemanticTokensWorkspaceClientCapabilities(Model): - refresh_support: Optional[bool] - - -class SemanticTokenTypes(str, enum.Enum): - Namespace = 'namespace' - Type = 'type' - Class = 'class' - Enum = 'enum' - Interface = 'interface' - Struct = 'struct' - TypeParameter = 'typeParameter' - Parameter = 'parameter' - Variable = 'variable' - Property = 'property' - EnumMember = 'enumMember' - Event = 'event' - Function = 'function' - Method = 'method' - Macro = 'macro' - Keyword = 'keyword' - Modifier = 'modifier' - Comment = 'comment' - String = 'string' - Number = 'number' - Regexp = 'regexp' - Operator = 'operator' - - -class SemanticTokenModifiers(str, enum.Enum): - Declaration = 'declaration' - Definition = 'definition' - Readonly = 'readonly' - Static = 'static' - Deprecated = 'deprecated' - Abstract = 'abstract' - Async = 'async' - Modification = 'modification' - Documentation = 'documentation' - DefaultLibrary = 'defaultLibrary' - - -class TokenFormat(str, enum.Enum): - Relative = 'relative' - - -class SemanticTokensLegend(Model): - token_types: List[str] - token_modifiers: List[str] - - -class SemanticTokensRequestsFull(Model): - delta: Optional[bool] - - -class SemanticTokensRequests(Model): - range: Optional[Union[bool, Dict]] - full: Optional[Union[bool, SemanticTokensRequestsFull]] - - -class SemanticTokensClientCapabilities(Model): - requests: SemanticTokensRequests - token_types: List[str] - token_modifiers: List[str] - formats: List[TokenFormat] - overlapping_token_support: Optional[bool] - multiline_token_support: Optional[bool] - dynamic_registration: Optional[bool] - - -class SemanticTokensOptions(WorkDoneProgressOptions): - legend: SemanticTokensLegend - range: Optional[Union[bool, Dict]] - full: Optional[Union[bool, SemanticTokensRequestsFull]] - - -class SemanticTokensRegistrationOptions(TextDocumentRegistrationOptions, SemanticTokensOptions, StaticRegistrationOptions): - pass - - -class SemanticTokensParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - - -class SemanticTokens(Model): - data: List[int] - result_id: Optional[str] - - -class SemanticTokensPartialResult(Model): - data: List[int] - - -class SemanticTokensDeltaParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - previous_result_id: str - - -class SemanticTokensEdit(Model): - start: int - delete_count: int - data: Optional[List[int]] - - -class SemanticTokensDelta(Model): - edits: List[SemanticTokensEdit] - result_id: Optional[str] - - -class SemanticTokensDeltaPartialResult(Model): - edits: List[SemanticTokensEdit] - - -class SemanticTokensRangeParams(WorkDoneProgressParams, PartialResultParams): - text_document: TextDocumentIdentifier - range: Range diff --git a/pygls/lsp/types/language_features/signature_help.py b/pygls/lsp/types/language_features/signature_help.py deleted file mode 100644 index 79c7b943..00000000 --- a/pygls/lsp/types/language_features/signature_help.py +++ /dev/null @@ -1,88 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Signature Help -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import List, Optional, Tuple, Union - -from pygls.lsp.types.basic_structures import (MarkupContent, MarkupKind, Model, NumType, - TextDocumentPositionParams, WorkDoneProgressOptions, - WorkDoneProgressParams) - - -class SignatureHelpInformationParameterInformationClientCapabilities(Model): - label_offset_support: Optional[bool] - - -class SignatureHelpInformationClientCapabilities(Model): - documentation_format: Optional[List[MarkupKind]] - parameter_information: Optional[SignatureHelpInformationParameterInformationClientCapabilities] - active_parameter_support: Optional[bool] - - -class SignatureHelpClientCapabilities(Model): - dynamic_registration: Optional[bool] - signature_information: Optional[SignatureHelpInformationClientCapabilities] - context_support: Optional[bool] - - -class SignatureHelpOptions(WorkDoneProgressOptions): - trigger_characters: Optional[List[str]] - retrigger_characters: Optional[List[str]] - - -class SignatureHelpTriggerKind(enum.IntEnum): - Invoked = 1 - TriggerCharacter = 2 - ContentChange = 3 - - -class ParameterInformation(Model): - label: Union[str, Tuple[int, int]] - documentation: Optional[Union[str, MarkupContent]] - - -class SignatureInformation(Model): - label: str - documentation: Optional[Union[str, MarkupContent]] - parameters: Optional[List[ParameterInformation]] - active_parameter: Optional[int] - - -class SignatureHelp(Model): - signatures: List[SignatureInformation] - active_signature: Optional[NumType] - active_parameter: Optional[NumType] - - -class SignatureHelpContext(Model): - trigger_kind: SignatureHelpTriggerKind - is_retrigger: bool - trigger_character: Optional[str] - active_signature_help: Optional[SignatureHelp] - - -class SignatureHelpParams(TextDocumentPositionParams, WorkDoneProgressParams): - context: Optional[SignatureHelpContext] diff --git a/pygls/lsp/types/language_features/type_definition.py b/pygls/lsp/types/language_features/type_definition.py deleted file mode 100644 index acaea913..00000000 --- a/pygls/lsp/types/language_features/type_definition.py +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Language Features - Type Definition -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -from typing import Optional - -from pygls.lsp.types.basic_structures import (Model, StaticRegistrationOptions, - TextDocumentPositionParams, - TextDocumentRegistrationOptions, - WorkDoneProgressOptions, WorkDoneProgressParams) - - -class TypeDefinitionClientCapabilities(Model): - dynamic_registration: Optional[bool] - link_support: Optional[bool] - - -class TypeDefinitionOptions(WorkDoneProgressOptions): - pass - - -class TypeDefinitionRegistrationOptions(TypeDefinitionOptions, - TextDocumentRegistrationOptions, - StaticRegistrationOptions): - pass - - -class TypeDefinitionParams(TextDocumentPositionParams, WorkDoneProgressParams): - pass diff --git a/pygls/lsp/types/text_synchronization.py b/pygls/lsp/types/text_synchronization.py deleted file mode 100644 index 3011a266..00000000 --- a/pygls/lsp/types/text_synchronization.py +++ /dev/null @@ -1,46 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Text Synchronization -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Optional - -from pygls.lsp.types import TextDocumentRegistrationOptions -from pygls.lsp.types.basic_structures import Model - - -class TextDocumentSyncKind(enum.IntEnum): - NONE = 0 - FULL = 1 - INCREMENTAL = 2 - - -class TextDocumentSyncOptions(Model): - open_close: Optional[bool] - change: Optional[TextDocumentSyncKind] - - -class TextDocumentSaveRegistrationOptions(TextDocumentRegistrationOptions): - include_text: Optional[bool] diff --git a/pygls/lsp/types/window.py b/pygls/lsp/types/window.py deleted file mode 100644 index a88115a5..00000000 --- a/pygls/lsp/types/window.py +++ /dev/null @@ -1,91 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Window -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Callable, List, Optional - -from pygls.lsp.types.basic_structures import URI, Model, NumType, ProgressToken, Range - - -class MessageType(enum.IntEnum): - Error = 1 - Warning = 2 - Info = 3 - Log = 4 - - -class ShowMessageParams(Model): - type: MessageType - message: str - - -class MessageActionItem(Model): - title: str - - -class ShowMessageRequestParams(Model): - type: MessageType - message: str - actions: Optional[List[MessageActionItem]] - - -class ShowDocumentClientCapabilities(Model): - support: Optional[bool] - - -class ShowDocumentParams(Model): - uri: URI - external: Optional[bool] - take_focus: Optional[bool] - selection: Optional[Range] - - -class ShowDocumentResult(Model): - success: bool - - -class ShowMessageRequestActionItem(Model): - additional_properties_support: Optional[bool] - - -class ShowMessageRequestClientCapabilities(Model): - message_action_item: Optional[ShowMessageRequestActionItem] - - -class LogMessageParams(Model): - type: NumType - message: str - - -class WorkDoneProgressCreateParams(Model): - token: ProgressToken - - -class WorkDoneProgressCancelParams(Model): - token: ProgressToken - - -ShowDocumentCallbackType = Callable[[ShowDocumentResult], None] diff --git a/pygls/lsp/types/workspace.py b/pygls/lsp/types/workspace.py deleted file mode 100644 index 63111e41..00000000 --- a/pygls/lsp/types/workspace.py +++ /dev/null @@ -1,202 +0,0 @@ -############################################################################ -# Original work Copyright 2017 Palantir Technologies, Inc. # -# Original work licensed under the MIT License. # -# See ThirdPartyNotices.txt in the project root for license information. # -# All modifications Copyright (c) Open Law Library. All rights reserved. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -"""This module contains Language Server Protocol types -https://microsoft.github.io/language-server-protocol/specification - --- Workspace -- - -Class attributes are named with camel case notation because client is expecting -that. -""" -import enum -from typing import Any, List, Optional, Union - -from pygls.lsp.types.basic_structures import (Model, NumType, PartialResultParams, Range, - TextDocumentIdentifier, TextDocumentItem, - VersionedTextDocumentIdentifier, - WorkDoneProgressOptions, WorkDoneProgressParams, - WorkspaceEdit) -from pygls.lsp.types.language_features.document_symbol import (WorkspaceCapabilitiesSymbolKind, - WorkspaceCapabilitiesTagSupport) - - -class WorkspaceFoldersServerCapabilities(Model): - supported: Optional[bool] - change_notifications: Optional[Union[bool, str]] - - -class WorkspaceFolder(Model): - uri: str - name: str - - -class WorkspaceFoldersChangeEvent(Model): - added: List[WorkspaceFolder] - removed: List[WorkspaceFolder] - - -class DidChangeWorkspaceFoldersParams(Model): - event: WorkspaceFoldersChangeEvent - - -class DidChangeConfigurationClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class DidChangeConfigurationParams(Model): - settings: Any - - -class ConfigurationItem(Model): - scope_uri: Optional[str] - section: Optional[str] - - -class ConfigurationParams(Model): - items: List[ConfigurationItem] - - -class DidChangeWatchedFilesClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class WatchKind(enum.IntFlag): - Create = 1 - Change = 2 - Delete = 4 - - -class FileSystemWatcher(Model): - glob_pattern: str - kind: Optional[WatchKind] - - -class DidChangeWatchedFilesRegistrationOptions(Model): - watchers: List[FileSystemWatcher] - - -class FileChangeType(enum.IntEnum): - Created = 1 - Changed = 2 - Deleted = 3 - - -class FileEvent(Model): - uri: str - type: FileChangeType - - -class DidChangeWatchedFilesParams(Model): - changes: List[FileEvent] - - -class WorkspaceSymbolClientCapabilities(Model): - dynamic_registration: Optional[bool] - symbol_kind: Optional[WorkspaceCapabilitiesSymbolKind] - tag_support: Optional[WorkspaceCapabilitiesTagSupport] - - -class WorkspaceSymbolOptions(WorkDoneProgressOptions): - pass - - -class WorkspaceSymbolRegistrationOptions(WorkspaceSymbolOptions): - pass - - -class WorkspaceSymbolParams(WorkDoneProgressParams, PartialResultParams): - query: str - - -class ExecuteCommandClientCapabilities(Model): - dynamic_registration: Optional[bool] - - -class ExecuteCommandOptions(WorkDoneProgressOptions): - commands: List[str] - - -class ExecuteCommandRegistrationOptions(ExecuteCommandOptions): - pass - - -class ExecuteCommandParams(WorkDoneProgressParams): - command: str - arguments: Optional[List[Any]] - - -class ApplyWorkspaceEditParams(Model): - edit: WorkspaceEdit - label: Optional[str] - - -class ApplyWorkspaceEditResponse(Model): - applied: bool - failure_reason: Optional[str] - - -class DidOpenTextDocumentParams(Model): - text_document: TextDocumentItem - - -class TextDocumentContentChangeEvent(Model): - range: Optional[Range] - range_length: Optional[NumType] - text: str - - -class TextDocumentContentChangeTextEvent(Model): - text: str - - -class DidChangeTextDocumentParams(Model): - text_document: VersionedTextDocumentIdentifier - content_changes: Union[List[TextDocumentContentChangeEvent], - List[TextDocumentContentChangeTextEvent]] - - -class TextDocumentSaveReason(enum.IntEnum): - Manual = 1 - AfterDelay = 2 - FocusOut = 3 - - -class WillSaveTextDocumentParams(Model): - text_document: TextDocumentIdentifier - reason: TextDocumentSaveReason - - -class SaveOptions(Model): - include_text: Optional[bool] - - -class DidSaveTextDocumentParams(Model): - text_document: TextDocumentIdentifier - text: Optional[str] - - -class DidCloseTextDocumentParams(Model): - text_document: TextDocumentIdentifier - - -class TextDocumentSyncClientCapabilities(Model): - dynamic_registration: Optional[bool] - will_save: Optional[bool] - will_save_wait_until: Optional[bool] - did_save: Optional[bool] From 3dd130df28fbf510119a4d5805ee4bf79f03d712 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 16:05:30 +0100 Subject: [PATCH 02/20] Drop pydantic, use lsprotocol. The `lsprotocol.types` module is re-exported through the `pygls.lsp.types` module hopefully minimsing the number of broken imports. This also drops pygls' `LSP_METHODS_MAP` in favour of the `METHOD_TO_TYPES` map provided by `lsprotocol`. The `get_method_xxx_type` functions have been adjusted to use the new mapping. As far as I can tell `lsprotocol` doesn't provide generic JSON RPC message types (except for `ResponseErrorMessage`) so the old `JsonRPCNotification`, `JsonRPCResponseMessage` and `JsonRPCRequestMessage` types have been preserved and converted to `attrs`. --- pygls/lsp/__init__.py | 316 ++++-------------------------------------- pygls/lsp/types.py | 24 ++++ setup.cfg | 7 +- 3 files changed, 55 insertions(+), 292 deletions(-) create mode 100644 pygls/lsp/types.py diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index ff50c1e8..c7d8dbf3 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -14,316 +14,60 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from typing import Any, List, Union +from typing import Any, Union +import attrs +from lsprotocol.types import METHOD_TO_TYPES from typeguard import check_type from pygls.exceptions import MethodTypeNotRegisteredError -from pygls.lsp.methods import * -from pygls.lsp.types import * -__LSP_VERSION__ = "3.15" +@attrs.define +class JsonRPCNotification: + """A class that represents json rpc notification message.""" -# Holds lsp methods and their appropriate types. It is used for type-checking. -# { -# 'COMPLETION': (CompletionOptions, CompletionParams, Union[List[CompletionItem], CompletionList, None]) -# } -# where: -# - CompletionOptions is used when registering a method: -# - CompletionParams are received from the client -# - Union[List[CompletionItem], CompletionList, None] should be returned by the server (result field of ResponseMessage) -# -# @json_server.feature(COMPLETION, CompletionOptions(trigger_characters=[','])) -# def completions(params: CompletionParams = None) -> Union[List[CompletionItem], CompletionList, None]: -# """Returns completion items.""" + jsonrpc: str + method: str + params: Any -# TODO: support partial-results types -LSP_METHODS_MAP = { - # Special methods - CANCEL_REQUEST: (None, CancelParams, None, ), - PROGRESS_NOTIFICATION: (None, ProgressParams, None, ), - LOG_TRACE_NOTIFICATION: (None, None, LogTraceParams, ), - SET_TRACE_NOTIFICATION: (None, SetTraceParams, None, ), - # General messages - INITIALIZE: (None, InitializeParams, InitializeResult, ), - INITIALIZED: (None, InitializedParams, None, ), - SHUTDOWN: (None, None, None, ), - EXIT: (None, None, None, ), - # Window - WINDOW_SHOW_MESSAGE: (None, None, ShowMessageParams, ), - WINDOW_SHOW_DOCUMENT: (None, ShowDocumentParams, ShowDocumentResult, ), - WINDOW_SHOW_MESSAGE_REQUEST: (None, None, ShowMessageRequestParams, ), - WINDOW_LOG_MESSAGE: (None, None, LogMessageParams, ), - WINDOW_WORK_DONE_PROGRESS_CREATE: (None, None, WorkDoneProgressCreateParams, ), - WINDOW_WORK_DONE_PROGRESS_CANCEL: (None, None, WorkDoneProgressCancelParams, ), - # Telemetry - TELEMETRY_EVENT: (None, None, Any, ), - # Client - (un)register capabilities - CLIENT_REGISTER_CAPABILITY: (None, None, RegistrationParams, ), - CLIENT_UNREGISTER_CAPABILITY: (None, None, UnregistrationParams, ), - # Workspace - WORKSPACE_APPLY_EDIT: (None, ApplyWorkspaceEditResponse, ApplyWorkspaceEditParams, ), - WORKSPACE_CODE_LENS_REFRESH: (None, None, None), - WORKSPACE_CONFIGURATION: (None, List[Any], ConfigurationParams, ), - WORKSPACE_DID_CHANGE_CONFIGURATION: (None, DidChangeConfigurationParams, None, ), - WORKSPACE_DID_CHANGE_WATCHED_FILES: (None, DidChangeWatchedFilesParams, None, ), - WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS: (None, DidChangeWorkspaceFoldersParams, None, ), - WORKSPACE_EXECUTE_COMMAND: (None, ExecuteCommandParams, Optional[Any], ), - WORKSPACE_FOLDERS: (None, Optional[List[WorkspaceFolder]], None, ), - WORKSPACE_SEMANTIC_TOKENS_REFRESH: (None, None, None), - WORKSPACE_SYMBOL: (None, WorkspaceSymbolParams, Optional[List[SymbolInformation]], ), - # Text Document Synchronization - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE: ( - Union[CallHierarchyOptions, CallHierarchyRegistrationOptions], - CallHierarchyPrepareParams, - Optional[List[CallHierarchyItem]], - ), - TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS: ( - None, - CallHierarchyIncomingCallsParams, - Optional[List[CallHierarchyIncomingCall]], - ), - TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS: ( - None, - CallHierarchyOutgoingCallsParams, - Optional[List[CallHierarchyOutgoingCall]], - ), - TEXT_DOCUMENT_DID_OPEN: (None, DidOpenTextDocumentParams, None, ), - TEXT_DOCUMENT_DID_CHANGE: (None, DidChangeTextDocumentParams, None, ), - TEXT_DOCUMENT_WILL_SAVE: (None, WillSaveTextDocumentParams, None, ), - TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL: (None, WillSaveTextDocumentParams, None, ), - TEXT_DOCUMENT_DID_SAVE: ( - TextDocumentSaveRegistrationOptions, - DidSaveTextDocumentParams, - None, - ), - TEXT_DOCUMENT_LINKED_EDITING_RANGE: ( - Union[LinkedEditingRangeOptions, LinkedEditingRangeRegistrationOptions], - LinkedEditingRangeParams, - Optional[LinkedEditingRanges], - ), - TEXT_DOCUMENT_MONIKER: ( - Union[MonikerOptions, MonikerRegistrationOptions], - MonikerParams, - Optional[List[Moniker]], - ), - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: ( - Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - SemanticTokensParams, - Union[SemanticTokensPartialResult, Optional[SemanticTokens]], - ), - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: ( - Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - SemanticTokensDeltaParams, - Union[SemanticTokensDeltaPartialResult, Optional[Union[SemanticTokens, SemanticTokensDelta]]], - ), - TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: ( - Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - SemanticTokensRangeParams, - Union[SemanticTokensPartialResult, Optional[SemanticTokens]], +@attrs.define +class JsonRPCRequestMessage: + """A class that represents json rpc request message.""" - ), - TEXT_DOCUMENT_DID_CLOSE: (None, DidCloseTextDocumentParams, None, ), - # File operations - WORKSPACE_WILL_CREATE_FILES: ( - FileOperationRegistrationOptions, - CreateFilesParams, - Optional[WorkspaceEdit], - ), - WORKSPACE_DID_CREATE_FILES: ( - FileOperationRegistrationOptions, - CreateFilesParams, - None, - ), - WORKSPACE_WILL_RENAME_FILES: ( - FileOperationRegistrationOptions, - RenameFilesParams, - Optional[WorkspaceEdit], - ), - WORKSPACE_DID_RENAME_FILES: ( - FileOperationRegistrationOptions, - RenameFilesParams, - None, - ), - WORKSPACE_WILL_DELETE_FILES: ( - FileOperationRegistrationOptions, - DeleteFilesParams, - Optional[WorkspaceEdit], - ), - WORKSPACE_DID_DELETE_FILES: ( - FileOperationRegistrationOptions, - DeleteFilesParams, - None, - ), - # Diagnostics notification - TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS: (None, None, PublishDiagnosticsParams, ), - # Language features - COMPLETION: ( - CompletionOptions, - CompletionParams, - Union[List[CompletionItem], CompletionList, None], - ), - COMPLETION_ITEM_RESOLVE: ( - None, - CompletionItem, - CompletionItem, - ), - HOVER: ( - HoverOptions, - HoverParams, - Optional[Hover], - ), - SIGNATURE_HELP: ( - SignatureHelpOptions, - SignatureHelpParams, - Optional[SignatureHelp], - ), - DECLARATION: ( - DeclarationOptions, - DeclarationParams, - Union[Location, List[Location], List[LocationLink], None], - ), - DEFINITION: ( - DefinitionOptions, - DefinitionParams, - Union[Location, List[Location], List[LocationLink], None], - ), - TYPE_DEFINITION: ( - TypeDefinitionOptions, - TypeDefinitionParams, - Union[Location, List[Location], List[LocationLink], None], - ), - IMPLEMENTATION: ( - ImplementationOptions, - ImplementationParams, - Union[Location, List[Location], List[LocationLink], None], - ), - REFERENCES: ( - ReferenceOptions, - ReferenceParams, - Optional[List[Location]], - ), - DOCUMENT_HIGHLIGHT: ( - DocumentHighlightOptions, - DocumentHighlightParams, - Optional[List[DocumentHighlight]], - ), - DOCUMENT_SYMBOL: ( - DocumentSymbolOptions, - DocumentSymbolParams, - Union[List[DocumentSymbol], List[SymbolInformation], None], - ), - CODE_ACTION: ( - Union[CodeActionOptions, CodeActionRegistrationOptions], - CodeActionParams, - Optional[List[Union[Command, CodeAction]]], - ), - CODE_ACTION_RESOLVE: ( - None, - CodeAction, - CodeAction, - ), - CODE_LENS: ( - CodeLensOptions, - CodeLensParams, - Optional[List[CodeLens]], - ), - CODE_LENS_RESOLVE: ( - None, - CodeLens, - CodeLens, - ), - DOCUMENT_LINK: ( - DocumentLinkOptions, - DocumentLinkParams, - Optional[List[DocumentLink]], - ), - DOCUMENT_LINK_RESOLVE: ( - None, - DocumentLink, - DocumentLink, - ), - DOCUMENT_COLOR: ( - DocumentColorOptions, - DocumentColorParams, - List[ColorInformation], - ), - COLOR_PRESENTATION: ( - None, - ColorPresentationParams, - List[ColorPresentation], - ), - FORMATTING: ( - DocumentFormattingOptions, - DocumentFormattingParams, - Optional[List[TextEdit]], - ), - RANGE_FORMATTING: ( - DocumentRangeFormattingOptions, - DocumentRangeFormattingParams, - Optional[List[TextEdit]], - ), - ON_TYPE_FORMATTING: ( - DocumentOnTypeFormattingOptions, - DocumentOnTypeFormattingParams, - Optional[List[TextEdit]], - ), - RENAME: ( - RenameOptions, - RenameParams, - Optional[WorkspaceEdit], - ), - PREPARE_RENAME: ( - None, - PrepareRenameParams, - Union[Range, PrepareRename, None], - ), - FOLDING_RANGE: ( - FoldingRangeOptions, - FoldingRangeParams, - Optional[List[FoldingRange]], - ), - SELECTION_RANGE: ( - SelectionRangeOptions, - SelectionRangeParams, - Optional[List[SelectionRange]], - ), -} + jsonrpc: str + id: Union[int, str] + method: str + params: Any -def get_method_registration_options_type(method_name, lsp_methods_map=LSP_METHODS_MAP): - try: - return lsp_methods_map[method_name][0] - except KeyError: - raise MethodTypeNotRegisteredError(method_name) +@attrs.define +class JsonRPCResponseMessage: + """A class that represents json rpc response message.""" + jsonrpc: str + id: Union[int, str] + result: Union[Any, None] = attrs.field(default=None) -def get_method_params_type(method_name, lsp_methods_map=LSP_METHODS_MAP): + +def get_method_registration_options_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: - return lsp_methods_map[method_name][1] + return lsp_methods_map[method_name][3] except KeyError: raise MethodTypeNotRegisteredError(method_name) -def get_method_return_type(method_name, lsp_methods_map=LSP_METHODS_MAP): +def get_method_params_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: return lsp_methods_map[method_name][2] except KeyError: raise MethodTypeNotRegisteredError(method_name) -def _get_origin(t): +def get_method_return_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: - return t.__origin__ - except AttributeError: - return None - - -def _get_args(t): - try: - return t.__args__ - except AttributeError: - return None + return lsp_methods_map[method_name][1] + except KeyError: + raise MethodTypeNotRegisteredError(method_name) def is_instance(o, t): diff --git a/pygls/lsp/types.py b/pygls/lsp/types.py new file mode 100644 index 00000000..f36b6e37 --- /dev/null +++ b/pygls/lsp/types.py @@ -0,0 +1,24 @@ +############################################################################ +# Copyright(c) Open Law Library. All rights reserved. # +# See ThirdPartyNotices.txt in the project root for additional notices. # +# # +# Licensed under the Apache License, Version 2.0 (the "License") # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http: // www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ +# flake8: noqa +from typing import Any, Callable, List + +from lsprotocol.types import * + + +ConfigCallbackType = Callable[[List[Any]], None] +ShowDocumentCallbackType = Callable[[ShowDocumentResult], None] diff --git a/setup.cfg b/setup.cfg index 13248482..f9b89926 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,8 +33,7 @@ python_requires = >=3.7,<3.12 packages = find: zip_safe = False install_requires = - pydantic>=1.9.1 ; python_version<"3.11" - pydantic>=1.10.2 ; python_version>="3.11" + lsprotocol typeguard>=2.10.0,<3 include_package_data = True tests_require = @@ -72,7 +71,3 @@ max-line-length = 99 [mypy] ignore_missing_imports = True -plugins = pydantic.mypy - -[pydantic-mypy] -init_forbid_extra = True From 2f9ca7eeb23cf17aa94910abd3e1ac6e118c8d0c Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 16:15:36 +0100 Subject: [PATCH 03/20] Fix `Options` type checking The machine readable version of the LSP spec (and therefore `lsprotocol`) provides a mapping from an LSP method's name to its `RegistrationOptions` type, which is an extension of the method's `Options` type used when computing a server's capabilities. This means the `RegistrationOptions` type includes additional fields that are not valid within the `ServerCapabilities` response. This commit introduces a new `get_method_options_type` function that returns the correct `Options` type for a given method, automatically deriving the type name from the result of the existing `get_method_registration_options_type` function when appropriate. --- pygls/feature_manager.py | 4 +-- pygls/lsp/__init__.py | 65 +++++++++++++++++++++++++++++++++-- tests/test_feature_manager.py | 2 +- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/pygls/feature_manager.py b/pygls/feature_manager.py index fb249523..dc02ae40 100644 --- a/pygls/feature_manager.py +++ b/pygls/feature_manager.py @@ -25,7 +25,7 @@ ATTR_REGISTERED_NAME, ATTR_REGISTERED_TYPE, PARAM_LS) from pygls.exceptions import (CommandAlreadyRegisteredError, FeatureAlreadyRegisteredError, ThreadDecoratorError, ValidationError) -from pygls.lsp import get_method_registration_options_type, is_instance +from pygls.lsp import get_method_options_type, is_instance logger = logging.getLogger(__name__) @@ -169,7 +169,7 @@ def decorator(f): self._features[feature_name] = wrapped if options: - options_type = get_method_registration_options_type(feature_name) + options_type = get_method_options_type(feature_name) if options_type and not is_instance(options, options_type): raise TypeError( (f'Options of method "{feature_name}"' diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index c7d8dbf3..5a810c7b 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -14,14 +14,28 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from typing import Any, Union +from typing import Any, Optional, Union import attrs -from lsprotocol.types import METHOD_TO_TYPES +from lsprotocol.types import ( + ALL_TYPES_MAP, + METHOD_TO_TYPES, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, + SemanticTokensLegend, + SemanticTokensRegistrationOptions +) from typeguard import check_type from pygls.exceptions import MethodTypeNotRegisteredError +METHOD_TO_OPTIONS = { + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], +} + @attrs.define class JsonRPCNotification: """A class that represents json rpc notification message.""" @@ -49,13 +63,58 @@ class JsonRPCResponseMessage: result: Union[Any, None] = attrs.field(default=None) -def get_method_registration_options_type(method_name, lsp_methods_map=METHOD_TO_TYPES): +def get_method_registration_options_type( + method_name, lsp_methods_map=METHOD_TO_TYPES +) -> Optional[Any]: + """The type corresponding with a method's options when dynamically registering + capability for it.""" + try: return lsp_methods_map[method_name][3] except KeyError: raise MethodTypeNotRegisteredError(method_name) +def get_method_options_type( + method_name, lsp_options_map=METHOD_TO_OPTIONS, lsp_methods_map=METHOD_TO_TYPES +) -> Optional[Any]: + """Return the type corresponding with a method's ``ServerCapabilities`` fields. + + In the majority of cases this simply means returning the ``Options`` + type, which we can easily derive from the method's + ``RegistrationOptions`` type. + + However, where the options are more involved (such as semantic tokens) and + ``pygls`` does some extra work to help derive the options for the user the type + has to be provided via the ``lsp_options_map`` + + Arguments: + method_name: + The lsp method name to retrieve the options for + + lsp_options_map: + The map used to override the default options type finding behavior + + lsp_methods_map: + The standard map used to look up the various method types. + """ + + options_type = lsp_options_map.get(method_name, None) + if options_type is not None: + return options_type + + registration_type = get_method_registration_options_type(method_name, lsp_methods_map) + if registration_type is None: + return None + + type_name = registration_type.__name__.replace('Registration', '') + options_type = ALL_TYPES_MAP.get(type_name, None) + + if options_type is None: + raise MethodTypeNotRegisteredError(method_name) + + return options_type + def get_method_params_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: return lsp_methods_map[method_name][2] diff --git a/tests/test_feature_manager.py b/tests/test_feature_manager.py index 682ef4f7..764b3e86 100644 --- a/tests/test_feature_manager.py +++ b/tests/test_feature_manager.py @@ -96,7 +96,7 @@ class Options: TypeError, match=( f'Options of method "{methods.COMPLETION}" should be instance of type ' - "" + "" ), # noqa ): From 389da66a06e8637412baef1765b290f22d314064 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 16:26:19 +0100 Subject: [PATCH 04/20] Use lsprotocol's converter for (de)serialization This simplifies much of the (de)serialization code by relying on the converter provided by `lsprotocol`. We use the `METHOD_TO_TYPES` mapping to determine which type definition to use for any given message. If a method is not known (as in the case of custom lsp commands) we fall back to pygls's existing generic RPC message classes. The following changes to the base `JsonRPCProtocol` class have also been made - server and client futures have been unified into a single `_request_futures` dict. - upon sending a request, the corresponding result type is looked up and stored in an internal `_result_types` dict. - (de)serialization code has been moved to a method on the `JsonRPCProtocol` class itself so that it has access to the required internal state. - subclasses (such as the `LanguageServerProtocol` class) are now required to implement the `get_message_type` and `get_result_type` methods to provide the type definitions corresponding with the given RPC method name. --- pygls/exceptions.py | 9 +- pygls/lsp/__init__.py | 26 ---- pygls/protocol.py | 291 +++++++++++++++++++++++------------------- pygls/server.py | 4 +- 4 files changed, 169 insertions(+), 161 deletions(-) diff --git a/pygls/exceptions.py b/pygls/exceptions.py index 0d3245e2..21ad5bd1 100644 --- a/pygls/exceptions.py +++ b/pygls/exceptions.py @@ -40,11 +40,12 @@ def __hash__(self): return hash((self.code, self.message)) @staticmethod - def from_dict(error): + def from_error(error): for exc_class in _EXCEPTIONS: - if exc_class.supports_code(error['code']): - return exc_class(**error) - return JsonRpcException(**error) + if exc_class.supports_code(error.code): + return exc_class(code=error.code, message=error.message, data=error.data) + + return JsonRpcException(code=error.code, message=error.message, data=error.data) @classmethod def supports_code(cls, code): diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index 5a810c7b..0b92ec3b 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -36,32 +36,6 @@ TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], } -@attrs.define -class JsonRPCNotification: - """A class that represents json rpc notification message.""" - - jsonrpc: str - method: str - params: Any - -@attrs.define -class JsonRPCRequestMessage: - """A class that represents json rpc request message.""" - - jsonrpc: str - id: Union[int, str] - method: str - params: Any - - -@attrs.define -class JsonRPCResponseMessage: - """A class that represents json rpc response message.""" - - jsonrpc: str - id: Union[int, str] - result: Union[Any, None] = attrs.field(default=None) - def get_method_registration_options_type( method_name, lsp_methods_map=METHOD_TO_TYPES diff --git a/pygls/protocol.py b/pygls/protocol.py index 294b051e..c45a8e01 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -24,23 +24,24 @@ import uuid from collections import namedtuple from concurrent.futures import Future -from functools import partial +from functools import lru_cache, partial from itertools import zip_longest -from typing import Callable, List, Optional +from typing import Any, Dict, Callable, List, Optional, Type, Union + +import attrs +from cattrs.errors import ClassValidationError +from lsprotocol import converters from pygls.capabilities import ServerCapabilitiesBuilder from pygls.constants import ATTR_FEATURE_TYPE from pygls.exceptions import (JsonRpcException, JsonRpcInternalError, JsonRpcInvalidParams, JsonRpcMethodNotFound, JsonRpcRequestCancelled, - MethodTypeNotRegisteredError, FeatureNotificationError, - FeatureRequestError) -from pygls.feature_manager import (FeatureManager, assign_help_attrs, get_help_attrs, - is_thread_function) -from pygls.lsp import (JsonRPCNotification, JsonRPCRequestMessage, JsonRPCResponseMessage, - get_method_params_type, get_method_return_type, is_instance) -from pygls.lsp.methods import (CANCEL_REQUEST, CLIENT_REGISTER_CAPABILITY, + FeatureNotificationError, FeatureRequestError) +from pygls.feature_manager import (FeatureManager, assign_help_attrs, is_thread_function) +from pygls.lsp import (ConfigCallbackType, ShowDocumentCallbackType) +from lsprotocol.types import (CANCEL_REQUEST, CLIENT_REGISTER_CAPABILITY, CLIENT_UNREGISTER_CAPABILITY, EXIT, INITIALIZE, INITIALIZED, - LOG_TRACE_NOTIFICATION, SET_TRACE_NOTIFICATION, SHUTDOWN, + METHOD_TO_TYPES, LOG_TRACE, SET_TRACE, SHUTDOWN, TEXT_DOCUMENT_DID_CHANGE, TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, WINDOW_LOG_MESSAGE, WINDOW_SHOW_DOCUMENT, WINDOW_SHOW_MESSAGE, @@ -62,7 +63,7 @@ from pygls.workspace import Workspace logger = logging.getLogger(__name__) - +converter = converters.get_converter() def call_user_feature(base_func, method_name): """Wraps generic LSP features and calls user registered feature @@ -86,8 +87,15 @@ def decorator(self, *args, **kwargs): return decorator -def dict_to_object(**d): +def dict_to_object(d: Any): """Create nested objects (namedtuple) from dict.""" + + if d is None: + return None + + if not isinstance(d, dict): + return d + type_name = d.pop('type_name', 'Object') return json.loads( json.dumps(d), @@ -95,73 +103,48 @@ def dict_to_object(**d): ) -def default_serializer(o): - """JSON serializer for complex objects that do not extend pydantic BaseModel class.""" - if isinstance(o, enum.Enum): - return o.value - return o.__dict__ - - -def deserialize_command(params): - """Function used to deserialize command arguments to a specific class - or a namedtuple.""" - # TODO: Register/Look up custom command arguments' types - # Currently command parameters are type of 'any', but eventually we would - # want to register an argument type of our custom command and to - # deserialize it properly. - temp_obj = dict_to_object(**params, type_name='CommandParams') - - params['arguments'] = getattr(temp_obj, 'arguments', None) - return params - +@attrs.define +class JsonRPCNotification: + """A class that represents a generic json rpc notification message. + Used as a fallback for unknown types. + """ -def deserialize_params(data, get_params_type): - """Function used to deserialize params to a specific class.""" - try: - method = data['method'] - params = data['params'] + method: str + jsonrpc: str + params: Any = attrs.field(converter=dict_to_object) - if not isinstance(params, dict): - return data +@attrs.define +class JsonRPCRequestMessage: + """A class that represents a generic json rpc request message. + Used as a fallback for unknown types. + """ - try: - params_type = get_params_type(method) - if params_type is None: - params_type = dict_to_object - elif params_type.__name__ == ExecuteCommandParams.__name__: - params = deserialize_command(params) + id: Union[int, str] + method: str + jsonrpc: str + params: Any = attrs.field(converter=dict_to_object) - except MethodTypeNotRegisteredError: - params_type = dict_to_object - try: - data['params'] = params_type(**params) - except TypeError: - raise ValueError( - f'Could not instantiate "{params_type.__name__}" from params: {params}') - except KeyError: - pass +@attrs.define +class JsonRPCResponseMessage: + """A class that represents a generic json rpc response message. + Used as a fallback for unknown types. + """ - return data + id: Union[int, str] + jsonrpc: str + result: Any = attrs.field(converter=dict_to_object) +def default_serializer(o): + """JSON serializer for complex objects that do not extend pydantic BaseModel class.""" -def deserialize_message(data, get_params_type=get_method_params_type): - """Function used to deserialize data received from client.""" - if 'jsonrpc' in data: - try: - deserialize_params(data, get_params_type) - except ValueError: - raise JsonRpcInvalidParams() + if hasattr(o, '__attrs_attrs__'): + return converter.unstructure(o) - if 'id' in data: - if 'method' in data: - return JsonRPCRequestMessage(**data) - else: - return JsonRPCResponseMessage(**data) - else: - return JsonRPCNotification(**data) + if isinstance(o, enum.Enum): + return o.value - return data + return o.__dict__ class JsonRPCProtocol(asyncio.Protocol): @@ -189,8 +172,9 @@ def __init__(self, server): self._server = server self._shutdown = False - self._client_request_futures = {} - self._server_request_futures = {} + # Book keeping for in-flight requests + self._request_futures = {} + self._result_types = {} self.fm = FeatureManager(server) self.transport = None @@ -201,16 +185,6 @@ def __init__(self, server): def __call__(self): return self - def _check_ret_type_and_send_response(self, method_name, method_type, msg_id, result): - """Check if registered feature returns appropriate result type.""" - if method_type == ATTR_FEATURE_TYPE: - return_type = get_method_return_type(method_name) - if not is_instance(result, return_type): - error = JsonRpcInternalError().to_dict() - self._send_response(msg_id, error=error) - - self._send_response(msg_id, result=result) - def _execute_notification(self, handler, *params): """Executes notification message handler.""" if asyncio.iscoroutinefunction(handler): @@ -238,39 +212,34 @@ def _execute_notification_callback(self, future): def _execute_request(self, msg_id, handler, params): """Executes request message handler.""" - method_name, method_type = get_help_attrs(handler) if asyncio.iscoroutinefunction(handler): future = asyncio.ensure_future(handler(params)) - self._client_request_futures[msg_id] = future - future.add_done_callback(partial(self._execute_request_callback, - method_name, method_type, msg_id)) + self._request_futures[msg_id] = future + future.add_done_callback(partial(self._execute_request_callback, msg_id)) else: # Can't be canceled if is_thread_function(handler): self._server.thread_pool.apply_async( handler, (params, ), callback=partial( - self._check_ret_type_and_send_response, - method_name, method_type, msg_id, + self._send_response, msg_id, ), error_callback=partial(self._execute_request_err_callback, msg_id)) else: - self._check_ret_type_and_send_response( - method_name, method_type, msg_id, handler(params)) + self._send_response(msg_id, handler(params)) - def _execute_request_callback(self, method_name, method_type, msg_id, future): + def _execute_request_callback(self, msg_id, future): """Success callback used for coroutine request message.""" try: if not future.cancelled(): - self._check_ret_type_and_send_response( - method_name, method_type, msg_id, result=future.result()) + self._send_response(msg_id, result=future.result()) else: self._send_response( msg_id, error=JsonRpcRequestCancelled(f'Request with id "{msg_id}" is canceled') ) - self._client_request_futures.pop(msg_id, None) + self._request_futures.pop(msg_id, None) except Exception: error = JsonRpcInternalError.of(sys.exc_info()).to_dict() logger.exception('Exception occurred for message "%s": %s', msg_id, error) @@ -295,7 +264,7 @@ def _get_handler(self, feature_name): def _handle_cancel_notification(self, msg_id): """Handles a cancel notification from the client.""" - future = self._client_request_futures.pop(msg_id, None) + future = self._request_futures.pop(msg_id, None) if not future: logger.warning('Cancel notification for unknown message id "%s"', msg_id) @@ -360,7 +329,7 @@ def _handle_request(self, msg_id, method_name, params): def _handle_response(self, msg_id, result=None, error=None): """Handles a response from the client.""" - future = self._server_request_futures.pop(msg_id, None) + future = self._request_futures.pop(msg_id, None) if not future: logger.warning('Received response to unknown message id "%s"', msg_id) @@ -368,11 +337,47 @@ def _handle_response(self, msg_id, result=None, error=None): if error is not None: logger.debug('Received error response to message "%s": %s', msg_id, error) - future.set_exception(JsonRpcException.from_dict(error)) + future.set_exception(JsonRpcException.from_error(error)) else: logger.debug('Received result for message "%s": %s', msg_id, result) future.set_result(result) + def _deserialize_message(self, data): + """Function used to deserialize data recevied from the client.""" + + if 'jsonrpc' not in data: + return data + + try: + if 'id' in data: + if 'error' in data: + return converter.structure(data, ResponseErrorMessage) + elif 'method' in data: + request_type = ( + self.get_message_type(data['method']) or JsonRPCRequestMessage + ) + return converter.structure(data, request_type) + else: + response_type = ( + self._result_types.pop(data['id']) or JsonRPCResponseMessage + ) + return converter.structure(data, response_type) + + else: + method = data.get('method', '') + notification_type = self.get_message_type(method) or JsonRPCNotification + return converter.structure(data, notification_type) + + except ClassValidationError as exc: + logger.error("Unable to deserialize message\n%s", traceback.format_exc()) + raise JsonRpcInvalidParams() from exc + + except Exception as exc: + logger.error("Unable to deserialize message\n%s", traceback.format_exc()) + raise JsonRpcInternalError() from exc + + return data + def _procedure_handler(self, message): """Delegates message to handlers depending on message type.""" @@ -384,22 +389,27 @@ def _procedure_handler(self, message): logger.warning('Server shutting down. No more requests!') return - if isinstance(message, JsonRPCNotification): - logger.debug('Notification message received.') - self._handle_notification(message.method, message.params) - elif isinstance(message, JsonRPCResponseMessage): - logger.debug('Response message received.') - self._handle_response(message.id, message.result, message.error) - elif isinstance(message, JsonRPCRequestMessage): - logger.debug('Request message received.') - self._handle_request(message.id, message.method, message.params) + if hasattr(message, 'method'): + if hasattr(message, 'id'): + logger.debug('Request message received.') + self._handle_request(message.id, message.method, message.params) + else: + logger.debug('Notification message received.') + self._handle_notification(message.method, message.params) + else: + if hasattr(message, 'error'): + logger.debug('Error message received.') + self._handle_response(message.id, None, message.error) + else: + logger.debug('Response message received.') + self._handle_response(message.id, message.result) def _send_data(self, data): """Sends data to the client.""" if not data: return try: - body = data.json(by_alias=True, exclude_unset=True, encoder=default_serializer) + body = json.dumps(data, default=default_serializer) logger.info('Sending data: %s', body) body = body.encode(self.CHARSET) @@ -424,15 +434,15 @@ def _send_response(self, msg_id, result=None, error=None): result(any): Result returned by handler error(any): Error returned by handler """ - response = JsonRPCResponseMessage(id=msg_id, - jsonrpc=JsonRPCProtocol.VERSION, - result=result, - error=error) - if error is None: - del response.error + if error is not None: + response = ResponseErrorMessage(id=msg_id, error=error) + else: - del response.result + response_type = self._result_types.pop(msg_id, JsonRPCResponseMessage) + response = response_type( + id=msg_id, result=result, jsonrpc=JsonRPCProtocol.VERSION + ) self._send_data(response) @@ -482,19 +492,29 @@ def _data_received(self, data: bytes): # Parse the body self._procedure_handler( json.loads(body.decode(self.CHARSET), - object_hook=deserialize_message)) + object_hook=self._deserialize_message)) + + def get_message_type(self, method: str) -> Optional[Type]: + """Return the type definition of the message associated with the given method.""" + return None + + def get_result_type(self, method: str) -> Optional[Type]: + """Return the type definition of the result associated with the given method.""" + return None def notify(self, method: str, params=None): """Sends a JSON RPC notification to the client.""" - logger.debug('Sending notification: "%s" %s', method, params) - request = JsonRPCNotification( - jsonrpc=JsonRPCProtocol.VERSION, + logger.debug("Sending notification: '%s' %s", method, params) + + notification_type = self.get_message_type(method) or JsonRPCNotification + notification = notification_type( method=method, - params=params + params=params, + jsonrpc=JsonRPCProtocol.VERSION ) - self._send_data(request) + self._send_data(notification) def send_request(self, method, params=None, callback=None): """Sends a JSON RPC request to the client. @@ -506,14 +526,16 @@ def send_request(self, method, params=None, callback=None): Returns: Future that will be resolved once a response has been received """ + msg_id = str(uuid.uuid4()) + request_type = self.get_message_type(method) or JsonRPCRequestMessage logger.debug('Sending request with id "%s": %s %s', msg_id, method, params) - request = JsonRPCRequestMessage( + request = request_type( id=msg_id, - jsonrpc=JsonRPCProtocol.VERSION, method=method, - params=params + params=params, + jsonrpc=JsonRPCProtocol.VERSION, ) future = Future() @@ -525,7 +547,9 @@ def wrapper(future: Future): callback(result) future.add_done_callback(wrapper) - self._server_request_futures[msg_id] = future + self._request_futures[msg_id] = future + self._result_types[msg_id] = self.get_result_type(method) + self._send_data(request) return future @@ -612,7 +636,16 @@ def _register_builtin_features(self): if callable(attr) and hasattr(attr, 'method_name'): self.fm.add_builtin_feature(attr.method_name, attr) - def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> ApplyWorkspaceEditResponse: + @lru_cache() + def get_message_type(self, method: str) -> Optional[Type]: + """Return LSP type definitions, as provided by `lsprotocol`""" + return METHOD_TO_TYPES.get(method, (None,))[0] + + @lru_cache() + def get_result_type(self, method: str) -> Optional[Type]: + return METHOD_TO_TYPES.get(method, (None, None))[1] + + def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" return self.send_request(WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label)) @@ -642,7 +675,10 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult: list(self.fm.commands.keys()), self._server.sync_kind, ).build() - logger.debug('Server capabilities: %s', self.server_capabilities.dict()) + logger.debug( + 'Server capabilities: %s', + json.dumps(self.server_capabilities, default=default_serializer) + ) root_path = params.root_path root_uri = params.root_uri or from_fs_path(root_path) @@ -666,10 +702,7 @@ def lsp_initialized(self, *args) -> None: @lsp_method(SHUTDOWN) def lsp_shutdown(self, *args) -> None: """Request from client which asks server to shutdown.""" - for future in self._client_request_futures.values(): - future.cancel() - - for future in self._server_request_futures.values(): + for future in self._request_futures.values(): future.cancel() self._shutdown = True diff --git a/pygls/server.py b/pygls/server.py index dd8d0d9a..d1fdff36 100644 --- a/pygls/server.py +++ b/pygls/server.py @@ -31,7 +31,7 @@ WorkspaceEdit) from pygls.lsp.types.window import ShowDocumentCallbackType, ShowDocumentParams from pygls.progress import Progress -from pygls.protocol import LanguageServerProtocol, deserialize_message +from pygls.protocol import LanguageServerProtocol from pygls.workspace import Workspace if not IS_PYODIDE: @@ -279,7 +279,7 @@ async def connection_made(websocket, _): self.lsp.transport = WebSocketTransportAdapter(websocket, self.loop) async for message in websocket: self.lsp._procedure_handler( - json.loads(message, object_hook=deserialize_message) + json.loads(message, object_hook=self.lsp._deserialize_message) ) start_server = websockets.serve(connection_made, host, port, loop=self.loop) From 66f81da3948dde952dfbfe05dc17849758e9ee4d Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 16:40:34 +0100 Subject: [PATCH 05/20] Make it possible to disable the testsuite timeout The timeouts can get in the way when trying to debug the code under test. This commit makes it possible to disable the timeout by running the testsuite with the `DISABLE_TIMEOUT` environment variable set e.g. $ DISABLE_TIMEOUT=1 pytest -x tests/ --- tests/_init_server_stall_fix_hack.py | 5 +++++ tests/ls_setup.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/_init_server_stall_fix_hack.py b/tests/_init_server_stall_fix_hack.py index 89045a51..c7fa4513 100644 --- a/tests/_init_server_stall_fix_hack.py +++ b/tests/_init_server_stall_fix_hack.py @@ -4,12 +4,17 @@ fix to actually guarantee it doesn't generate false negatives in the test suite. """ +import os import concurrent RETRIES = 3 def retry_stalled_init_fix_hack(): + + if 'DISABLE_TIMEOUT' in os.environ: + return lambda f: f + def decorator(func): def newfn(*args, **kwargs): attempt = 0 diff --git a/tests/ls_setup.py b/tests/ls_setup.py index 42f61a0f..808f820c 100644 --- a/tests/ls_setup.py +++ b/tests/ls_setup.py @@ -164,6 +164,8 @@ def stop(self): @retry_stalled_init_fix_hack() def initialize(self): + + timeout = None if 'DISABLE_TIMEOUT' in os.environ else 1 response = self.client.lsp.send_request( INITIALIZE, InitializeParams( @@ -171,7 +173,7 @@ def initialize(self): root_uri="file://", capabilities=ClientCapabilities() ), - ).result(timeout=1) + ).result(timeout=timeout) assert "capabilities" in response def __iter__(self): From b1087c21b358e27fb66c3a2caa4d515ec8f8f8e0 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 14 Aug 2022 16:47:31 +0100 Subject: [PATCH 06/20] Align to breaking changes Nothing too interesting in this one, just updating imports, class names etc to align `pygls` to the definitions in `lsprotocol` --- examples/fountain-extension/src/server.py | 6 +- examples/json-extension/server/server.py | 25 ++- .../server/tests/unit/test_features.py | 2 +- pygls/capabilities.py | 161 +++++++++------- pygls/lsp/__init__.py | 16 +- pygls/lsp/types.py | 24 --- pygls/progress.py | 18 +- pygls/protocol.py | 73 ++++---- pygls/server.py | 23 ++- pygls/workspace.py | 49 ++--- tests/ls_setup.py | 20 +- .../test_delta_missing_legend.py | 12 +- .../test_delta_missing_legend_none.py | 4 +- .../test_full_missing_legend.py | 4 +- tests/lsp/semantic_tokens/test_range.py | 6 +- .../test_range_missing_legends.py | 4 +- .../test_semantic_tokens_full.py | 6 +- tests/lsp/test_call_hierarchy.py | 75 ++++---- tests/lsp/test_code_action.py | 22 +-- tests/lsp/test_code_lens.py | 24 +-- tests/lsp/test_color_presentation.py | 50 ++--- tests/lsp/test_completion.py | 44 ++--- tests/lsp/test_declaration.py | 60 +++--- tests/lsp/test_definition.py | 60 +++--- tests/lsp/test_document_color.py | 24 +-- tests/lsp/test_document_highlight.py | 30 +-- tests/lsp/test_document_link.py | 24 +-- tests/lsp/test_document_symbol.py | 80 ++++---- tests/lsp/test_errors.py | 3 +- tests/lsp/test_folding_range.py | 20 +- tests/lsp/test_formatting.py | 20 +- tests/lsp/test_hover.py | 56 +++--- tests/lsp/test_implementation.py | 60 +++--- tests/lsp/test_linked_editing_range.py | 22 +-- tests/lsp/test_moniker.py | 12 +- tests/lsp/test_on_type_formatting.py | 20 +- tests/lsp/test_prepare_rename.py | 37 ++-- tests/lsp/test_progress.py | 22 +-- tests/lsp/test_range_formatting.py | 20 +- tests/lsp/test_references.py | 20 +- tests/lsp/test_rename.py | 86 ++++----- tests/lsp/test_selection_range.py | 34 ++-- tests/lsp/test_signature_help.py | 14 +- tests/lsp/test_type_definition.py | 60 +++--- tests/test_document.py | 30 +-- tests/test_feature_manager.py | 35 ++-- tests/test_language_server.py | 16 +- tests/test_protocol.py | 174 ++++++++++-------- tests/test_types.py | 2 +- tests/test_workspace.py | 2 +- 50 files changed, 886 insertions(+), 825 deletions(-) delete mode 100644 pygls/lsp/types.py diff --git a/examples/fountain-extension/src/server.py b/examples/fountain-extension/src/server.py index 2d1ef0e4..646e02c2 100644 --- a/examples/fountain-extension/src/server.py +++ b/examples/fountain-extension/src/server.py @@ -1,7 +1,7 @@ import re from pygls.server import LanguageServer -from pygls.lsp.methods import COMPLETION -from pygls.lsp.types import (CompletionItem, CompletionParams, CompletionList, CompletionOptions) +from lsprotocol.types import TEXT_DOCUMENT_COMPLETION +from lsprotocol.types import (CompletionItem, CompletionParams, CompletionList, CompletionOptions) # The following imports are required for the glue code in 'server.ts' import json @@ -12,7 +12,7 @@ CHARACTER = re.compile(r"^[A-Z][A-Z ]+$", re.MULTILINE) -@server.feature(COMPLETION, CompletionOptions(trigger_characters=['.'])) +@server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=['.'])) def on_completion(ls: LanguageServer, params: CompletionParams) -> CompletionList: """Completion suggestions for character names.""" diff --git a/examples/json-extension/server/server.py b/examples/json-extension/server/server.py index 3b06e21a..f6ca93fb 100644 --- a/examples/json-extension/server/server.py +++ b/examples/json-extension/server/server.py @@ -22,10 +22,10 @@ from json import JSONDecodeError from typing import Optional -from pygls.lsp.methods import (COMPLETION, TEXT_DOCUMENT_DID_CHANGE, - TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, +from lsprotocol.types import (TEXT_DOCUMENT_COMPLETION, TEXT_DOCUMENT_DID_CHANGE, + TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL) -from pygls.lsp.types import (CompletionItem, CompletionList, CompletionOptions, +from lsprotocol.types import (CompletionItem, CompletionList, CompletionOptions, CompletionParams, ConfigurationItem, ConfigurationParams, Diagnostic, DidChangeTextDocumentParams, @@ -33,10 +33,9 @@ DidOpenTextDocumentParams, MessageType, Position, Range, Registration, RegistrationParams, SemanticTokens, SemanticTokensLegend, SemanticTokensParams, - Unregistration, UnregistrationParams) -from pygls.lsp.types.basic_structures import (WorkDoneProgressBegin, - WorkDoneProgressEnd, - WorkDoneProgressReport) + Unregistration, UnregistrationParams, + WorkDoneProgressBegin, WorkDoneProgressEnd, + WorkDoneProgressReport) from pygls.server import LanguageServer COUNT_DOWN_START_IN_SECONDS = 10 @@ -98,7 +97,7 @@ def _validate_json(source): return diagnostics -@json_server.feature(COMPLETION, CompletionOptions(trigger_characters=[','])) +@json_server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[','])) def completions(params: Optional[CompletionParams] = None) -> CompletionList: """Returns completion items.""" return CompletionList( @@ -164,9 +163,9 @@ async def did_open(ls, params: DidOpenTextDocumentParams): def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams): """See https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens for details on how semantic tokens are encoded.""" - + TOKENS = re.compile('".*"(?=:)') - + uri = params.text_document.uri doc = ls.workspace.get_document(uri) @@ -184,7 +183,7 @@ def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams): (lineno - last_line), (start - last_start), (end - start), - 0, + 0, 0 ] @@ -220,7 +219,7 @@ async def register_completions(ls: JsonLanguageServer, *args): params = RegistrationParams(registrations=[ Registration( id=str(uuid.uuid4()), - method=COMPLETION, + method=TEXT_DOCUMENT_COMPLETION, register_options={"triggerCharacters": "[':']"}) ]) response = await ls.register_capability_async(params) @@ -292,7 +291,7 @@ def show_configuration_thread(ls: JsonLanguageServer, *args): async def unregister_completions(ls: JsonLanguageServer, *args): """Unregister completions method on the client.""" params = UnregistrationParams(unregisterations=[ - Unregistration(id=str(uuid.uuid4()), method=COMPLETION) + Unregistration(id=str(uuid.uuid4()), method=TEXT_DOCUMENT_COMPLETION) ]) response = await ls.unregister_capability_async(params) if response is None: diff --git a/examples/json-extension/server/tests/unit/test_features.py b/examples/json-extension/server/tests/unit/test_features.py index 6453c513..ee0182bf 100644 --- a/examples/json-extension/server/tests/unit/test_features.py +++ b/examples/json-extension/server/tests/unit/test_features.py @@ -19,7 +19,7 @@ import pytest from mock import Mock -from pygls.lsp.types import (DidCloseTextDocumentParams, +from lsprotocol.types import (DidCloseTextDocumentParams, DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem) from pygls.workspace import Document, Workspace diff --git a/pygls/capabilities.py b/pygls/capabilities.py index 8540ee30..030e1ac7 100644 --- a/pygls/capabilities.py +++ b/pygls/capabilities.py @@ -14,30 +14,56 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from pygls.lsp.methods import (CODE_ACTION, CODE_LENS, COMPLETION, DECLARATION, DEFINITION, - DOCUMENT_COLOR, DOCUMENT_HIGHLIGHT, DOCUMENT_LINK, DOCUMENT_SYMBOL, - FOLDING_RANGE, FORMATTING, HOVER, IMPLEMENTATION, - ON_TYPE_FORMATTING, RANGE_FORMATTING, REFERENCES, RENAME, - SELECTION_RANGE, SIGNATURE_HELP, - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE, TEXT_DOCUMENT_DID_CLOSE, - TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_DID_SAVE, - TEXT_DOCUMENT_LINKED_EDITING_RANGE, TEXT_DOCUMENT_MONIKER, - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, - TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, TEXT_DOCUMENT_WILL_SAVE, - TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, TYPE_DEFINITION, - WORKSPACE_DID_CREATE_FILES, WORKSPACE_DID_DELETE_FILES, - WORKSPACE_DID_RENAME_FILES, WORKSPACE_SYMBOL, - WORKSPACE_WILL_CREATE_FILES, WORKSPACE_WILL_DELETE_FILES, - WORKSPACE_WILL_RENAME_FILES) -from pygls.lsp.types import (CodeLensOptions, CompletionOptions, DocumentLinkOptions, - ExecuteCommandOptions, ImplementationOptions, SaveOptions, - SemanticTokensOptions, SemanticTokensRegistrationOptions, - SemanticTokensRequestsFull, - ServerCapabilities, SignatureHelpOptions, - TextDocumentSyncOptionsServerCapabilities, TypeDefinitionOptions, - WorkspaceFileOperationsServerCapabilities, - WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities) +from functools import reduce +from typing import Any + +from lsprotocol.types import ( + TEXT_DOCUMENT_CODE_ACTION, TEXT_DOCUMENT_CODE_LENS, + TEXT_DOCUMENT_COMPLETION, TEXT_DOCUMENT_DECLARATION, + TEXT_DOCUMENT_DEFINITION, TEXT_DOCUMENT_DOCUMENT_COLOR, + TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, TEXT_DOCUMENT_DOCUMENT_LINK, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, TEXT_DOCUMENT_FOLDING_RANGE, + TEXT_DOCUMENT_FORMATTING, TEXT_DOCUMENT_HOVER, + TEXT_DOCUMENT_IMPLEMENTATION, TEXT_DOCUMENT_ON_TYPE_FORMATTING, + TEXT_DOCUMENT_RANGE_FORMATTING, TEXT_DOCUMENT_REFERENCES, + TEXT_DOCUMENT_RENAME, TEXT_DOCUMENT_SELECTION_RANGE, + TEXT_DOCUMENT_SIGNATURE_HELP, TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, + TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN, + TEXT_DOCUMENT_DID_SAVE, TEXT_DOCUMENT_LINKED_EDITING_RANGE, + TEXT_DOCUMENT_MONIKER, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, + TEXT_DOCUMENT_WILL_SAVE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, + TEXT_DOCUMENT_TYPE_DEFINITION, WORKSPACE_DID_CREATE_FILES, + WORKSPACE_DID_DELETE_FILES, WORKSPACE_DID_RENAME_FILES, + WORKSPACE_SYMBOL, WORKSPACE_WILL_CREATE_FILES, + WORKSPACE_WILL_DELETE_FILES, WORKSPACE_WILL_RENAME_FILES +) +from lsprotocol.types import ( + ClientCapabilities, CodeLensOptions, CompletionOptions, + DocumentLinkOptions, ExecuteCommandOptions, ImplementationOptions, + SaveOptions, SemanticTokensOptions, SemanticTokensRegistrationOptions, + SemanticTokensOptionsFullType1, ServerCapabilities, + ServerCapabilitiesWorkspaceType, SignatureHelpOptions, + TextDocumentSyncOptions, TypeDefinitionOptions, + FileOperationOptions, WorkspaceFoldersServerCapabilities +) + + +def get_capability( + client_capabilities: ClientCapabilities, field: str, default: Any = None +) -> Any: + """Check if ClientCapabilities has some nested value without raising + AttributeError. + e.g. get_capability('text_document.synchronization.will_save') + """ + try: + value = reduce(getattr, field.split("."), client_capabilities) + except AttributeError: + return default + + # If we reach the desired leaf value but it's None, return the default. + value = default if value is None else value + return class ServerCapabilitiesBuilder: @@ -71,13 +97,17 @@ def _with_text_doc_sync(self): or TEXT_DOCUMENT_DID_CLOSE in self.features ) will_save = ( - self.client_capabilities.get_capability( - 'text_document.synchronization.will_save') + get_capability( + self.client_capabilities, + 'text_document.synchronization.will_save' + ) and TEXT_DOCUMENT_WILL_SAVE in self.features ) will_save_wait_until = ( - self.client_capabilities.get_capability( - 'text_document.synchronization.will_save_wait_until') + get_capability( + self.client_capabilities, + 'text_document.synchronization.will_save_wait_until' + ) and TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL in self.features ) if TEXT_DOCUMENT_DID_SAVE in self.features: @@ -89,7 +119,7 @@ def _with_text_doc_sync(self): else: save = False - self.server_cap.text_document_sync = TextDocumentSyncOptionsServerCapabilities( + self.server_cap.text_document_sync = TextDocumentSyncOptions( open_close=open_close, change=self.sync_kind, will_save=will_save, @@ -100,115 +130,121 @@ def _with_text_doc_sync(self): return self def _with_completion(self): - value = self._provider_options(COMPLETION, default=CompletionOptions()) + value = self._provider_options(TEXT_DOCUMENT_COMPLETION, default=CompletionOptions()) if value is not None: self.server_cap.completion_provider = value return self def _with_hover(self): - value = self._provider_options(HOVER) + value = self._provider_options(TEXT_DOCUMENT_HOVER) if value is not None: self.server_cap.hover_provider = value return self def _with_signature_help(self): - value = self._provider_options(SIGNATURE_HELP, default=SignatureHelpOptions()) + value = self._provider_options( + TEXT_DOCUMENT_SIGNATURE_HELP, default=SignatureHelpOptions() + ) if value is not None: self.server_cap.signature_help_provider = value return self def _with_declaration(self): - value = self._provider_options(DECLARATION) + value = self._provider_options(TEXT_DOCUMENT_DECLARATION) if value is not None: self.server_cap.declaration_provider = value return self def _with_definition(self): - value = self._provider_options(DEFINITION) + value = self._provider_options(TEXT_DOCUMENT_DEFINITION) if value is not None: self.server_cap.definition_provider = value return self def _with_type_definition(self): - value = self._provider_options(TYPE_DEFINITION, default=TypeDefinitionOptions()) + value = self._provider_options( + TEXT_DOCUMENT_TYPE_DEFINITION, default=TypeDefinitionOptions() + ) if value is not None: self.server_cap.type_definition_provider = value return self def _with_implementation(self): - value = self._provider_options(IMPLEMENTATION, default=ImplementationOptions()) + value = self._provider_options( + TEXT_DOCUMENT_IMPLEMENTATION, default=ImplementationOptions() + ) if value is not None: self.server_cap.implementation_provider = value return self def _with_references(self): - value = self._provider_options(REFERENCES) + value = self._provider_options(TEXT_DOCUMENT_REFERENCES) if value is not None: self.server_cap.references_provider = value return self def _with_document_highlight(self): - value = self._provider_options(DOCUMENT_HIGHLIGHT) + value = self._provider_options(TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT) if value is not None: self.server_cap.document_highlight_provider = value return self def _with_document_symbol(self): - value = self._provider_options(DOCUMENT_SYMBOL) + value = self._provider_options(TEXT_DOCUMENT_DOCUMENT_SYMBOL) if value is not None: self.server_cap.document_symbol_provider = value return self def _with_code_action(self): - value = self._provider_options(CODE_ACTION) + value = self._provider_options(TEXT_DOCUMENT_CODE_ACTION) if value is not None: self.server_cap.code_action_provider = value return self def _with_code_lens(self): - value = self._provider_options(CODE_LENS, default=CodeLensOptions()) + value = self._provider_options(TEXT_DOCUMENT_CODE_LENS, default=CodeLensOptions()) if value is not None: self.server_cap.code_lens_provider = value return self def _with_document_link(self): - value = self._provider_options(DOCUMENT_LINK, default=DocumentLinkOptions()) + value = self._provider_options(TEXT_DOCUMENT_DOCUMENT_LINK, default=DocumentLinkOptions()) if value is not None: self.server_cap.document_link_provider = value return self def _with_color(self): - value = self._provider_options(DOCUMENT_COLOR) + value = self._provider_options(TEXT_DOCUMENT_DOCUMENT_COLOR) if value is not None: self.server_cap.color_provider = value return self def _with_document_formatting(self): - value = self._provider_options(FORMATTING) + value = self._provider_options(TEXT_DOCUMENT_FORMATTING) if value is not None: self.server_cap.document_formatting_provider = value return self def _with_document_range_formatting(self): - value = self._provider_options(RANGE_FORMATTING) + value = self._provider_options(TEXT_DOCUMENT_RANGE_FORMATTING) if value is not None: self.server_cap.document_range_formatting_provider = value return self def _with_document_on_type_formatting(self): - value = self._provider_options(ON_TYPE_FORMATTING) + value = self._provider_options(TEXT_DOCUMENT_ON_TYPE_FORMATTING) if value is not None: self.server_cap.document_on_type_formatting_provider = value return self def _with_rename(self): - value = self._provider_options(RENAME) + value = self._provider_options(TEXT_DOCUMENT_RENAME) if value is not None: self.server_cap.rename_provider = value return self def _with_folding_range(self): - value = self._provider_options(FOLDING_RANGE) + value = self._provider_options(TEXT_DOCUMENT_FOLDING_RANGE) if value is not None: self.server_cap.folding_range_provider = value return self @@ -219,13 +255,13 @@ def _with_execute_command(self): return self def _with_selection_range(self): - value = self._provider_options(SELECTION_RANGE) + value = self._provider_options(TEXT_DOCUMENT_SELECTION_RANGE) if value is not None: self.server_cap.selection_range_provider = value return self def _with_call_hierarchy(self): - value = self._provider_options(TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE) + value = self._provider_options(TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY) if value is not None: self.server_cap.call_hierarchy_provider = value return self @@ -250,11 +286,10 @@ def _with_semantic_tokens(self): self.server_cap.semantic_tokens_provider = value return self - full_support = ( - SemanticTokensRequestsFull(delta=True) - if TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA in self.features - else TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL in self.features - ) + if TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA in self.features: + full_support = SemanticTokensOptionsFullType1(delta=True) + else: + full_support = TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL in self.features options = SemanticTokensOptions( legend=value, @@ -287,10 +322,10 @@ def _with_workspace_symbol(self): def _with_workspace_capabilities(self): # File operations - file_operations = WorkspaceFileOperationsServerCapabilities() + file_operations = FileOperationOptions() will_create = ( - self.client_capabilities.get_capability('workspace.fileOperations.willCreate') + get_capability(self.client_capabilities, 'workspace.fileOperations.willCreate') if WORKSPACE_WILL_CREATE_FILES in self.features else None ) @@ -298,7 +333,7 @@ def _with_workspace_capabilities(self): file_operations.will_create = will_create did_create = ( - self.client_capabilities.get_capability('workspace.fileOperations.didCreate') + get_capability(self.client_capabilities, 'workspace.fileOperations.didCreate') if WORKSPACE_DID_CREATE_FILES in self.features else None ) @@ -306,7 +341,7 @@ def _with_workspace_capabilities(self): file_operations.did_create = did_create will_rename = ( - self.client_capabilities.get_capability('workspace.fileOperations.willRename') + get_capability(self.client_capabilities, 'workspace.fileOperations.willRename') if WORKSPACE_WILL_RENAME_FILES in self.features else None ) @@ -314,7 +349,7 @@ def _with_workspace_capabilities(self): file_operations.will_rename = will_rename did_rename = ( - self.client_capabilities.get_capability('workspace.fileOperations.didRename') + get_capability(self.client_capabilities, 'workspace.fileOperations.didRename') if WORKSPACE_DID_RENAME_FILES in self.features else None ) @@ -322,7 +357,7 @@ def _with_workspace_capabilities(self): file_operations.did_rename = did_rename will_delete = ( - self.client_capabilities.get_capability('workspace.fileOperations.willDelete') + get_capability(self.client_capabilities, 'workspace.fileOperations.willDelete') if WORKSPACE_WILL_DELETE_FILES in self.features else None ) @@ -330,14 +365,14 @@ def _with_workspace_capabilities(self): file_operations.will_delete = will_delete did_delete = ( - self.client_capabilities.get_capability('workspace.fileOperations.didDelete') + get_capability(self.client_capabilities, 'workspace.fileOperations.didDelete') if WORKSPACE_DID_DELETE_FILES in self.features else None ) if did_delete is not None: file_operations.did_delete = did_delete - self.server_cap.workspace = WorkspaceServerCapabilities( + self.server_cap.workspace = ServerCapabilitiesWorkspaceType( workspace_folders=WorkspaceFoldersServerCapabilities( supported=True, change_notifications=True, diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index 0b92ec3b..11ea0b12 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -14,9 +14,8 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from typing import Any, Optional, Union +from typing import Any, Callable, List, Optional, Union -import attrs from lsprotocol.types import ( ALL_TYPES_MAP, METHOD_TO_TYPES, @@ -24,16 +23,20 @@ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensLegend, - SemanticTokensRegistrationOptions + SemanticTokensRegistrationOptions, + ShowDocumentResult ) from typeguard import check_type from pygls.exceptions import MethodTypeNotRegisteredError +ConfigCallbackType = Callable[[List[Any]], None] +ShowDocumentCallbackType = Callable[[ShowDocumentResult], None] + METHOD_TO_OPTIONS = { - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], - TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], } @@ -89,6 +92,7 @@ def get_method_options_type( return options_type + def get_method_params_type(method_name, lsp_methods_map=METHOD_TO_TYPES): try: return lsp_methods_map[method_name][2] diff --git a/pygls/lsp/types.py b/pygls/lsp/types.py deleted file mode 100644 index f36b6e37..00000000 --- a/pygls/lsp/types.py +++ /dev/null @@ -1,24 +0,0 @@ -############################################################################ -# Copyright(c) Open Law Library. All rights reserved. # -# See ThirdPartyNotices.txt in the project root for additional notices. # -# # -# Licensed under the Apache License, Version 2.0 (the "License") # -# you may not use this file except in compliance with the License. # -# You may obtain a copy of the License at # -# # -# http: // www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -############################################################################ -# flake8: noqa -from typing import Any, Callable, List - -from lsprotocol.types import * - - -ConfigCallbackType = Callable[[List[Any]], None] -ShowDocumentCallbackType = Callable[[ShowDocumentResult], None] diff --git a/pygls/progress.py b/pygls/progress.py index 3cbd5d15..34e55619 100644 --- a/pygls/progress.py +++ b/pygls/progress.py @@ -2,11 +2,13 @@ from concurrent.futures import Future from typing import Dict -from pygls.lsp.methods import (PROGRESS_NOTIFICATION, WINDOW_WORK_DONE_PROGRESS_CANCEL, - WINDOW_WORK_DONE_PROGRESS_CREATE) -from pygls.lsp.types.basic_structures import (ProgressParams, ProgressToken, WorkDoneProgressBegin, - WorkDoneProgressEnd, WorkDoneProgressReport) -from pygls.lsp.types.window import WorkDoneProgressCancelParams, WorkDoneProgressCreateParams +from lsprotocol.types import ( + PROGRESS, WINDOW_WORK_DONE_PROGRESS_CANCEL, + WINDOW_WORK_DONE_PROGRESS_CREATE, + ProgressParams, ProgressToken, WorkDoneProgressBegin, + WorkDoneProgressEnd, WorkDoneProgressReport, + WorkDoneProgressCancelParams, WorkDoneProgressCreateParams +) from pygls.protocol import LanguageServerProtocol @@ -70,7 +72,7 @@ def cancel_async(self, token: ProgressToken) -> asyncio.Future: def begin(self, token: ProgressToken, value: WorkDoneProgressBegin) -> None: return self._lsp.notify( - PROGRESS_NOTIFICATION, + PROGRESS, ProgressParams( token=token, value=value @@ -78,7 +80,7 @@ def begin(self, token: ProgressToken, value: WorkDoneProgressBegin) -> None: ) def report(self, token: ProgressToken, value: WorkDoneProgressReport) -> None: - self._lsp.notify(PROGRESS_NOTIFICATION, ProgressParams(token=token, value=value)) + self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) def end(self, token: ProgressToken, value: WorkDoneProgressEnd) -> None: - self._lsp.notify(PROGRESS_NOTIFICATION, ProgressParams(token=token, value=value)) + self._lsp.notify(PROGRESS, ProgressParams(token=token, value=value)) diff --git a/pygls/protocol.py b/pygls/protocol.py index c45a8e01..9715d7dc 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -22,11 +22,12 @@ import re import sys import uuid +import traceback from collections import namedtuple from concurrent.futures import Future from functools import lru_cache, partial from itertools import zip_longest -from typing import Any, Dict, Callable, List, Optional, Type, Union +from typing import Any, Callable, List, Optional, Type, Union import attrs from cattrs.errors import ClassValidationError @@ -34,37 +35,43 @@ from pygls.capabilities import ServerCapabilitiesBuilder from pygls.constants import ATTR_FEATURE_TYPE -from pygls.exceptions import (JsonRpcException, JsonRpcInternalError, JsonRpcInvalidParams, - JsonRpcMethodNotFound, JsonRpcRequestCancelled, - FeatureNotificationError, FeatureRequestError) -from pygls.feature_manager import (FeatureManager, assign_help_attrs, is_thread_function) -from pygls.lsp import (ConfigCallbackType, ShowDocumentCallbackType) -from lsprotocol.types import (CANCEL_REQUEST, CLIENT_REGISTER_CAPABILITY, - CLIENT_UNREGISTER_CAPABILITY, EXIT, INITIALIZE, INITIALIZED, - METHOD_TO_TYPES, LOG_TRACE, SET_TRACE, SHUTDOWN, - TEXT_DOCUMENT_DID_CHANGE, TEXT_DOCUMENT_DID_CLOSE, - TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, - WINDOW_LOG_MESSAGE, WINDOW_SHOW_DOCUMENT, WINDOW_SHOW_MESSAGE, - WORKSPACE_APPLY_EDIT, WORKSPACE_CONFIGURATION, - WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS, WORKSPACE_EXECUTE_COMMAND, - WORKSPACE_SEMANTIC_TOKENS_REFRESH) -from pygls.lsp.types import (ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse, Diagnostic, - DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, - DidCloseTextDocumentParams, DidOpenTextDocumentParams, - ExecuteCommandParams, InitializeParams, InitializeResult, - LogMessageParams, MessageType, PublishDiagnosticsParams, - RegistrationParams, ServerInfo, ShowMessageParams, - UnregistrationParams, WorkspaceEdit) -from pygls.lsp.types.basic_structures import (ConfigCallbackType, LogTraceParams, SetTraceParams, - Trace) -from pygls.lsp.types.window import ShowDocumentCallbackType, ShowDocumentParams -from pygls.lsp.types.workspace import ConfigurationParams +from pygls.exceptions import ( + JsonRpcException, JsonRpcInternalError, JsonRpcInvalidParams, + JsonRpcMethodNotFound, JsonRpcRequestCancelled, + FeatureNotificationError, FeatureRequestError +) +from pygls.feature_manager import FeatureManager, assign_help_attrs, is_thread_function +from pygls.lsp import ConfigCallbackType, ShowDocumentCallbackType +from lsprotocol.types import ( + CANCEL_REQUEST, CLIENT_REGISTER_CAPABILITY, + CLIENT_UNREGISTER_CAPABILITY, EXIT, INITIALIZE, INITIALIZED, + METHOD_TO_TYPES, LOG_TRACE, SET_TRACE, SHUTDOWN, + TEXT_DOCUMENT_DID_CHANGE, TEXT_DOCUMENT_DID_CLOSE, + TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, + WINDOW_LOG_MESSAGE, WINDOW_SHOW_DOCUMENT, WINDOW_SHOW_MESSAGE, + WORKSPACE_APPLY_EDIT, WORKSPACE_CONFIGURATION, + WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS, WORKSPACE_EXECUTE_COMMAND, + WORKSPACE_SEMANTIC_TOKENS_REFRESH +) +from lsprotocol.types import ( + ApplyWorkspaceEditParams, + ConfigurationParams, Diagnostic, + DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, + DidCloseTextDocumentParams, DidOpenTextDocumentParams, + ExecuteCommandParams, InitializeParams, InitializeResult, + LogMessageParams, LogTraceParams, MessageType, PublishDiagnosticsParams, + RegistrationParams, ResponseErrorMessage, SetTraceParams, + ShowDocumentParams, ShowMessageParams, + TraceValues, UnregistrationParams, WorkspaceApplyEditResponse, + WorkspaceEdit, InitializeResultServerInfoType +) from pygls.uris import from_fs_path from pygls.workspace import Workspace logger = logging.getLogger(__name__) converter = converters.get_converter() + def call_user_feature(base_func, method_name): """Wraps generic LSP features and calls user registered feature immediately after it. @@ -113,6 +120,7 @@ class JsonRPCNotification: jsonrpc: str params: Any = attrs.field(converter=dict_to_object) + @attrs.define class JsonRPCRequestMessage: """A class that represents a generic json rpc request message. @@ -135,6 +143,7 @@ class JsonRPCResponseMessage: jsonrpc: str result: Any = attrs.field(converter=dict_to_object) + def default_serializer(o): """JSON serializer for complex objects that do not extend pydantic BaseModel class.""" @@ -622,7 +631,7 @@ def __init__(self, server): "This will be mandatory: " "https://github.com/openlawlibrary/pygls/pull/276") else: - self.server_info = ServerInfo( + self.server_info = InitializeResultServerInfoType( name=server.name, version=server.version, ) @@ -687,7 +696,7 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult: workspace_folders = params.workspace_folders or [] self.workspace = Workspace(root_uri, self._server.sync_kind, workspace_folders) - self.trace = Trace.Off + self.trace = TraceValues.Off return InitializeResult( capabilities=self.server_capabilities, @@ -726,7 +735,7 @@ def lsp_text_document__did_open(self, params: DidOpenTextDocumentParams) -> None """Puts document to the workspace.""" self.workspace.put_document(params.text_document) - @lsp_method(SET_TRACE_NOTIFICATION) + @lsp_method(SET_TRACE) def lsp_set_trace(self, params: SetTraceParams) -> None: """Changes server trace value.""" self.trace = params.value @@ -778,14 +787,14 @@ def get_configuration_async(self, params: ConfigurationParams) -> asyncio.Future def log_trace(self, message: str, verbose: Optional[str] = None) -> None: """Sends trace notification to the client.""" - if self.trace == Trace.Off: + if self.trace == TraceValues.Off: return params = LogTraceParams(message=message) - if verbose and self.trace == Trace.Verbose: + if verbose and self.trace == TraceValues.VERBOSE: params.verbose = verbose - self.notify(LOG_TRACE_NOTIFICATION, params) + self.notify(LOG_TRACE, params) def publish_diagnostics(self, doc_uri: str, diagnostics: List[Diagnostic]) -> None: """Sends diagnostic notification to the client.""" diff --git a/pygls/server.py b/pygls/server.py index d1fdff36..4d62c0e1 100644 --- a/pygls/server.py +++ b/pygls/server.py @@ -24,12 +24,15 @@ from typing import Any, Callable, List, Optional, TypeVar, Union from pygls import IS_WIN, IS_PYODIDE +from pygls.lsp import ConfigCallbackType, ShowDocumentCallbackType from pygls.exceptions import PyglsError, JsonRpcException, FeatureRequestError -from pygls.lsp.types import (ApplyWorkspaceEditResponse, ClientCapabilities, ConfigCallbackType, - ConfigurationParams, Diagnostic, MessageType, RegistrationParams, - ServerCapabilities, TextDocumentSyncKind, UnregistrationParams, - WorkspaceEdit) -from pygls.lsp.types.window import ShowDocumentCallbackType, ShowDocumentParams +from lsprotocol.types import ( + ClientCapabilities, + ConfigurationParams, Diagnostic, MessageType, RegistrationParams, + ServerCapabilities, ShowDocumentParams, + TextDocumentSyncKind, UnregistrationParams, + WorkspaceApplyEditResponse, WorkspaceEdit +) from pygls.progress import Progress from pygls.protocol import LanguageServerProtocol from pygls.workspace import Workspace @@ -151,9 +154,9 @@ class Server: `ThreadPoolExecutor` sync_kind(TextDocumentSyncKind): Text document synchronization option - - NONE(0): no synchronization - - FULL(1): replace whole text - - INCREMENTAL(2): replace text within a given range + - None(0): no synchronization + - Full(1): replace whole text + - Incremental(2): replace text within a given range Attributes: _max_workers(int): Number of workers for thread pool executor @@ -167,7 +170,7 @@ class Server: """ def __init__(self, protocol_cls, loop=None, max_workers=2, - sync_kind=TextDocumentSyncKind.INCREMENTAL): + sync_kind=TextDocumentSyncKind.Incremental): if not issubclass(protocol_cls, asyncio.Protocol): raise TypeError('Protocol class should be subclass of asyncio.Protocol') @@ -342,7 +345,7 @@ def __init__(self, name: str = None, version: str = None, loop=None, self.version = version super().__init__(protocol_cls, loop, max_workers) - def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> ApplyWorkspaceEditResponse: + def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" return self.lsp.apply_edit(edit, label) diff --git a/pygls/workspace.py b/pygls/workspace.py index 8e73625f..dda2a0b0 100644 --- a/pygls/workspace.py +++ b/pygls/workspace.py @@ -22,9 +22,11 @@ import re from typing import List, Optional, Pattern -from pygls.lsp.types import (NumType, Position, Range, TextDocumentContentChangeEvent, - TextDocumentItem, TextDocumentSyncKind, - VersionedTextDocumentIdentifier, WorkspaceFolder) +from lsprotocol.types import ( + Position, Range, TextDocumentContentChangeEvent, + TextDocumentItem, TextDocumentSyncKind, + VersionedTextDocumentIdentifier, WorkspaceFolder +) from pygls.uris import to_fs_path, uri_scheme # TODO: this is not the best e.g. we capture numbers @@ -164,10 +166,10 @@ def __init__( self, uri: str, source: Optional[str] = None, - version: Optional[NumType] = None, + version: Optional[int] = None, language_id: Optional[str] = None, local: bool = True, - sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.INCREMENTAL + sync_kind: TextDocumentSyncKind = TextDocumentSyncKind.Incremental ): self.uri = uri self.version = version @@ -178,21 +180,24 @@ def __init__( self._local = local self._source = source - self._is_sync_kind_full = sync_kind == TextDocumentSyncKind.FULL - self._is_sync_kind_incremental = sync_kind == TextDocumentSyncKind.INCREMENTAL - self._is_sync_kind_none = sync_kind == TextDocumentSyncKind.NONE + self._is_sync_kind_full = sync_kind == TextDocumentSyncKind.Full + self._is_sync_kind_incremental = sync_kind == TextDocumentSyncKind.Incremental + self._is_sync_kind_none = sync_kind == TextDocumentSyncKind.None_ def __str__(self): return str(self.uri) def _apply_incremental_change(self, change: TextDocumentContentChangeEvent) -> None: - """Apply an INCREMENTAL text change to the document""" + """Apply an ``Incremental`` text change to the document""" lines = self.lines text = change.text change_range = change.range - (start_line, start_col), (end_line, end_col) = \ - range_from_utf16(lines, change_range) # type: ignore + range = range_from_utf16(lines, change_range) # type: ignore + start_line = range.start.line + start_col = range.start.character + end_line = range.end.line + end_col = range.end.character # Check for an edit occurring at the very end of the file if start_line == len(lines): @@ -223,11 +228,11 @@ def _apply_incremental_change(self, change: TextDocumentContentChangeEvent) -> N self._source = new.getvalue() def _apply_full_change(self, change: TextDocumentContentChangeEvent) -> None: - """Apply a FULL text change to the document.""" + """Apply a ``Full`` text change to the document.""" self._source = change.text def _apply_none_change(self, change: TextDocumentContentChangeEvent) -> None: - """Apply a NONE text change to the document + """Apply a ``None`` text change to the document Currently does nothing, provided for consistency. """ @@ -236,14 +241,14 @@ def _apply_none_change(self, change: TextDocumentContentChangeEvent) -> None: def apply_change(self, change: TextDocumentContentChangeEvent) -> None: """Apply a text change to a document, considering TextDocumentSyncKind - Performs either INCREMENTAL, FULL, or NONE synchronization based on + Performs either ``Incremental``, ``Full``, or ``None`` synchronization based on both the Client request and server capabilities. - INCREMENTAL versus FULL synchronization: - Even if a server accepts INCREMENTAL SyncKinds, clients may request - a FULL SyncKind. In LSP 3.x, clients make this request by omitting + ``Incremental`` versus ``Full`` synchronization: + Even if a server accepts ``Incremantal`` SyncKinds, clients may request + a ``Full`` SyncKind. In LSP 3.x, clients make this request by omitting both Range and RangeLength from their request. Consequently, the - attributes "range" and "rangeLength" will be missing from FULL + attributes "range" and "rangeLength" will be missing from ``Full`` content update client requests in the pygls Python library. NOTE: After adding pydantic models, "range" and "rangeLength" fileds @@ -274,7 +279,8 @@ def lines(self) -> List[str]: def offset_at_position(self, position: Position) -> int: """Return the character offset pointed at by the given position.""" lines = self.lines - row, col = position_from_utf16(lines, position) + pos = position_from_utf16(lines, position) + row, col = pos.line, pos.character return col + sum(len(line) for line in lines[:row]) @property @@ -313,7 +319,8 @@ def word_at_position( if position.line >= len(lines): return '' - row, col = position_from_utf16(lines, position) + pos = position_from_utf16(lines, position) + row, col = pos.line, pos.character line = lines[row] # Split word in two start = line[:col] @@ -345,7 +352,7 @@ def _create_document( self, doc_uri: str, source: Optional[str] = None, - version: Optional[NumType] = None, + version: Optional[int] = None, language_id: Optional[str] = None, ) -> Document: return Document( diff --git a/tests/ls_setup.py b/tests/ls_setup.py index 808f820c..e556eeb7 100644 --- a/tests/ls_setup.py +++ b/tests/ls_setup.py @@ -20,10 +20,13 @@ import threading import pytest -from pygls.lsp.methods import EXIT, INITIALIZE, SHUTDOWN -from pygls.lsp.types import ClientCapabilities, InitializeParams -from pygls.protocol import deserialize_message - +from lsprotocol.types import ( + EXIT, + INITIALIZE, + SHUTDOWN, + ClientCapabilities, + InitializeParams, +) from pygls.server import LanguageServer @@ -60,8 +63,9 @@ def close(self): ... def write(self, data): + object_hook = self.dest.lsp._deserialize_message self.dest.lsp._procedure_handler( - json.loads(data, object_hook=deserialize_message) + json.loads(data, object_hook=object_hook) ) @@ -104,7 +108,7 @@ def initialize(self): ) ).result(timeout=CALL_TIMEOUT) - assert 'capabilities' in response + assert response.capabilities is not None def __iter__(self): yield self.client @@ -121,6 +125,7 @@ def __init__(self, LS=LanguageServer): # Setup Server self.server = LS('server', 'v1') self.server_thread = threading.Thread( + name='Server Thread', target=self.server.start_io, args=(os.fdopen(csr, "rb"), os.fdopen(scw, "wb")), ) @@ -129,6 +134,7 @@ def __init__(self, LS=LanguageServer): # Setup client self.client = LS('client', 'v1', asyncio.new_event_loop()) self.client_thread = threading.Thread( + name='Client Thread', target=self.client.start_io, args=(os.fdopen(scr, "rb"), os.fdopen(csw, "wb")), ) @@ -174,7 +180,7 @@ def initialize(self): capabilities=ClientCapabilities() ), ).result(timeout=timeout) - assert "capabilities" in response + assert response.capabilities is not None def __iter__(self): yield self.client diff --git a/tests/lsp/semantic_tokens/test_delta_missing_legend.py b/tests/lsp/semantic_tokens/test_delta_missing_legend.py index daf49aec..b7a5c010 100644 --- a/tests/lsp/semantic_tokens/test_delta_missing_legend.py +++ b/tests/lsp/semantic_tokens/test_delta_missing_legend.py @@ -16,15 +16,15 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( SemanticTokens, SemanticTokensDeltaParams, SemanticTokensLegend, SemanticTokensPartialResult, - SemanticTokensRequestsFull, + SemanticTokensOptionsFullType1, TextDocumentIdentifier, ) @@ -55,9 +55,7 @@ def test_capabilities(client_server): capabilities = server.server_capabilities provider = capabilities.semantic_tokens_provider - assert provider.full == SemanticTokensRequestsFull( - delta=True - ) + assert provider.full == SemanticTokensOptionsFullType1(delta=True) assert provider.legend.token_types == [ "keyword", "operator", @@ -81,7 +79,7 @@ def test_semantic_tokens_full_delta_return_tokens(client_server): assert response - assert response["data"] == [0, 0, 3, 0, 0] + assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() diff --git a/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py b/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py index 71c759fb..6f4fa17d 100644 --- a/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py +++ b/tests/lsp/semantic_tokens/test_delta_missing_legend_none.py @@ -16,12 +16,12 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.types import ( +from lsprotocol.types import ( SemanticTokens, SemanticTokensDeltaParams, SemanticTokensPartialResult, ) -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, ) diff --git a/tests/lsp/semantic_tokens/test_full_missing_legend.py b/tests/lsp/semantic_tokens/test_full_missing_legend.py index e2e1f747..76c00e4b 100644 --- a/tests/lsp/semantic_tokens/test_full_missing_legend.py +++ b/tests/lsp/semantic_tokens/test_full_missing_legend.py @@ -16,10 +16,10 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( SemanticTokens, SemanticTokensPartialResult, SemanticTokensParams diff --git a/tests/lsp/semantic_tokens/test_range.py b/tests/lsp/semantic_tokens/test_range.py index d990a947..8099e92e 100644 --- a/tests/lsp/semantic_tokens/test_range.py +++ b/tests/lsp/semantic_tokens/test_range.py @@ -16,10 +16,10 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( Position, Range, SemanticTokens, @@ -90,7 +90,7 @@ def test_semantic_tokens_range_return_tokens(client_server): assert response - assert response["data"] == [0, 0, 3, 0, 0] + assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() diff --git a/tests/lsp/semantic_tokens/test_range_missing_legends.py b/tests/lsp/semantic_tokens/test_range_missing_legends.py index 1df1e50b..69780efa 100644 --- a/tests/lsp/semantic_tokens/test_range_missing_legends.py +++ b/tests/lsp/semantic_tokens/test_range_missing_legends.py @@ -16,10 +16,10 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( SemanticTokens, SemanticTokensParams, SemanticTokensPartialResult, diff --git a/tests/lsp/semantic_tokens/test_semantic_tokens_full.py b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py index 0341d284..41c3c85c 100644 --- a/tests/lsp/semantic_tokens/test_semantic_tokens_full.py +++ b/tests/lsp/semantic_tokens/test_semantic_tokens_full.py @@ -16,10 +16,10 @@ ############################################################################ from typing import Optional, Union -from pygls.lsp.methods import ( +from lsprotocol.types import ( TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( SemanticTokens, SemanticTokensLegend, SemanticTokensParams, @@ -84,7 +84,7 @@ def test_semantic_tokens_full_return_tokens(client_server): assert response - assert response["data"] == [0, 0, 3, 0, 0] + assert response.data == [0, 0, 3, 0, 0] @ConfiguredLS.decorate() diff --git a/tests/lsp/test_call_hierarchy.py b/tests/lsp/test_call_hierarchy.py index 721aaa4a..c86d0d59 100644 --- a/tests/lsp/test_call_hierarchy.py +++ b/tests/lsp/test_call_hierarchy.py @@ -16,12 +16,12 @@ ############################################################################ from typing import List, Optional -from pygls.lsp.methods import ( - TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS, - TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS, - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE +from lsprotocol.types import ( + CALL_HIERARCHY_INCOMING_CALLS, + CALL_HIERARCHY_OUTGOING_CALLS, + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY ) -from pygls.lsp.types import ( +from lsprotocol.types import ( CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOptions, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, @@ -49,21 +49,21 @@ def check_call_hierarchy_item_response(item): - assert item['name'] == 'test_name' - assert item['kind'] == SymbolKind.File - assert item['uri'] == 'test_uri' - assert item['range']['start']['line'] == 0 - assert item['range']['start']['character'] == 0 - assert item['range']['end']['line'] == 1 - assert item['range']['end']['character'] == 1 - assert item['selectionRange']['start']['line'] == 1 - assert item['selectionRange']['start']['character'] == 1 - assert item['selectionRange']['end']['line'] == 2 - assert item['selectionRange']['end']['character'] == 2 - assert len(item['tags']) == 1 - assert item['tags'][0] == SymbolTag.Deprecated - assert item['detail'] == 'test_detail' - assert item['data'] == 'test_data' + assert item.name == 'test_name' + assert item.kind == SymbolKind.File + assert item.uri == 'test_uri' + assert item.range.start.line == 0 + assert item.range.start.character == 0 + assert item.range.end.line == 1 + assert item.range.end.character == 1 + assert item.selection_range.start.line == 1 + assert item.selection_range.start.character == 1 + assert item.selection_range.end.line == 2 + assert item.selection_range.end.character == 2 + assert len(item.tags) == 1 + assert item.tags[0] == SymbolTag.Deprecated + assert item.detail == 'test_detail' + assert item.data == 'test_data' class ConfiguredLS(ClientServer): @@ -71,7 +71,7 @@ def __init__(self): super().__init__() @self.server.feature( - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE, + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyOptions(), ) def f1( @@ -82,7 +82,7 @@ def f1( else: return None - @self.server.feature(TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS) + @self.server.feature(CALL_HIERARCHY_INCOMING_CALLS) def f2( params: CallHierarchyIncomingCallsParams ) -> Optional[List[CallHierarchyIncomingCall]]: @@ -98,7 +98,7 @@ def f2( ), ] - @self.server.feature(TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS) + @self.server.feature(CALL_HIERARCHY_OUTGOING_CALLS) def f3( params: CallHierarchyOutgoingCallsParams ) -> Optional[List[CallHierarchyOutgoingCall]]: @@ -126,7 +126,7 @@ def test_capabilities(client_server): def test_call_hierarchy_prepare_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE, + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( text_document=TextDocumentIdentifier(uri='file://return.list'), position=Position(line=0, character=0), @@ -140,7 +140,7 @@ def test_call_hierarchy_prepare_return_list(client_server): def test_call_hierarchy_prepare_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE, + TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY, CallHierarchyPrepareParams( text_document=TextDocumentIdentifier(uri='file://return.none'), position=Position(line=0, character=0), @@ -154,33 +154,34 @@ def test_call_hierarchy_prepare_return_none(client_server): def test_call_hierarchy_incoming_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS, + CALL_HIERARCHY_INCOMING_CALLS, CallHierarchyIncomingCallsParams(item=CALL_HIERARCHY_ITEM) ).result() item = response[0] - check_call_hierarchy_item_response(item['from']) + check_call_hierarchy_item_response(item.from_) - assert item['fromRanges'][0]['start']['line'] == 2 - assert item['fromRanges'][0]['start']['character'] == 2 - assert item['fromRanges'][0]['end']['line'] == 3 - assert item['fromRanges'][0]['end']['character'] == 3 + assert item.from_ranges[0].start.line == 2 + assert item.from_ranges[0].start.character == 2 + assert item.from_ranges[0].end.line == 3 + assert item.from_ranges[0].end.character == 3 @ConfiguredLS.decorate() def test_call_hierarchy_outgoing_calls_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS, + CALL_HIERARCHY_OUTGOING_CALLS, CallHierarchyOutgoingCallsParams(item=CALL_HIERARCHY_ITEM) ).result() item = response[0] - check_call_hierarchy_item_response(item['to']) + check_call_hierarchy_item_response(item.to) + + assert item.from_ranges[0].start.line == 3 + assert item.from_ranges[0].start.character == 3 + assert item.from_ranges[0].end.line == 4 + assert item.from_ranges[0].end.character == 4 - assert item['fromRanges'][0]['start']['line'] == 3 - assert item['fromRanges'][0]['start']['character'] == 3 - assert item['fromRanges'][0]['end']['line'] == 4 - assert item['fromRanges'][0]['end']['character'] == 4 diff --git a/tests/lsp/test_code_action.py b/tests/lsp/test_code_action.py index 31946cd8..033ba5ed 100644 --- a/tests/lsp/test_code_action.py +++ b/tests/lsp/test_code_action.py @@ -16,8 +16,8 @@ ############################################################################ from typing import List, Optional, Union -from pygls.lsp.methods import CODE_ACTION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_CODE_ACTION +from lsprotocol.types import ( CodeAction, CodeActionContext, CodeActionKind, @@ -38,7 +38,7 @@ def __init__(self): super().__init__() @self.server.feature( - CODE_ACTION, + TEXT_DOCUMENT_CODE_ACTION, CodeActionOptions(code_action_kinds=[CodeActionKind.Refactor]) ) def f( @@ -72,7 +72,7 @@ def test_capabilities(client_server): def test_code_action_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - CODE_ACTION, + TEXT_DOCUMENT_CODE_ACTION, CodeActionParams( text_document=TextDocumentIdentifier(uri="file://return.list"), range=Range( @@ -94,19 +94,19 @@ def test_code_action_return_list(client_server): ), ).result() - assert response[0]["title"] == "action1" - assert response[1]["title"] == "action2" - assert response[1]["kind"] == CodeActionKind.Refactor - assert response[2]["title"] == "cmd1" - assert response[2]["command"] == "cmd1" - assert response[2]["arguments"] == [1, "two"] + assert response[0].title == "action1" + assert response[1].title == "action2" + assert response[1].kind == CodeActionKind.Refactor + assert response[2].title == "cmd1" + assert response[2].command == "cmd1" + assert response[2].arguments == [1, "two"] @ConfiguredLS.decorate() def test_code_action_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - CODE_ACTION, + TEXT_DOCUMENT_CODE_ACTION, CodeActionParams( text_document=TextDocumentIdentifier(uri="file://return.none"), range=Range( diff --git a/tests/lsp/test_code_lens.py b/tests/lsp/test_code_lens.py index db2b5146..2b728bf4 100644 --- a/tests/lsp/test_code_lens.py +++ b/tests/lsp/test_code_lens.py @@ -16,8 +16,8 @@ ############################################################################ from typing import List, Optional -from pygls.lsp.methods import CODE_LENS -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_CODE_LENS +from lsprotocol.types import ( CodeLens, CodeLensOptions, CodeLensParams, @@ -35,7 +35,7 @@ def __init__(self): super().__init__() @self.server.feature( - CODE_LENS, + TEXT_DOCUMENT_CODE_LENS, CodeLensOptions(resolve_provider=False), ) def f(params: CodeLensParams) -> Optional[List[CodeLens]]: @@ -70,26 +70,26 @@ def test_capabilities(client_server): def test_code_lens_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - CODE_LENS, + TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier(uri="file://return.list") ), ).result() - assert response[0]["data"] == "some data" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 - assert response[0]["command"]["title"] == "cmd1" - assert response[0]["command"]["command"] == "cmd1" + assert response[0].data == "some data" + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 + assert response[0].command.title == "cmd1" + assert response[0].command.command == "cmd1" @ConfiguredLS.decorate() def test_code_lens_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - CODE_LENS, + TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier(uri="file://return.none") ), diff --git a/tests/lsp/test_color_presentation.py b/tests/lsp/test_color_presentation.py index ffd64c01..6748e66f 100644 --- a/tests/lsp/test_color_presentation.py +++ b/tests/lsp/test_color_presentation.py @@ -17,8 +17,8 @@ from typing import List -from pygls.lsp.methods import COLOR_PRESENTATION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_COLOR_PRESENTATION +from lsprotocol.types import ( Color, ColorPresentation, ColorPresentationParams, @@ -35,7 +35,7 @@ class ConfiguredLS(ClientServer): def __init__(self): super().__init__() - @self.server.feature(COLOR_PRESENTATION) + @self.server.feature(TEXT_DOCUMENT_COLOR_PRESENTATION) def f(params: ColorPresentationParams) -> List[ColorPresentation]: return [ ColorPresentation( @@ -67,21 +67,11 @@ def f(params: ColorPresentationParams) -> List[ColorPresentation]: ] -@ConfiguredLS.decorate() -def test_capabilities(): - """From specs: - - This request has no special capabilities and registration options since - it is send as a resolve request for the textDocument/documentColor - request. - """ - - @ConfiguredLS.decorate() def test_color_presentation(client_server): client, _ = client_server response = client.lsp.send_request( - COLOR_PRESENTATION, + TEXT_DOCUMENT_COLOR_PRESENTATION, ColorPresentationParams( text_document=TextDocumentIdentifier(uri="file://return.list"), color=Color(red=0.6, green=0.2, blue=0.3, alpha=0.5), @@ -92,22 +82,22 @@ def test_color_presentation(client_server): ), ).result() - assert response[0]["label"] == "label1" - assert response[0]["textEdit"]["newText"] == "te" + assert response[0].label == "label1" + assert response[0].text_edit.new_text == "te" - assert response[0]["textEdit"]["range"]["start"]["line"] == 0 - assert response[0]["textEdit"]["range"]["start"]["character"] == 0 - assert response[0]["textEdit"]["range"]["end"]["line"] == 1 - assert response[0]["textEdit"]["range"]["end"]["character"] == 1 + assert response[0].text_edit.range.start.line == 0 + assert response[0].text_edit.range.start.character == 0 + assert response[0].text_edit.range.end.line == 1 + assert response[0].text_edit.range.end.character == 1 - range = response[0]["additionalTextEdits"][0]["range"] - assert range["start"]["line"] == 1 - assert range["start"]["character"] == 1 - assert range["end"]["line"] == 2 - assert range["end"]["character"] == 2 + range = response[0].additional_text_edits[0].range + assert range.start.line == 1 + assert range.start.character == 1 + assert range.end.line == 2 + assert range.end.character == 2 - range = response[0]["additionalTextEdits"][1]["range"] - assert range["start"]["line"] == 2 - assert range["start"]["character"] == 2 - assert range["end"]["line"] == 3 - assert range["end"]["character"] == 3 + range = response[0].additional_text_edits[1].range + assert range.start.line == 2 + assert range.start.character == 2 + assert range.end.line == 3 + assert range.end.character == 3 diff --git a/tests/lsp/test_completion.py b/tests/lsp/test_completion.py index 59c70946..2cd7c182 100644 --- a/tests/lsp/test_completion.py +++ b/tests/lsp/test_completion.py @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from pygls.lsp.methods import COMPLETION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_COMPLETION +from lsprotocol.types import ( CompletionItem, CompletionItemKind, CompletionList, @@ -33,7 +33,7 @@ def __init__(self): super().__init__() @self.server.feature( - COMPLETION, + TEXT_DOCUMENT_COMPLETION, CompletionOptions( trigger_characters=[","], all_commit_characters=[":"], @@ -68,28 +68,28 @@ def test_capabilities(client_server): def test_completions(client_server): client, _ = client_server response = client.lsp.send_request( - COMPLETION, + TEXT_DOCUMENT_COMPLETION, CompletionParams( text_document=TextDocumentIdentifier(uri="file://test.test"), position=Position(line=0, character=0), ), ).result() - assert not response["isIncomplete"] - assert response["items"][0]["label"] == "test1" - assert response["items"][0]["kind"] == CompletionItemKind.Method - assert response["items"][0]["preselect"] - assert "deprecated" not in response["items"][0] - assert "tags" not in response["items"][0] - assert "detail" not in response["items"][0] - assert "documentation" not in response["items"][0] - assert "sort_text" not in response["items"][0] - assert "filter_text" not in response["items"][0] - assert "insert_text" not in response["items"][0] - assert "insert_text_format" not in response["items"][0] - assert "insert_text_mode" not in response["items"][0] - assert "text_edit" not in response["items"][0] - assert "additional_text_edits" not in response["items"][0] - assert "commit_characters" not in response["items"][0] - assert "command" not in response["items"][0] - assert "data" not in response["items"][0] + assert not response.is_incomplete + assert response.items[0].label == "test1" + assert response.items[0].kind == CompletionItemKind.Method + assert response.items[0].preselect + assert response.items[0].deprecated is None + assert response.items[0].tags is None + assert response.items[0].detail is None + assert response.items[0].documentation is None + assert response.items[0].sort_text is None + assert response.items[0].filter_text is None + assert response.items[0].insert_text is None + assert response.items[0].insert_text_format is None + assert response.items[0].insert_text_mode is None + assert response.items[0].text_edit is None + assert response.items[0].additional_text_edits is None + assert response.items[0].commit_characters is None + assert response.items[0].command is None + assert response.items[0].data is None diff --git a/tests/lsp/test_declaration.py b/tests/lsp/test_declaration.py index a1a1849b..4a8c3de9 100644 --- a/tests/lsp/test_declaration.py +++ b/tests/lsp/test_declaration.py @@ -17,8 +17,8 @@ from typing import List, Optional, Union -from pygls.lsp.methods import DECLARATION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DECLARATION +from lsprotocol.types import ( DeclarationOptions, DeclarationParams, Location, @@ -35,7 +35,7 @@ class ConfiguredLS(ClientServer): def __init__(self): super().__init__() - @self.server.feature(DECLARATION, DeclarationOptions()) + @self.server.feature(TEXT_DOCUMENT_DECLARATION, DeclarationOptions()) def f( params: DeclarationParams, ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: @@ -82,7 +82,7 @@ def test_capabilities(client_server): def test_declaration_return_location(client_server): client, _ = client_server response = client.lsp.send_request( - DECLARATION, + TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier( uri="file://return.location" @@ -91,19 +91,19 @@ def test_declaration_return_location(client_server): ), ).result() - assert response["uri"] == "uri" + assert response.uri == "uri" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_declaration_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( - DECLARATION, + TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier( uri="file://return.location_list"), @@ -111,19 +111,19 @@ def test_declaration_return_location_list(client_server): ), ).result() - assert response[0]["uri"] == "uri" + assert response[0].uri == "uri" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_declaration_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( - DECLARATION, + TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" @@ -132,29 +132,29 @@ def test_declaration_return_location_link_list(client_server): ), ).result() - assert response[0]["targetUri"] == "uri" + assert response[0].target_uri == "uri" - assert response[0]["targetRange"]["start"]["line"] == 0 - assert response[0]["targetRange"]["start"]["character"] == 0 - assert response[0]["targetRange"]["end"]["line"] == 1 - assert response[0]["targetRange"]["end"]["character"] == 1 + assert response[0].target_range.start.line == 0 + assert response[0].target_range.start.character == 0 + assert response[0].target_range.end.line == 1 + assert response[0].target_range.end.character == 1 - assert response[0]["targetSelectionRange"]["start"]["line"] == 0 - assert response[0]["targetSelectionRange"]["start"]["character"] == 0 - assert response[0]["targetSelectionRange"]["end"]["line"] == 2 - assert response[0]["targetSelectionRange"]["end"]["character"] == 2 + assert response[0].target_selection_range.start.line == 0 + assert response[0].target_selection_range.start.character == 0 + assert response[0].target_selection_range.end.line == 2 + assert response[0].target_selection_range.end.character == 2 - assert response[0]["originSelectionRange"]["start"]["line"] == 0 - assert response[0]["originSelectionRange"]["start"]["character"] == 0 - assert response[0]["originSelectionRange"]["end"]["line"] == 3 - assert response[0]["originSelectionRange"]["end"]["character"] == 3 + assert response[0].origin_selection_range.start.line == 0 + assert response[0].origin_selection_range.start.character == 0 + assert response[0].origin_selection_range.end.line == 3 + assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_declaration_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - DECLARATION, + TEXT_DOCUMENT_DECLARATION, DeclarationParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_definition.py b/tests/lsp/test_definition.py index ae8f3f57..d101efb5 100644 --- a/tests/lsp/test_definition.py +++ b/tests/lsp/test_definition.py @@ -17,8 +17,8 @@ from typing import List, Optional, Union -from pygls.lsp.methods import DEFINITION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DEFINITION +from lsprotocol.types import ( DefinitionOptions, DefinitionParams, Location, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - DEFINITION, + TEXT_DOCUMENT_DEFINITION, DefinitionOptions(), ) def f( @@ -85,7 +85,7 @@ def test_capabilities(client_server): def test_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( - DEFINITION, + TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location"), @@ -93,19 +93,19 @@ def test_definition_return_location(client_server): ), ).result() - assert response["uri"] == "uri" + assert response.uri == "uri" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( - DEFINITION, + TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_list"), @@ -113,19 +113,19 @@ def test_definition_return_location_list(client_server): ), ).result() - assert response[0]["uri"] == "uri" + assert response[0].uri == "uri" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( - DEFINITION, + TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" @@ -134,29 +134,29 @@ def test_definition_return_location_link_list(client_server): ), ).result() - assert response[0]["targetUri"] == "uri" + assert response[0].target_uri == "uri" - assert response[0]["targetRange"]["start"]["line"] == 0 - assert response[0]["targetRange"]["start"]["character"] == 0 - assert response[0]["targetRange"]["end"]["line"] == 1 - assert response[0]["targetRange"]["end"]["character"] == 1 + assert response[0].target_range.start.line == 0 + assert response[0].target_range.start.character == 0 + assert response[0].target_range.end.line == 1 + assert response[0].target_range.end.character == 1 - assert response[0]["targetSelectionRange"]["start"]["line"] == 0 - assert response[0]["targetSelectionRange"]["start"]["character"] == 0 - assert response[0]["targetSelectionRange"]["end"]["line"] == 2 - assert response[0]["targetSelectionRange"]["end"]["character"] == 2 + assert response[0].target_selection_range.start.line == 0 + assert response[0].target_selection_range.start.character == 0 + assert response[0].target_selection_range.end.line == 2 + assert response[0].target_selection_range.end.character == 2 - assert response[0]["originSelectionRange"]["start"]["line"] == 0 - assert response[0]["originSelectionRange"]["start"]["character"] == 0 - assert response[0]["originSelectionRange"]["end"]["line"] == 3 - assert response[0]["originSelectionRange"]["end"]["character"] == 3 + assert response[0].origin_selection_range.start.line == 0 + assert response[0].origin_selection_range.start.character == 0 + assert response[0].origin_selection_range.end.line == 3 + assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - DEFINITION, + TEXT_DOCUMENT_DEFINITION, DefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_document_color.py b/tests/lsp/test_document_color.py index 2c872202..460a60b4 100644 --- a/tests/lsp/test_document_color.py +++ b/tests/lsp/test_document_color.py @@ -17,8 +17,8 @@ from typing import List -from pygls.lsp.methods import DOCUMENT_COLOR -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_COLOR +from lsprotocol.types import ( Color, ColorInformation, DocumentColorOptions, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - DOCUMENT_COLOR, + TEXT_DOCUMENT_DOCUMENT_COLOR, DocumentColorOptions(), ) def f(params: DocumentColorParams) -> List[ColorInformation]: @@ -63,19 +63,19 @@ def test_capabilities(client_server): def test_document_color(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_COLOR, + TEXT_DOCUMENT_DOCUMENT_COLOR, DocumentColorParams( text_document=TextDocumentIdentifier(uri="file://return.list") ), ).result() assert response - assert response[0]["color"]["red"] == 0.5 - assert response[0]["color"]["green"] == 0.5 - assert response[0]["color"]["blue"] == 0.5 - assert response[0]["color"]["alpha"] == 0.5 + assert response[0].color.red == 0.5 + assert response[0].color.green == 0.5 + assert response[0].color.blue == 0.5 + assert response[0].color.alpha == 0.5 - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 diff --git a/tests/lsp/test_document_highlight.py b/tests/lsp/test_document_highlight.py index 6d76ce41..dcfc0162 100644 --- a/tests/lsp/test_document_highlight.py +++ b/tests/lsp/test_document_highlight.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import DOCUMENT_HIGHLIGHT -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT +from lsprotocol.types import ( DocumentHighlight, DocumentHighlightKind, DocumentHighlightOptions, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - DOCUMENT_HIGHLIGHT, + TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightOptions(), ) def f( @@ -74,7 +74,7 @@ def test_capabilities(client_server): def test_document_highlight_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_HIGHLIGHT, + TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), @@ -83,24 +83,24 @@ def test_document_highlight_return_list(client_server): assert response - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 - assert "kind" not in response[0] + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 + assert response[0].kind is None - assert response[1]["range"]["start"]["line"] == 1 - assert response[1]["range"]["start"]["character"] == 1 - assert response[1]["range"]["end"]["line"] == 2 - assert response[1]["range"]["end"]["character"] == 2 - assert response[1]["kind"] == DocumentHighlightKind.Write + assert response[1].range.start.line == 1 + assert response[1].range.start.character == 1 + assert response[1].range.end.line == 2 + assert response[1].range.end.character == 2 + assert response[1].kind == DocumentHighlightKind.Write @ConfiguredLS.decorate() def test_document_highlight_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_HIGHLIGHT, + TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT, DocumentHighlightParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_document_link.py b/tests/lsp/test_document_link.py index f208392c..0602773d 100644 --- a/tests/lsp/test_document_link.py +++ b/tests/lsp/test_document_link.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import DOCUMENT_LINK -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_LINK +from lsprotocol.types import ( DocumentLink, DocumentLinkOptions, DocumentLinkParams, @@ -35,7 +35,7 @@ def __init__(self): super().__init__() @self.server.feature( - DOCUMENT_LINK, + TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkOptions(resolve_provider=True), ) def f(params: DocumentLinkParams) -> Optional[List[DocumentLink]]: @@ -68,7 +68,7 @@ def test_capabilities(client_server): def test_document_link_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_LINK, + TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkParams( text_document=TextDocumentIdentifier(uri="file://return.list"), ), @@ -76,20 +76,20 @@ def test_document_link_return_list(client_server): assert response - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 - assert response[0]["target"] == "target" - assert response[0]["tooltip"] == "tooltip" - assert response[0]["data"] == "data" + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 + assert response[0].target == "target" + assert response[0].tooltip == "tooltip" + assert response[0].data == "data" @ConfiguredLS.decorate() def test_document_link_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_LINK, + TEXT_DOCUMENT_DOCUMENT_LINK, DocumentLinkParams( text_document=TextDocumentIdentifier(uri="file://return.none"), ), diff --git a/tests/lsp/test_document_symbol.py b/tests/lsp/test_document_symbol.py index 9dd97c4e..251c8fb8 100644 --- a/tests/lsp/test_document_symbol.py +++ b/tests/lsp/test_document_symbol.py @@ -17,8 +17,8 @@ from typing import List, Union -from pygls.lsp.methods import DOCUMENT_SYMBOL -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_DOCUMENT_SYMBOL +from lsprotocol.types import ( DocumentSymbol, DocumentSymbolOptions, DocumentSymbolParams, @@ -38,7 +38,7 @@ def __init__(self): super().__init__() @self.server.feature( - DOCUMENT_SYMBOL, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolOptions(), ) def f( @@ -105,7 +105,7 @@ def test_capabilities(client_server): def test_document_symbol_return_symbol_information_list(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_SYMBOL, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolParams( text_document=TextDocumentIdentifier( uri="file://return.symbol_information_list" @@ -115,22 +115,22 @@ def test_document_symbol_return_symbol_information_list(client_server): assert response - assert response[0]["name"] == "symbol" - assert response[0]["kind"] == SymbolKind.Namespace - assert response[0]["location"]["uri"] == "uri" - assert response[0]["location"]["range"]["start"]["line"] == 0 - assert response[0]["location"]["range"]["start"]["character"] == 0 - assert response[0]["location"]["range"]["end"]["line"] == 1 - assert response[0]["location"]["range"]["end"]["character"] == 1 - assert response[0]["containerName"] == "container" - assert not response[0]["deprecated"] + assert response[0].name == "symbol" + assert response[0].kind == SymbolKind.Namespace + assert response[0].location.uri == "uri" + assert response[0].location.range.start.line == 0 + assert response[0].location.range.start.character == 0 + assert response[0].location.range.end.line == 1 + assert response[0].location.range.end.character == 1 + assert response[0].container_name == "container" + assert not response[0].deprecated @ConfiguredLS.decorate() def test_document_symbol_return_document_symbol_list(client_server): client, _ = client_server response = client.lsp.send_request( - DOCUMENT_SYMBOL, + TEXT_DOCUMENT_DOCUMENT_SYMBOL, DocumentSymbolParams( text_document=TextDocumentIdentifier( uri="file://return.document_symbol_list" @@ -140,29 +140,29 @@ def test_document_symbol_return_document_symbol_list(client_server): assert response - assert response[0]["name"] == "symbol" - assert response[0]["kind"] == SymbolKind.Object - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 10 - assert response[0]["range"]["end"]["character"] == 10 - assert response[0]["selectionRange"]["start"]["line"] == 0 - assert response[0]["selectionRange"]["start"]["character"] == 0 - assert response[0]["selectionRange"]["end"]["line"] == 10 - assert response[0]["selectionRange"]["end"]["character"] == 10 - assert response[0]["detail"] == "detail" - assert response[0]["deprecated"] - - assert response[0]["children"][0]["name"] == "inner_symbol" - assert response[0]["children"][0]["kind"] == SymbolKind.Number - assert response[0]["children"][0]["range"]["start"]["line"] == 0 - assert response[0]["children"][0]["range"]["start"]["character"] == 0 - assert response[0]["children"][0]["range"]["end"]["line"] == 1 - assert response[0]["children"][0]["range"]["end"]["character"] == 1 - range = response[0]["children"][0]["selectionRange"] - assert range["start"]["line"] == 0 - assert range["start"]["character"] == 0 - assert range["end"]["line"] == 1 - assert range["end"]["character"] == 1 - - assert "children" not in response[0]["children"][0] + assert response[0].name == "symbol" + assert response[0].kind == SymbolKind.Object + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 10 + assert response[0].range.end.character == 10 + assert response[0].selection_range.start.line == 0 + assert response[0].selection_range.start.character == 0 + assert response[0].selection_range.end.line == 10 + assert response[0].selection_range.end.character == 10 + assert response[0].detail == "detail" + assert response[0].deprecated + + assert response[0].children[0].name == "inner_symbol" + assert response[0].children[0].kind == SymbolKind.Number + assert response[0].children[0].range.start.line == 0 + assert response[0].children[0].range.start.character == 0 + assert response[0].children[0].range.end.line == 1 + assert response[0].children[0].range.end.character == 1 + range = response[0].children[0].selection_range + assert range.start.line == 0 + assert range.start.character == 0 + assert range.end.line == 1 + assert range.end.character == 1 + + assert response[0].children[0].children is None diff --git a/tests/lsp/test_errors.py b/tests/lsp/test_errors.py index 4f5f7484..1fbc05b4 100644 --- a/tests/lsp/test_errors.py +++ b/tests/lsp/test_errors.py @@ -21,9 +21,8 @@ import pytest from pygls.exceptions import JsonRpcInternalError, PyglsError, JsonRpcException -from pygls.lsp.methods import WINDOW_SHOW_MESSAGE +from lsprotocol.types import WINDOW_SHOW_MESSAGE, MessageType from pygls.server import LanguageServer -from pygls.lsp.types import MessageType from ..conftest import ClientServer diff --git a/tests/lsp/test_folding_range.py b/tests/lsp/test_folding_range.py index b2bdfef6..8f9c749a 100644 --- a/tests/lsp/test_folding_range.py +++ b/tests/lsp/test_folding_range.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import FOLDING_RANGE -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_FOLDING_RANGE +from lsprotocol.types import ( FoldingRange, FoldingRangeKind, FoldingRangeOptions, @@ -34,7 +34,7 @@ def __init__(self): super().__init__() @self.server.feature( - FOLDING_RANGE, + TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeOptions(), ) def f(params: FoldingRangeParams) -> Optional[List[FoldingRange]]: @@ -64,7 +64,7 @@ def test_capabilities(client_server): def test_folding_range_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - FOLDING_RANGE, + TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.list"), ), @@ -72,18 +72,18 @@ def test_folding_range_return_list(client_server): assert response - assert response[0]["startLine"] == 0 - assert response[0]["endLine"] == 0 - assert response[0]["startCharacter"] == 1 - assert response[0]["endCharacter"] == 1 - assert response[0]["kind"] == FoldingRangeKind.Comment + assert response[0].start_line == 0 + assert response[0].end_line == 0 + assert response[0].start_character == 1 + assert response[0].end_character == 1 + assert response[0].kind == FoldingRangeKind.Comment @ConfiguredLS.decorate() def test_folding_range_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - FOLDING_RANGE, + TEXT_DOCUMENT_FOLDING_RANGE, FoldingRangeParams( text_document=TextDocumentIdentifier(uri="file://return.none"), ), diff --git a/tests/lsp/test_formatting.py b/tests/lsp/test_formatting.py index 6f0caa60..0c3fbd66 100644 --- a/tests/lsp/test_formatting.py +++ b/tests/lsp/test_formatting.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import FORMATTING -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_FORMATTING +from lsprotocol.types import ( DocumentFormattingOptions, DocumentFormattingParams, FormattingOptions, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - FORMATTING, + TEXT_DOCUMENT_FORMATTING, DocumentFormattingOptions(), ) def f(params: DocumentFormattingParams) -> Optional[List[TextEdit]]: @@ -66,7 +66,7 @@ def test_capabilities(client_server): def test_document_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - FORMATTING, + TEXT_DOCUMENT_FORMATTING, DocumentFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), options=FormattingOptions( @@ -81,18 +81,18 @@ def test_document_formatting_return_list(client_server): assert response - assert response[0]["newText"] == "text" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].new_text == "text" + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_document_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - FORMATTING, + TEXT_DOCUMENT_FORMATTING, DocumentFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), options=FormattingOptions( diff --git a/tests/lsp/test_hover.py b/tests/lsp/test_hover.py index 3adae7ad..7e320a8b 100644 --- a/tests/lsp/test_hover.py +++ b/tests/lsp/test_hover.py @@ -17,12 +17,12 @@ from typing import Optional -from pygls.lsp.methods import HOVER -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_HOVER +from lsprotocol.types import ( Hover, HoverOptions, HoverParams, - MarkedString, + MarkedString_Type1, MarkupContent, MarkupKind, Position, @@ -38,7 +38,7 @@ def __init__(self): super().__init__() @self.server.feature( - HOVER, + TEXT_DOCUMENT_HOVER, HoverOptions(), ) def f(params: HoverParams) -> Optional[Hover]: @@ -50,7 +50,7 @@ def f(params: HoverParams) -> Optional[Hover]: return { "file://return.marked_string": Hover( range=range, - contents=MarkedString( + contents=MarkedString_Type1( language="language", value="value", ), @@ -58,7 +58,7 @@ def f(params: HoverParams) -> Optional[Hover]: "file://return.marked_string_list": Hover( range=range, contents=[ - MarkedString( + MarkedString_Type1( language="language", value="value", ), @@ -85,7 +85,7 @@ def test_capabilities(client_server): def test_hover_return_marked_string(client_server): client, _ = client_server response = client.lsp.send_request( - HOVER, + TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier( uri="file://return.marked_string"), @@ -95,20 +95,20 @@ def test_hover_return_marked_string(client_server): assert response - assert response["contents"]["language"] == "language" - assert response["contents"]["value"] == "value" + assert response.contents.language == "language" + assert response.contents.value == "value" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_hover_return_marked_string_list(client_server): client, _ = client_server response = client.lsp.send_request( - HOVER, + TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier( uri="file://return.marked_string_list" @@ -119,21 +119,21 @@ def test_hover_return_marked_string_list(client_server): assert response - assert response["contents"][0]["language"] == "language" - assert response["contents"][0]["value"] == "value" - assert response["contents"][1] == "str type" + assert response.contents[0].language == "language" + assert response.contents[0].value == "value" + assert response.contents[1] == "str type" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_hover_return_markup_content(client_server): client, _ = client_server response = client.lsp.send_request( - HOVER, + TEXT_DOCUMENT_HOVER, HoverParams( text_document=TextDocumentIdentifier( uri="file://return.markup_content" @@ -144,10 +144,10 @@ def test_hover_return_markup_content(client_server): assert response - assert response["contents"]["kind"] == MarkupKind.Markdown - assert response["contents"]["value"] == "value" + assert response.contents.kind == MarkupKind.Markdown + assert response.contents.value == "value" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 diff --git a/tests/lsp/test_implementation.py b/tests/lsp/test_implementation.py index 88014124..9a8e690c 100644 --- a/tests/lsp/test_implementation.py +++ b/tests/lsp/test_implementation.py @@ -17,8 +17,8 @@ from typing import List, Optional, Union -from pygls.lsp.methods import IMPLEMENTATION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_IMPLEMENTATION +from lsprotocol.types import ( ImplementationOptions, ImplementationParams, Location, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - IMPLEMENTATION, + TEXT_DOCUMENT_IMPLEMENTATION, ImplementationOptions(), ) def f( @@ -85,7 +85,7 @@ def test_capabilities(client_server): def test_type_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( - IMPLEMENTATION, + TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier( uri="file://return.location"), @@ -93,19 +93,19 @@ def test_type_definition_return_location(client_server): ), ).result() - assert response["uri"] == "uri" + assert response.uri == "uri" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( - IMPLEMENTATION, + TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier( uri="file://return.location_list"), @@ -113,19 +113,19 @@ def test_type_definition_return_location_list(client_server): ), ).result() - assert response[0]["uri"] == "uri" + assert response[0].uri == "uri" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( - IMPLEMENTATION, + TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" @@ -134,29 +134,29 @@ def test_type_definition_return_location_link_list(client_server): ), ).result() - assert response[0]["targetUri"] == "uri" + assert response[0].target_uri == "uri" - assert response[0]["targetRange"]["start"]["line"] == 0 - assert response[0]["targetRange"]["start"]["character"] == 0 - assert response[0]["targetRange"]["end"]["line"] == 1 - assert response[0]["targetRange"]["end"]["character"] == 1 + assert response[0].target_range.start.line == 0 + assert response[0].target_range.start.character == 0 + assert response[0].target_range.end.line == 1 + assert response[0].target_range.end.character == 1 - assert response[0]["targetSelectionRange"]["start"]["line"] == 0 - assert response[0]["targetSelectionRange"]["start"]["character"] == 0 - assert response[0]["targetSelectionRange"]["end"]["line"] == 2 - assert response[0]["targetSelectionRange"]["end"]["character"] == 2 + assert response[0].target_selection_range.start.line == 0 + assert response[0].target_selection_range.start.character == 0 + assert response[0].target_selection_range.end.line == 2 + assert response[0].target_selection_range.end.character == 2 - assert response[0]["originSelectionRange"]["start"]["line"] == 0 - assert response[0]["originSelectionRange"]["start"]["character"] == 0 - assert response[0]["originSelectionRange"]["end"]["line"] == 3 - assert response[0]["originSelectionRange"]["end"]["character"] == 3 + assert response[0].origin_selection_range.start.line == 0 + assert response[0].origin_selection_range.start.character == 0 + assert response[0].origin_selection_range.end.line == 3 + assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_type_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - IMPLEMENTATION, + TEXT_DOCUMENT_IMPLEMENTATION, ImplementationParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_linked_editing_range.py b/tests/lsp/test_linked_editing_range.py index 7da35029..8b00644c 100644 --- a/tests/lsp/test_linked_editing_range.py +++ b/tests/lsp/test_linked_editing_range.py @@ -17,8 +17,8 @@ from typing import Optional -from pygls.lsp.methods import TEXT_DOCUMENT_LINKED_EDITING_RANGE -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_LINKED_EDITING_RANGE +from lsprotocol.types import ( LinkedEditingRangeOptions, LinkedEditingRangeParams, LinkedEditingRanges, @@ -81,15 +81,15 @@ def test_linked_editing_ranges_return_ranges(client_server): assert response - assert response["ranges"][0]["start"]["line"] == 0 - assert response["ranges"][0]["start"]["character"] == 0 - assert response["ranges"][0]["end"]["line"] == 1 - assert response["ranges"][0]["end"]["character"] == 1 - assert response["ranges"][1]["start"]["line"] == 1 - assert response["ranges"][1]["start"]["character"] == 1 - assert response["ranges"][1]["end"]["line"] == 2 - assert response["ranges"][1]["end"]["character"] == 2 - assert response["wordPattern"] == "pattern" + assert response.ranges[0].start.line == 0 + assert response.ranges[0].start.character == 0 + assert response.ranges[0].end.line == 1 + assert response.ranges[0].end.character == 1 + assert response.ranges[1].start.line == 1 + assert response.ranges[1].start.character == 1 + assert response.ranges[1].end.line == 2 + assert response.ranges[1].end.character == 2 + assert response.word_pattern == "pattern" @ConfiguredLS.decorate() diff --git a/tests/lsp/test_moniker.py b/tests/lsp/test_moniker.py index 3784a9af..09b962d6 100644 --- a/tests/lsp/test_moniker.py +++ b/tests/lsp/test_moniker.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import TEXT_DOCUMENT_MONIKER -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_MONIKER +from lsprotocol.types import ( Moniker, MonikerKind, MonikerOptions, @@ -74,10 +74,10 @@ def test_moniker_return_list(client_server): assert response - assert response[0]["scheme"] == "test_scheme" - assert response[0]["identifier"] == "test_identifier" - assert response[0]["unique"] == "global" - assert response[0]["kind"] == "local" + assert response[0].scheme == "test_scheme" + assert response[0].identifier == "test_identifier" + assert response[0].unique == UniquenessLevel.Global + assert response[0].kind == MonikerKind.Local @ConfiguredLS.decorate() diff --git a/tests/lsp/test_on_type_formatting.py b/tests/lsp/test_on_type_formatting.py index 42494a9c..b0f041e8 100644 --- a/tests/lsp/test_on_type_formatting.py +++ b/tests/lsp/test_on_type_formatting.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import ON_TYPE_FORMATTING -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_ON_TYPE_FORMATTING +from lsprotocol.types import ( DocumentOnTypeFormattingOptions, DocumentOnTypeFormattingParams, FormattingOptions, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - ON_TYPE_FORMATTING, + TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingOptions( first_trigger_character=":", more_trigger_character=[",", "."], @@ -83,7 +83,7 @@ def test_capabilities(client_server): def test_on_type_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - ON_TYPE_FORMATTING, + TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), @@ -100,18 +100,18 @@ def test_on_type_formatting_return_list(client_server): assert response - assert response[0]["newText"] == "text" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].new_text == "text" + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_on_type_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - ON_TYPE_FORMATTING, + TEXT_DOCUMENT_ON_TYPE_FORMATTING, DocumentOnTypeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_prepare_rename.py b/tests/lsp/test_prepare_rename.py index 7a6e13f8..11dae9c4 100644 --- a/tests/lsp/test_prepare_rename.py +++ b/tests/lsp/test_prepare_rename.py @@ -17,10 +17,11 @@ from typing import Optional, Union -from pygls.lsp.methods import PREPARE_RENAME -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_PREPARE_RENAME +from lsprotocol.types import ( Position, - PrepareRename, + PrepareRenameResult, + PrepareRenameResult_Type1, PrepareRenameParams, Range, TextDocumentIdentifier, @@ -33,16 +34,16 @@ class ConfiguredLS(ClientServer): def __init__(self): super().__init__() - @self.server.feature(PREPARE_RENAME) + @self.server.feature(TEXT_DOCUMENT_PREPARE_RENAME) def f( params: PrepareRenameParams - ) -> Optional[Union[Range, PrepareRename]]: + ) -> Optional[Union[Range, PrepareRenameResult]]: return { # type: ignore "file://return.range": Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), - "file://return.prepare_rename": PrepareRename( + "file://return.prepare_rename": PrepareRenameResult_Type1( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), @@ -61,7 +62,7 @@ def test_capabilities(client_server): def test_prepare_rename_return_range(client_server): client, _ = client_server response = client.lsp.send_request( - PREPARE_RENAME, + TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier( uri="file://return.range"), @@ -71,17 +72,17 @@ def test_prepare_rename_return_range(client_server): assert response - assert response["start"]["line"] == 0 - assert response["start"]["character"] == 0 - assert response["end"]["line"] == 1 - assert response["end"]["character"] == 1 + assert response.start.line == 0 + assert response.start.character == 0 + assert response.end.line == 1 + assert response.end.character == 1 @ConfiguredLS.decorate() def test_prepare_rename_return_prepare_rename(client_server): client, _ = client_server response = client.lsp.send_request( - PREPARE_RENAME, + TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier( uri="file://return.prepare_rename" @@ -92,18 +93,18 @@ def test_prepare_rename_return_prepare_rename(client_server): assert response - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 - assert response["placeholder"] == "placeholder" + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 + assert response.placeholder == "placeholder" @ConfiguredLS.decorate() def test_prepare_rename_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - PREPARE_RENAME, + TEXT_DOCUMENT_PREPARE_RENAME, PrepareRenameParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_progress.py b/tests/lsp/test_progress.py index e58117cc..2f27fa9c 100644 --- a/tests/lsp/test_progress.py +++ b/tests/lsp/test_progress.py @@ -18,17 +18,17 @@ from typing import List, Optional import time -from pygls.lsp.methods import CODE_LENS, PROGRESS_NOTIFICATION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_CODE_LENS, PROGRESS +from lsprotocol.types import ( CodeLens, CodeLensParams, CodeLensOptions, + ProgressParams, TextDocumentIdentifier, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, ) -from pygls.lsp.types.basic_structures import ProgressParams from ..conftest import ClientServer @@ -41,25 +41,25 @@ def __init__(self): self.client.notifications: List[ProgressParams] = [] @self.server.feature( - CODE_LENS, + TEXT_DOCUMENT_CODE_LENS, CodeLensOptions(resolve_provider=False, - work_done_progress=PROGRESS_TOKEN), + work_done_progress=True), ) def f1(params: CodeLensParams) -> Optional[List[CodeLens]]: self.server.lsp.progress.begin( PROGRESS_TOKEN, WorkDoneProgressBegin( - title="starting", percentage=0) + kind='begin', title="starting", percentage=0) ) self.server.lsp.progress.report( PROGRESS_TOKEN, - WorkDoneProgressReport(message="doing", percentage=50), + WorkDoneProgressReport(kind='report', message="doing", percentage=50), ) self.server.lsp.progress.end( - PROGRESS_TOKEN, WorkDoneProgressEnd(message="done") + PROGRESS_TOKEN, WorkDoneProgressEnd(kind='end', message="done") ) return None - @self.client.feature(PROGRESS_NOTIFICATION) + @self.client.feature(PROGRESS) def f2(params): self.client.notifications.append(params) @@ -71,14 +71,14 @@ def test_capabilities(client_server): provider = capabilities.code_lens_provider assert provider - assert provider.work_done_progress == PROGRESS_TOKEN + assert provider.work_done_progress @ConfiguredLS.decorate() def test_progress_notifications(client_server): client, _ = client_server client.lsp.send_request( - CODE_LENS, + TEXT_DOCUMENT_CODE_LENS, CodeLensParams( text_document=TextDocumentIdentifier(uri="file://return.none"), work_done_token=PROGRESS_TOKEN, diff --git a/tests/lsp/test_range_formatting.py b/tests/lsp/test_range_formatting.py index 64cdb677..7e8c480b 100644 --- a/tests/lsp/test_range_formatting.py +++ b/tests/lsp/test_range_formatting.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import RANGE_FORMATTING -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_RANGE_FORMATTING +from lsprotocol.types import ( DocumentRangeFormattingOptions, DocumentRangeFormattingParams, FormattingOptions, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - RANGE_FORMATTING, + TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingOptions(), ) def f( @@ -68,7 +68,7 @@ def test_capabilities(client_server): def test_range_formatting_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - RANGE_FORMATTING, + TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.list"), range=Range( @@ -87,18 +87,18 @@ def test_range_formatting_return_list(client_server): assert response - assert response[0]["newText"] == "text" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].new_text == "text" + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_range_formatting_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - RANGE_FORMATTING, + TEXT_DOCUMENT_RANGE_FORMATTING, DocumentRangeFormattingParams( text_document=TextDocumentIdentifier(uri="file://return.none"), range=Range( diff --git a/tests/lsp/test_references.py b/tests/lsp/test_references.py index e874c0d6..5867e35d 100644 --- a/tests/lsp/test_references.py +++ b/tests/lsp/test_references.py @@ -17,8 +17,8 @@ from typing import List, Optional -from pygls.lsp.methods import REFERENCES -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_REFERENCES +from lsprotocol.types import ( Location, Position, Range, @@ -36,7 +36,7 @@ def __init__(self): super().__init__() @self.server.feature( - REFERENCES, + TEXT_DOCUMENT_REFERENCES, ReferenceOptions(), ) def f(params: ReferenceParams) -> Optional[List[Location]]: @@ -66,7 +66,7 @@ def test_capabilities(client_server): def test_references_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - REFERENCES, + TEXT_DOCUMENT_REFERENCES, ReferenceParams( text_document=TextDocumentIdentifier(uri="file://return.list"), position=Position(line=0, character=0), @@ -78,19 +78,19 @@ def test_references_return_list(client_server): assert response - assert response[0]["uri"] == "uri" + assert response[0].uri == "uri" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_references_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - REFERENCES, + TEXT_DOCUMENT_REFERENCES, ReferenceParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_rename.py b/tests/lsp/test_rename.py index 1db95bcb..a4e5952a 100644 --- a/tests/lsp/test_rename.py +++ b/tests/lsp/test_rename.py @@ -17,8 +17,8 @@ from typing import Optional -from pygls.lsp.methods import RENAME -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_RENAME +from lsprotocol.types import ( CreateFile, CreateFileOptions, DeleteFile, @@ -75,7 +75,7 @@ ], ), CreateFile( - kind=ResourceOperationKind.Create, + kind=ResourceOperationKind.Create.value, uri="create file", options=CreateFileOptions( overwrite=True, @@ -83,7 +83,7 @@ ), ), RenameFile( - kind=ResourceOperationKind.Rename, + kind=ResourceOperationKind.Rename.value, old_uri="rename old uri", new_uri="rename new uri", options=RenameFileOptions( @@ -92,11 +92,11 @@ ), ), DeleteFile( - kind=ResourceOperationKind.Delete, + kind=ResourceOperationKind.Delete.value, uri="delete file", options=DeleteFileOptions( recursive=True, - ignore_if_exists=True, + ignore_if_not_exists=True, ), ), ], } @@ -107,7 +107,7 @@ def __init__(self): super().__init__() @self.server.feature( - RENAME, + TEXT_DOCUMENT_RENAME, RenameOptions(prepare_provider=True), ) def f(params: RenameParams) -> Optional[WorkspaceEdit]: @@ -130,7 +130,7 @@ def test_capabilities(client_server): def test_rename_return_workspace_edit(client_server): client, _ = client_server response = client.lsp.send_request( - RENAME, + TEXT_DOCUMENT_RENAME, RenameParams( text_document=TextDocumentIdentifier( uri="file://return.workspace_edit" @@ -142,55 +142,55 @@ def test_rename_return_workspace_edit(client_server): assert response - changes = response["changes"]["uri1"] - assert changes[0]["newText"] == "text1" - assert changes[0]["range"]["start"]["line"] == 0 - assert changes[0]["range"]["start"]["character"] == 0 - assert changes[0]["range"]["end"]["line"] == 1 - assert changes[0]["range"]["end"]["character"] == 1 - - assert changes[1]["newText"] == "text2" - assert changes[1]["range"]["start"]["line"] == 1 - assert changes[1]["range"]["start"]["character"] == 1 - assert changes[1]["range"]["end"]["line"] == 2 - assert changes[1]["range"]["end"]["character"] == 2 - - changes = response["documentChanges"] - assert changes[0]["textDocument"]["uri"] == "uri" - assert changes[0]["textDocument"]["version"] == 3 - assert changes[0]["edits"][0]["newText"] == "text3" - assert changes[0]["edits"][0]["range"]["start"]["line"] == 2 + changes = response.changes["uri1"] + assert changes[0].new_text == "text1" + assert changes[0].range.start.line == 0 + assert changes[0].range.start.character == 0 + assert changes[0].range.end.line == 1 + assert changes[0].range.end.character == 1 + + assert changes[1].new_text == "text2" + assert changes[1].range.start.line == 1 + assert changes[1].range.start.character == 1 + assert changes[1].range.end.line == 2 + assert changes[1].range.end.character == 2 + + changes = response.document_changes + assert changes[0].text_document.uri == "uri" + assert changes[0].text_document.version == 3 + assert changes[0].edits[0].new_text == "text3" + assert changes[0].edits[0].range.start.line == 2 assert ( - changes[0]["edits"][0]["range"]["start"]["character"] + changes[0].edits[0].range.start.character == 2 ) - assert changes[0]["edits"][0]["range"]["end"]["line"] == 3 + assert changes[0].edits[0].range.end.line == 3 assert ( - changes[0]["edits"][0]["range"]["end"]["character"] == 3 + changes[0].edits[0].range.end.character == 3 ) - assert changes[1]["kind"] == ResourceOperationKind.Create - assert changes[1]["uri"] == "create file" - assert changes[1]["options"]["ignoreIfExists"] - assert changes[1]["options"]["overwrite"] + assert changes[1].kind == ResourceOperationKind.Create.value + assert changes[1].uri == "create file" + assert changes[1].options.ignore_if_exists + assert changes[1].options.overwrite - assert changes[2]["kind"] == ResourceOperationKind.Rename - assert changes[2]["newUri"] == "rename new uri" - assert changes[2]["oldUri"] == "rename old uri" - assert changes[2]["options"]["ignoreIfExists"] - assert changes[2]["options"]["overwrite"] + assert changes[2].kind == ResourceOperationKind.Rename.value + assert changes[2].new_uri == "rename new uri" + assert changes[2].old_uri == "rename old uri" + assert changes[2].options.ignore_if_exists + assert changes[2].options.overwrite - assert changes[3]["kind"] == ResourceOperationKind.Delete - assert changes[3]["uri"] == "delete file" - assert changes[3]["options"]["ignoreIfExists"] - assert changes[3]["options"]["recursive"] + assert changes[3].kind == ResourceOperationKind.Delete.value + assert changes[3].uri == "delete file" + assert changes[3].options.ignore_if_not_exists + assert changes[3].options.recursive @ConfiguredLS.decorate() def test_rename_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - RENAME, + TEXT_DOCUMENT_RENAME, RenameParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_selection_range.py b/tests/lsp/test_selection_range.py index 1beaea9e..5f669a20 100644 --- a/tests/lsp/test_selection_range.py +++ b/tests/lsp/test_selection_range.py @@ -16,8 +16,8 @@ ############################################################################ from typing import List, Optional -from pygls.lsp.methods import SELECTION_RANGE -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_SELECTION_RANGE +from lsprotocol.types import ( Position, Range, SelectionRange, @@ -34,7 +34,7 @@ def __init__(self): super().__init__() @self.server.feature( - SELECTION_RANGE, + TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeOptions(), ) def f(params: SelectionRangeParams) -> Optional[List[SelectionRange]]: @@ -71,9 +71,9 @@ def test_capabilities(client_server): def test_selection_range_return_list(client_server): client, _ = client_server response = client.lsp.send_request( - SELECTION_RANGE, + TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeParams( - query="query", + # query="query", text_document=TextDocumentIdentifier(uri="file://return.list"), positions=[Position(line=0, character=0)], ), @@ -82,26 +82,26 @@ def test_selection_range_return_list(client_server): assert response root = response[0] - assert root["range"]["start"]["line"] == 0 - assert root["range"]["start"]["character"] == 0 - assert root["range"]["end"]["line"] == 10 - assert root["range"]["end"]["character"] == 10 - assert "parent" not in root + assert root.range.start.line == 0 + assert root.range.start.character == 0 + assert root.range.end.line == 10 + assert root.range.end.character == 10 + assert root.parent is None - assert response[1]["range"]["start"]["line"] == 0 - assert response[1]["range"]["start"]["character"] == 0 - assert response[1]["range"]["end"]["line"] == 1 - assert response[1]["range"]["end"]["character"] == 1 - assert response[1]["parent"] == root + assert response[1].range.start.line == 0 + assert response[1].range.start.character == 0 + assert response[1].range.end.line == 1 + assert response[1].range.end.character == 1 + assert response[1].parent == root @ConfiguredLS.decorate() def test_selection_range_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - SELECTION_RANGE, + TEXT_DOCUMENT_SELECTION_RANGE, SelectionRangeParams( - query="query", + # query="query", text_document=TextDocumentIdentifier(uri="file://return.none"), positions=[Position(line=0, character=0)], ), diff --git a/tests/lsp/test_signature_help.py b/tests/lsp/test_signature_help.py index 350ea362..3c3292c4 100644 --- a/tests/lsp/test_signature_help.py +++ b/tests/lsp/test_signature_help.py @@ -17,8 +17,10 @@ from typing import Optional -from pygls.lsp.methods import SIGNATURE_HELP -from pygls.lsp.types import ( +import pytest + +from lsprotocol.types import TEXT_DOCUMENT_SIGNATURE_HELP +from lsprotocol.types import ( ParameterInformation, Position, SignatureHelp, @@ -38,7 +40,7 @@ def __init__(self): super().__init__() @self.server.feature( - SIGNATURE_HELP, + TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpOptions( trigger_characters=["a", "b"], retrigger_characters=["c", "d"], @@ -78,10 +80,11 @@ def test_capabilities(client_server): @ConfiguredLS.decorate() +@pytest.mark.skip def test_signature_help_return_signature_help(client_server): client, _ = client_server response = client.lsp.send_request( - SIGNATURE_HELP, + TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpParams( text_document=TextDocumentIdentifier( uri="file://return.signature_help" @@ -126,10 +129,11 @@ def test_signature_help_return_signature_help(client_server): @ConfiguredLS.decorate() +@pytest.mark.skip def test_signature_help_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - SIGNATURE_HELP, + TEXT_DOCUMENT_SIGNATURE_HELP, SignatureHelpParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/lsp/test_type_definition.py b/tests/lsp/test_type_definition.py index 37e1d1e6..da437b63 100644 --- a/tests/lsp/test_type_definition.py +++ b/tests/lsp/test_type_definition.py @@ -16,8 +16,8 @@ ############################################################################ from typing import List, Optional, Union -from pygls.lsp.methods import TYPE_DEFINITION -from pygls.lsp.types import ( +from lsprotocol.types import TEXT_DOCUMENT_TYPE_DEFINITION +from lsprotocol.types import ( Location, LocationLink, Position, @@ -35,7 +35,7 @@ def __init__(self): super().__init__() @self.server.feature( - TYPE_DEFINITION, + TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionOptions(), ) def f( @@ -84,7 +84,7 @@ def test_capabilities(client_server): def test_type_definition_return_location(client_server): client, _ = client_server response = client.lsp.send_request( - TYPE_DEFINITION, + TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location"), @@ -92,19 +92,19 @@ def test_type_definition_return_location(client_server): ), ).result() - assert response["uri"] == "uri" + assert response.uri == "uri" - assert response["range"]["start"]["line"] == 0 - assert response["range"]["start"]["character"] == 0 - assert response["range"]["end"]["line"] == 1 - assert response["range"]["end"]["character"] == 1 + assert response.range.start.line == 0 + assert response.range.start.character == 0 + assert response.range.end.line == 1 + assert response.range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_list(client_server): client, _ = client_server response = client.lsp.send_request( - TYPE_DEFINITION, + TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_list"), @@ -112,19 +112,19 @@ def test_type_definition_return_location_list(client_server): ), ).result() - assert response[0]["uri"] == "uri" + assert response[0].uri == "uri" - assert response[0]["range"]["start"]["line"] == 0 - assert response[0]["range"]["start"]["character"] == 0 - assert response[0]["range"]["end"]["line"] == 1 - assert response[0]["range"]["end"]["character"] == 1 + assert response[0].range.start.line == 0 + assert response[0].range.start.character == 0 + assert response[0].range.end.line == 1 + assert response[0].range.end.character == 1 @ConfiguredLS.decorate() def test_type_definition_return_location_link_list(client_server): client, _ = client_server response = client.lsp.send_request( - TYPE_DEFINITION, + TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier( uri="file://return.location_link_list" @@ -133,29 +133,29 @@ def test_type_definition_return_location_link_list(client_server): ), ).result() - assert response[0]["targetUri"] == "uri" + assert response[0].target_uri == "uri" - assert response[0]["targetRange"]["start"]["line"] == 0 - assert response[0]["targetRange"]["start"]["character"] == 0 - assert response[0]["targetRange"]["end"]["line"] == 1 - assert response[0]["targetRange"]["end"]["character"] == 1 + assert response[0].target_range.start.line == 0 + assert response[0].target_range.start.character == 0 + assert response[0].target_range.end.line == 1 + assert response[0].target_range.end.character == 1 - assert response[0]["targetSelectionRange"]["start"]["line"] == 0 - assert response[0]["targetSelectionRange"]["start"]["character"] == 0 - assert response[0]["targetSelectionRange"]["end"]["line"] == 2 - assert response[0]["targetSelectionRange"]["end"]["character"] == 2 + assert response[0].target_selection_range.start.line == 0 + assert response[0].target_selection_range.start.character == 0 + assert response[0].target_selection_range.end.line == 2 + assert response[0].target_selection_range.end.character == 2 - assert response[0]["originSelectionRange"]["start"]["line"] == 0 - assert response[0]["originSelectionRange"]["start"]["character"] == 0 - assert response[0]["originSelectionRange"]["end"]["line"] == 3 - assert response[0]["originSelectionRange"]["end"]["character"] == 3 + assert response[0].origin_selection_range.start.line == 0 + assert response[0].origin_selection_range.start.character == 0 + assert response[0].origin_selection_range.end.line == 3 + assert response[0].origin_selection_range.end.character == 3 @ConfiguredLS.decorate() def test_type_definition_return_none(client_server): client, _ = client_server response = client.lsp.send_request( - TYPE_DEFINITION, + TEXT_DOCUMENT_TYPE_DEFINITION, TypeDefinitionParams( text_document=TextDocumentIdentifier(uri="file://return.none"), position=Position(line=0, character=0), diff --git a/tests/test_document.py b/tests/test_document.py index 046ea7c7..5c3c36b8 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -18,10 +18,10 @@ ############################################################################ import re -from pygls.lsp.types import ( +from lsprotocol.types import ( Position, Range, - TextDocumentContentChangeEvent, + TextDocumentContentChangeEvent_Type1, TextDocumentSyncKind, ) from pygls.workspace import ( @@ -36,7 +36,7 @@ def test_document_empty_edit(): doc = Document("file:///uri", "") - change = TextDocumentContentChangeEvent( + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=0, character=0), end=Position(line=0, character=0) @@ -52,7 +52,7 @@ def test_document_end_of_file_edit(): old = ["print 'a'\n", "print 'b'\n"] doc = Document("file:///uri", "".join(old)) - change = TextDocumentContentChangeEvent( + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=2, character=0), end=Position(line=2, character=0) @@ -72,8 +72,8 @@ def test_document_end_of_file_edit(): def test_document_full_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.FULL) - change = TextDocumentContentChangeEvent( + sync_kind=TextDocumentSyncKind.Full) + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=1, character=4), end=Position(line=2, character=11) @@ -86,8 +86,8 @@ def test_document_full_edit(): assert doc.lines == ["print a, b"] doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.FULL) - change = TextDocumentContentChangeEvent(range=None, text="print a, b") + sync_kind=TextDocumentSyncKind.Full) + change = TextDocumentContentChangeEvent_Type1(range=None, text="print a, b") doc.apply_change(change) assert doc.lines == ["print a, b"] @@ -95,7 +95,7 @@ def test_document_full_edit(): def test_document_line_edit(): doc = Document("file:///uri", "itshelloworld") - change = TextDocumentContentChangeEvent( + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=0, character=3), end=Position(line=0, character=8) @@ -115,9 +115,9 @@ def test_document_lines(doc): def test_document_multiline_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document( - "file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.INCREMENTAL + "file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.Incremental ) - change = TextDocumentContentChangeEvent( + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=1, character=4), end=Position(line=2, character=11) @@ -130,9 +130,9 @@ def test_document_multiline_edit(): assert doc.lines == ["def hello(a, b):\n", " print a, b\n"] doc = Document( - "file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.INCREMENTAL + "file:///uri", "".join(old), sync_kind=TextDocumentSyncKind.Incremental ) - change = TextDocumentContentChangeEvent( + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=1, character=4), end=Position(line=2, character=11) @@ -147,8 +147,8 @@ def test_document_multiline_edit(): def test_document_no_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document("file:///uri", "".join(old), - sync_kind=TextDocumentSyncKind.NONE) - change = TextDocumentContentChangeEvent( + sync_kind=TextDocumentSyncKind.None_) + change = TextDocumentContentChangeEvent_Type1( range=Range( start=Position(line=1, character=4), end=Position(line=2, character=11) diff --git a/tests/test_feature_manager.py b/tests/test_feature_manager.py index 764b3e86..020bf6bf 100644 --- a/tests/test_feature_manager.py +++ b/tests/test_feature_manager.py @@ -23,8 +23,7 @@ ValidationError, ) from pygls.feature_manager import has_ls_param_or_annotation, wrap_with_server -from pygls.lsp import methods -from pygls.lsp.types import CompletionOptions +from lsprotocol import types def test_has_ls_param_or_annotation(): @@ -72,20 +71,20 @@ def cmd2(): def test_register_feature_with_valid_options(feature_manager): - options = CompletionOptions(trigger_characters=["!"]) + options = types.CompletionOptions(trigger_characters=["!"]) - @feature_manager.feature(methods.COMPLETION, options) + @feature_manager.feature(types.TEXT_DOCUMENT_COMPLETION, options) def completions(): pass reg_features = feature_manager.features.keys() reg_feature_options = feature_manager.feature_options.keys() - assert methods.COMPLETION in reg_features - assert methods.COMPLETION in reg_feature_options + assert types.TEXT_DOCUMENT_COMPLETION in reg_features + assert types.TEXT_DOCUMENT_COMPLETION in reg_feature_options - assert feature_manager.features[methods.COMPLETION] is completions - assert feature_manager.feature_options[methods.COMPLETION] is options + assert feature_manager.features[types.TEXT_DOCUMENT_COMPLETION] is completions + assert feature_manager.feature_options[types.TEXT_DOCUMENT_COMPLETION] is options def test_register_feature_with_wrong_options(feature_manager): @@ -95,32 +94,32 @@ class Options: with pytest.raises( TypeError, match=( - f'Options of method "{methods.COMPLETION}" should be instance of type ' + f'Options of method "{types.TEXT_DOCUMENT_COMPLETION}" should be instance of type ' "" ), # noqa ): - @feature_manager.feature(methods.COMPLETION, Options()) + @feature_manager.feature(types.TEXT_DOCUMENT_COMPLETION, Options()) def completions(): pass def test_register_features(feature_manager): - @feature_manager.feature(methods.COMPLETION) + @feature_manager.feature(types.TEXT_DOCUMENT_COMPLETION) def completions(): pass - @feature_manager.feature(methods.CODE_LENS) + @feature_manager.feature(types.TEXT_DOCUMENT_CODE_LENS) def code_lens(): pass reg_features = feature_manager.features.keys() - assert methods.COMPLETION in reg_features - assert methods.CODE_LENS in reg_features + assert types.TEXT_DOCUMENT_COMPLETION in reg_features + assert types.TEXT_DOCUMENT_CODE_LENS in reg_features - assert feature_manager.features[methods.COMPLETION] is completions - assert feature_manager.features[methods.CODE_LENS] is code_lens + assert feature_manager.features[types.TEXT_DOCUMENT_COMPLETION] is completions + assert feature_manager.features[types.TEXT_DOCUMENT_CODE_LENS] is code_lens def test_register_same_command_twice_error(feature_manager): @@ -140,11 +139,11 @@ def test_register_same_feature_twice_error(feature_manager): with pytest.raises(FeatureAlreadyRegisteredError): - @feature_manager.feature(methods.CODE_ACTION) + @feature_manager.feature(types.TEXT_DOCUMENT_CODE_ACTION) def code_action1(): # pylint: disable=unused-variable pass - @feature_manager.feature(methods.CODE_ACTION) + @feature_manager.feature(types.TEXT_DOCUMENT_CODE_ACTION) def code_action2(): # pylint: disable=unused-variable pass diff --git a/tests/test_language_server.py b/tests/test_language_server.py index 591d340d..16a5854e 100644 --- a/tests/test_language_server.py +++ b/tests/test_language_server.py @@ -20,12 +20,12 @@ import pytest from pygls import IS_PYODIDE -from pygls.lsp.methods import ( +from lsprotocol.types import ( INITIALIZE, TEXT_DOCUMENT_DID_OPEN, WORKSPACE_EXECUTE_COMMAND, ) -from pygls.lsp.types import ( +from lsprotocol.types import ( ClientCapabilities, DidOpenTextDocumentParams, ExecuteCommandParams, @@ -54,16 +54,16 @@ def test_bf_initialize(client_server): response = client.lsp.send_request( INITIALIZE, - { - "processId": process_id, - "rootUri": root_uri, - "capabilities": ClientCapabilities(), - }, + InitializeParams( + process_id=process_id, + root_uri=root_uri, + capabilities=ClientCapabilities(), + ), ).result() assert server.process_id == process_id assert server.workspace.root_uri == root_uri - assert "capabilities" in response + assert response.capabilities is not None def test_bf_text_document_did_open(client_server): diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 54c4529f..860a6bbb 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -17,97 +17,120 @@ import io import json from concurrent.futures import Future -from functools import partial from pathlib import Path from typing import Optional from unittest.mock import Mock +import attrs import pytest from pygls.exceptions import JsonRpcException, JsonRpcInvalidParams -from pygls.lsp import Model, get_method_params_type -from pygls.lsp.types import ( +from lsprotocol.types import ( ClientCapabilities, CompletionItem, CompletionItemKind, InitializeParams, InitializeResult, ProgressParams, + ShutdownResponse, + TextDocumentCompletionResponse, WorkDoneProgressBegin, ) -from pygls.protocol import ( - JsonRPCNotification, - JsonRPCProtocol, - JsonRPCRequestMessage, - JsonRPCResponseMessage, -) -from pygls.protocol import deserialize_message as _deserialize_message +from pygls.protocol import JsonRPCProtocol, JsonRPCRequestMessage + +EXAMPLE_NOTIFICATION = "example/notification" +EXAMPLE_REQUEST = "example/request" -TEST_METHOD = "test_method" +@attrs.define +class IntResult: + id: str + result: int + jsonrpc: str = attrs.field(default='2.0') -class FeatureParams(Model): - class InnerType(Model): +@attrs.define +class ExampleParams: + @attrs.define + class InnerType: inner_field: str field_a: str field_b: Optional[InnerType] = None -TEST_LSP_METHODS_MAP = { - TEST_METHOD: (None, FeatureParams, None), +@attrs.define +class ExampleNotification: + jsonrpc: str = attrs.field(default='2.0') + method: str = EXAMPLE_NOTIFICATION + params: ExampleParams = attrs.field(default=None) + +@attrs.define +class ExampleRequest: + id: str + jsonrpc: str = attrs.field(default='2.0') + method: str = EXAMPLE_REQUEST + params: ExampleParams = attrs.field(default=None) + + +EXAMPLE_LSP_METHODS_MAP = { + EXAMPLE_NOTIFICATION: (ExampleNotification, None, ExampleParams, None), + EXAMPLE_REQUEST: (ExampleRequest, None, ExampleParams, None), } -deserialize_message = partial( - _deserialize_message, - get_params_type=partial( - get_method_params_type, lsp_methods_map=TEST_LSP_METHODS_MAP - ), -) +class ExampleProtocol(JsonRPCProtocol): + def get_message_type(self, method: str): + return EXAMPLE_LSP_METHODS_MAP.get(method, (None,))[0] -def test_deserialize_notification_message_valid_params(): - params = """ - { + +@pytest.fixture() +def protocol(): + return ExampleProtocol(None) + + +def test_deserialize_notification_message_valid_params(protocol): + params = f""" + {{ "jsonrpc": "2.0", - "method": "test_method", - "params": { - "field_a": "test_a", - "field_b": { - "inner_field": "test_inner" - } - } - } + "method": "{EXAMPLE_NOTIFICATION}", + "params": {{ + "fieldA": "test_a", + "fieldB": {{ + "innerField": "test_inner" + }} + }} + }} """ - result = json.loads(params, object_hook=deserialize_message) + result = json.loads(params, object_hook=protocol._deserialize_message) - assert isinstance(result, JsonRPCNotification) + assert isinstance(result, ExampleNotification), f"Expected FeatureRequest instance, got {result}" assert result.jsonrpc == "2.0" + assert result.method == EXAMPLE_NOTIFICATION - assert isinstance(result.params, FeatureParams) + assert isinstance(result.params, ExampleParams) assert result.params.field_a == "test_a" - assert isinstance(result.params.field_b, FeatureParams.InnerType) + assert isinstance(result.params.field_b, ExampleParams.InnerType) assert result.params.field_b.inner_field == "test_inner" -def test_deserialize_notification_message_bad_params_should_raise_error(): - params = """ - { +def test_deserialize_notification_message_bad_params_should_raise_error(protocol): + params = f""" + {{ "jsonrpc": "2.0", - "method": "test_method", - "params": { + "method": "{EXAMPLE_NOTIFICATION}", + "params": {{ "field_a": "test_a", - "field_b": { + "field_b": {{ "wrong_field_name": "test_inner" - } - } - } + }} + }} + }} """ with pytest.raises(JsonRpcInvalidParams): - json.loads(params, object_hook=deserialize_message) + json.loads(params, object_hook=protocol._deserialize_message) @pytest.mark.parametrize( @@ -117,6 +140,7 @@ def test_deserialize_notification_message_bad_params_should_raise_error(): ProgressParams( token="id1", value=WorkDoneProgressBegin( + kind='begin', title="Begin progress", percentage=0, ), @@ -154,7 +178,7 @@ def test_serialize_notification_message(params, expected): assert actual == expected -def test_deserialize_response_message(): +def test_deserialize_response_message(protocol): params = """ { "jsonrpc": "2.0", @@ -162,43 +186,44 @@ def test_deserialize_response_message(): "result": "1" } """ - result = json.loads(params, object_hook=deserialize_message) + protocol._result_types["id"] = IntResult + result = json.loads(params, object_hook=protocol._deserialize_message) - assert isinstance(result, JsonRPCResponseMessage) + assert isinstance(result, IntResult) assert result.jsonrpc == "2.0" assert result.id == "id" - assert result.result == "1" - assert result.error is None + assert result.result == 1 -def test_deserialize_request_message_with_registered_type(): - params = """ - { +def test_deserialize_request_message_with_registered_type(protocol): + params = f""" + {{ "jsonrpc": "2.0", "id": "id", - "method": "test_method", - "params": { - "field_a": "test_a", - "field_b": { - "inner_field": "test_inner" - } - } - } + "method": "{EXAMPLE_REQUEST}", + "params": {{ + "fieldA": "test_a", + "fieldB": {{ + "innerField": "test_inner" + }} + }} + }} """ - result = json.loads(params, object_hook=deserialize_message) + result = json.loads(params, object_hook=protocol._deserialize_message) - assert isinstance(result, JsonRPCRequestMessage) + assert isinstance(result, ExampleRequest) assert result.jsonrpc == "2.0" assert result.id == "id" + assert result.method == EXAMPLE_REQUEST - assert isinstance(result.params, FeatureParams) + assert isinstance(result.params, ExampleParams) assert result.params.field_a == "test_a" - assert isinstance(result.params.field_b, FeatureParams.InnerType) + assert isinstance(result.params.field_b, ExampleParams.InnerType) assert result.params.field_b.inner_field == "test_inner" -def test_deserialize_request_message_without_registered_type(): +def test_deserialize_request_message_without_registered_type(protocol): params = """ { "jsonrpc": "2.0", @@ -212,22 +237,23 @@ def test_deserialize_request_message_without_registered_type(): } } """ - result = json.loads(params, object_hook=deserialize_message) + result = json.loads(params, object_hook=protocol._deserialize_message) assert isinstance(result, JsonRPCRequestMessage) assert result.jsonrpc == "2.0" assert result.id == "id" + assert result.method == "random" - assert type(result.params).__name__ == "Object" assert result.params.field_a == "test_a" assert result.params.field_b.inner_field == "test_inner" @pytest.mark.parametrize( - "result, expected", + "msg_type, result, expected", [ - (None, {"jsonrpc": "2.0", "id": "1", "result": None}), + (ShutdownResponse, None, {"jsonrpc": "2.0", "id": "1", "result": None}), ( + TextDocumentCompletionResponse, [ CompletionItem(label="example-one"), CompletionItem( @@ -253,7 +279,7 @@ def test_deserialize_request_message_without_registered_type(): ), ], ) -def test_serialize_response_message(result, expected): +def test_serialize_response_message(msg_type, result, expected): """ Ensure that we can serialize response messages, retaining all expected fields. @@ -265,6 +291,8 @@ def test_serialize_response_message(result, expected): protocol._send_only_body = True protocol.connection_made(buffer) + protocol._result_types["1"] = msg_type + protocol._send_response("1", result=result) actual = json.loads(buffer.getvalue()) @@ -372,7 +400,7 @@ def test_data_received_error_should_raise_jsonrpc_error(client_server): body, ] ).encode("utf-8") - future = server.lsp._server_request_futures["err"] = Future() + future = server.lsp._request_futures["err"] = Future() server.lsp.data_received(message) with pytest.raises(JsonRpcException, match="message for you sir"): future.result() diff --git a/tests/test_types.py b/tests/test_types.py index 317fa397..b11c93ae 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -16,7 +16,7 @@ # See the License for the specific language governing permissions and # # limitations under the License. # ############################################################################ -from pygls.lsp.types import Location, Position, Range +from lsprotocol.types import Location, Position, Range def test_position(): diff --git a/tests/test_workspace.py b/tests/test_workspace.py index d0e47de0..4d7051f3 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -19,7 +19,7 @@ import os from pygls import uris -from pygls.lsp.types import TextDocumentItem, WorkspaceFolder +from lsprotocol.types import TextDocumentItem, WorkspaceFolder from pygls.workspace import Workspace DOC_URI = uris.from_fs_path(__file__) From 61137981c8d3f9008ae67ad5ef1e68639551218c Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Fri, 14 Oct 2022 18:02:26 +0100 Subject: [PATCH 07/20] Make it possible to pick browser used for pyodide tests It's now possible to select which browser is used to run the testsuite by setting the `BROWSER` environment variable e.g. BROWSER=firefox python pyodide_testrunner/run.py If no variable is found, the script will default to use Chrome. --- pyodide_testrunner/run.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pyodide_testrunner/run.py b/pyodide_testrunner/run.py index a426784e..26ac2afd 100644 --- a/pyodide_testrunner/run.py +++ b/pyodide_testrunner/run.py @@ -18,6 +18,10 @@ # Path to the root of the repo. REPO = pathlib.Path(__file__).parent.parent +BROWSERS = { + "chrome": (webdriver.Chrome, webdriver.ChromeOptions), + "firefox": (webdriver.Firefox, webdriver.FirefoxOptions), +} def build_wheel() -> str: @@ -82,10 +86,12 @@ def main(): print("Running tests...") try: - options = webdriver.ChromeOptions() + driver_cls, options_cls = BROWSERS[os.environ.get('BROWSER', 'chrome')] + + options = options_cls() options.headless = 'CI' in os.environ - driver = webdriver.Chrome(options=options) + driver = driver_cls(options=options) driver.get(f'http://localhost:{port}?whl={whl}') wait = WebDriverWait(driver, 120) From 345eeb7beda71a5ac4ed9befc57e1f4a6277bab6 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sun, 16 Oct 2022 18:49:27 +0100 Subject: [PATCH 08/20] Add some tests around `ClientCapabilities` As far as I can tell, there are no tests that depend on any of the values contained within `ClientCapabilities`. This commit adds some tests around the construction of the `TextDocumentSyncOptions` field for the server's capabilities. It also fixes a bug that was introduced in the previous commit --- pygls/capabilities.py | 3 +- tests/test_capabilities.py | 136 +++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 tests/test_capabilities.py diff --git a/pygls/capabilities.py b/pygls/capabilities.py index 030e1ac7..fa2d3240 100644 --- a/pygls/capabilities.py +++ b/pygls/capabilities.py @@ -62,8 +62,7 @@ def get_capability( return default # If we reach the desired leaf value but it's None, return the default. - value = default if value is None else value - return + return default if value is None else value class ServerCapabilitiesBuilder: diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py new file mode 100644 index 00000000..cb69027a --- /dev/null +++ b/tests/test_capabilities.py @@ -0,0 +1,136 @@ +from typing import Set + +import pytest +from lsprotocol.types import ( + TEXT_DOCUMENT_DID_OPEN, + TEXT_DOCUMENT_DID_CLOSE, + TEXT_DOCUMENT_DID_SAVE, + TEXT_DOCUMENT_WILL_SAVE, + TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, + ClientCapabilities, + SaveOptions, + TextDocumentClientCapabilities, + TextDocumentSaveRegistrationOptions, + TextDocumentSyncClientCapabilities, + TextDocumentSyncKind, + TextDocumentSyncOptions, +) + +from pygls.capabilities import ServerCapabilitiesBuilder + + +@pytest.mark.parametrize("capabilities,features,options,expected", [ + + # textDocument/didOpen + ( + ClientCapabilities(), + set(), + {}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=False, + ) + ), + ( + ClientCapabilities(), + {TEXT_DOCUMENT_DID_OPEN}, + {}, + TextDocumentSyncOptions( + open_close=True, + change=TextDocumentSyncKind.Incremental, + save=False, + ) + ), + ( + ClientCapabilities(), + {TEXT_DOCUMENT_DID_CLOSE}, + {}, + TextDocumentSyncOptions( + open_close=True, + change=TextDocumentSyncKind.Incremental, + save=False, + ) + ), + # textDocument/willSave & textDocument/willSaveWaitUntil + ( + ClientCapabilities(), + {TEXT_DOCUMENT_WILL_SAVE}, + {}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=False, + ) + ), + ( + ClientCapabilities( + text_document=TextDocumentClientCapabilities( + synchronization=TextDocumentSyncClientCapabilities( + will_save=True + ) + ) + ), + {TEXT_DOCUMENT_WILL_SAVE}, + {}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=False, + will_save=True + ) + ), + ( + ClientCapabilities( + text_document=TextDocumentClientCapabilities( + synchronization=TextDocumentSyncClientCapabilities( + will_save_wait_until=True + ) + ) + ), + {TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL}, + {}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=False, + will_save_wait_until=True + ) + ), + # textDocument/didSave + ( + ClientCapabilities(), + {TEXT_DOCUMENT_DID_SAVE}, + {}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=True, + ) + ), + ( + ClientCapabilities(), + {TEXT_DOCUMENT_DID_SAVE}, + {TEXT_DOCUMENT_DID_SAVE: TextDocumentSaveRegistrationOptions(include_text=True)}, + TextDocumentSyncOptions( + open_close=False, + change=TextDocumentSyncKind.Incremental, + save=SaveOptions(include_text=True), + ) + ) +]) +def test_text_doc_sync_capabilities( + capabilities: ClientCapabilities, + features: Set[str], + options, + expected: TextDocumentSyncOptions +): + """Ensure that `pygls` can correctly construct server capabilities for the + text document sync features.""" + + builder = ServerCapabilitiesBuilder( + capabilities, features, options, [], TextDocumentSyncKind.Incremental + ) + + actual = builder.build().text_document_sync + assert expected == actual From 0cabe1b81b42d400249f5dc56eba2248af4a8b00 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Mon, 7 Nov 2022 23:47:21 +0000 Subject: [PATCH 09/20] Fix mypy errors --- pygls/workspace.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pygls/workspace.py b/pygls/workspace.py index dda2a0b0..bfd5aa5a 100644 --- a/pygls/workspace.py +++ b/pygls/workspace.py @@ -24,6 +24,7 @@ from lsprotocol.types import ( Position, Range, TextDocumentContentChangeEvent, + TextDocumentContentChangeEvent_Type1, TextDocumentItem, TextDocumentSyncKind, VersionedTextDocumentIdentifier, WorkspaceFolder ) @@ -187,7 +188,7 @@ def __init__( def __str__(self): return str(self.uri) - def _apply_incremental_change(self, change: TextDocumentContentChangeEvent) -> None: + def _apply_incremental_change(self, change: TextDocumentContentChangeEvent_Type1) -> None: """Apply an ``Incremental`` text change to the document""" lines = self.lines text = change.text @@ -251,10 +252,8 @@ def apply_change(self, change: TextDocumentContentChangeEvent) -> None: attributes "range" and "rangeLength" will be missing from ``Full`` content update client requests in the pygls Python library. - NOTE: After adding pydantic models, "range" and "rangeLength" fileds - will be None if not passed by the client """ - if change.range is not None: + if isinstance(change, TextDocumentContentChangeEvent_Type1): if self._is_sync_kind_incremental: self._apply_incremental_change(change) return From 4193f0ee05366f447bdb31f7fe79fdd8e470ce43 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Mon, 7 Nov 2022 23:47:45 +0000 Subject: [PATCH 10/20] Fix test case --- tests/lsp/test_code_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lsp/test_code_action.py b/tests/lsp/test_code_action.py index 033ba5ed..2b34e63a 100644 --- a/tests/lsp/test_code_action.py +++ b/tests/lsp/test_code_action.py @@ -96,7 +96,7 @@ def test_code_action_return_list(client_server): assert response[0].title == "action1" assert response[1].title == "action2" - assert response[1].kind == CodeActionKind.Refactor + assert response[1].kind == CodeActionKind.Refactor.value assert response[2].title == "cmd1" assert response[2].command == "cmd1" assert response[2].arguments == [1, "two"] From 791aa91dca5f0a103bd829420efe5b8ab490a69a Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Tue, 8 Nov 2022 23:37:37 +0000 Subject: [PATCH 11/20] Prevent `pygls` from corrupting unknown message types This removes the `attrs.field` definitions from the `JsonRPCResponseMessage`, `JsonRPCRequestMessage` and `JsonRPCNotification` types. This prevents any dictionaries passed to the `result` or `params` fields from being corrupted during serialization. To preserve the "converting dictionaries to objects" behavior, this commit extends the converter we get from `lsprotocol` to include structure hooks that calls `dict_to_object` on the required fields during parsing. Finally, the module level converter has been removed in favour of a converter attached to the `JsonRPCProtocol` object in preparation for allowing servers to provide their own custom converters. --- pygls/protocol.py | 118 +++++++++++++-------- tests/test_protocol.py | 227 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 299 insertions(+), 46 deletions(-) diff --git a/pygls/protocol.py b/pygls/protocol.py index 9715d7dc..a9a90e17 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -69,7 +69,6 @@ from pygls.workspace import Workspace logger = logging.getLogger(__name__) -converter = converters.get_converter() def call_user_feature(base_func, method_name): @@ -94,22 +93,6 @@ def decorator(self, *args, **kwargs): return decorator -def dict_to_object(d: Any): - """Create nested objects (namedtuple) from dict.""" - - if d is None: - return None - - if not isinstance(d, dict): - return d - - type_name = d.pop('type_name', 'Object') - return json.loads( - json.dumps(d), - object_hook=lambda p: namedtuple(type_name, p.keys(), rename=True)(*p.values()) - ) - - @attrs.define class JsonRPCNotification: """A class that represents a generic json rpc notification message. @@ -118,7 +101,7 @@ class JsonRPCNotification: method: str jsonrpc: str - params: Any = attrs.field(converter=dict_to_object) + params: Any @attrs.define @@ -130,7 +113,7 @@ class JsonRPCRequestMessage: id: Union[int, str] method: str jsonrpc: str - params: Any = attrs.field(converter=dict_to_object) + params: Any @attrs.define @@ -141,20 +124,56 @@ class JsonRPCResponseMessage: id: Union[int, str] jsonrpc: str - result: Any = attrs.field(converter=dict_to_object) + result: Any + + +def _dict_to_object(d: Any): + """Create nested objects (namedtuple) from dict.""" + + if d is None: + return None + + if not isinstance(d, dict): + return d + + type_name = d.pop('type_name', 'Object') + return json.loads( + json.dumps(d), + object_hook=lambda p: namedtuple(type_name, p.keys(), rename=True)(*p.values()) + ) + + +def _params_field_structure_hook(obj, cls): + if 'params' in obj: + obj['params'] = _dict_to_object(obj['params']) + return cls(**obj) -def default_serializer(o): - """JSON serializer for complex objects that do not extend pydantic BaseModel class.""" - if hasattr(o, '__attrs_attrs__'): - return converter.unstructure(o) +def _result_field_structure_hook(obj, cls): + if 'result' in obj: + obj['result'] = _dict_to_object(obj['result']) - if isinstance(o, enum.Enum): - return o.value + return cls(**obj) - return o.__dict__ +def default_converter(): + """Default converter factory function.""" + + converter = converters.get_converter() + converter.register_structure_hook( + JsonRPCRequestMessage, _params_field_structure_hook + ) + + converter.register_structure_hook( + JsonRPCResponseMessage, _result_field_structure_hook + ) + + converter.register_structure_hook( + JsonRPCNotification, _params_field_structure_hook + ) + + return converter class JsonRPCProtocol(asyncio.Protocol): """Json RPC protocol implementation using on top of `asyncio.Protocol`. @@ -190,6 +209,7 @@ def __init__(self, server): self._message_buf = [] self._send_only_body = False + self._converter = default_converter() def __call__(self): return self @@ -351,6 +371,17 @@ def _handle_response(self, msg_id, result=None, error=None): logger.debug('Received result for message "%s": %s', msg_id, result) future.set_result(result) + def _serialize_message(self, data): + """Function used to serialize data sent to the client.""" + + if hasattr(data, '__attrs_attrs__'): + return self._converter.unstructure(data) + + if isinstance(data, enum.Enum): + return data.value + + return data.__dict__ + def _deserialize_message(self, data): """Function used to deserialize data recevied from the client.""" @@ -360,22 +391,22 @@ def _deserialize_message(self, data): try: if 'id' in data: if 'error' in data: - return converter.structure(data, ResponseErrorMessage) + return self._converter.structure(data, ResponseErrorMessage) elif 'method' in data: request_type = ( self.get_message_type(data['method']) or JsonRPCRequestMessage ) - return converter.structure(data, request_type) + return self._converter.structure(data, request_type) else: response_type = ( self._result_types.pop(data['id']) or JsonRPCResponseMessage ) - return converter.structure(data, response_type) + return self._converter.structure(data, response_type) else: method = data.get('method', '') notification_type = self.get_message_type(method) or JsonRPCNotification - return converter.structure(data, notification_type) + return self._converter.structure(data, notification_type) except ClassValidationError as exc: logger.error("Unable to deserialize message\n%s", traceback.format_exc()) @@ -385,8 +416,6 @@ def _deserialize_message(self, data): logger.error("Unable to deserialize message\n%s", traceback.format_exc()) raise JsonRpcInternalError() from exc - return data - def _procedure_handler(self, message): """Delegates message to handlers depending on message type.""" @@ -417,8 +446,13 @@ def _send_data(self, data): """Sends data to the client.""" if not data: return + + if self.transport is None: + logger.error("Unable to send data, no available transport!") + return + try: - body = json.dumps(data, default=default_serializer) + body = json.dumps(data, default=self._serialize_message) logger.info('Sending data: %s', body) body = body.encode(self.CHARSET) @@ -522,10 +556,10 @@ def notify(self, method: str, params=None): params=params, jsonrpc=JsonRPCProtocol.VERSION ) - + # breakpoint() self._send_data(notification) - def send_request(self, method, params=None, callback=None): + def send_request(self, method, params=None, callback=None, msg_id=None): """Sends a JSON RPC request to the client. Args: @@ -536,7 +570,9 @@ def send_request(self, method, params=None, callback=None): Future that will be resolved once a response has been received """ - msg_id = str(uuid.uuid4()) + if msg_id is None: + msg_id = str(uuid.uuid4()) + request_type = self.get_message_type(method) or JsonRPCRequestMessage logger.debug('Sending request with id "%s": %s %s', msg_id, method, params) @@ -662,7 +698,9 @@ def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> WorkspaceApplyEd @lsp_method(EXIT) def lsp_exit(self, *args) -> None: """Stops the server process.""" - self.transport.close() + if self.transport is not None: + self.transport.close() + sys.exit(0 if self._shutdown else 1) @lsp_method(INITIALIZE) @@ -686,7 +724,7 @@ def lsp_initialize(self, params: InitializeParams) -> InitializeResult: ).build() logger.debug( 'Server capabilities: %s', - json.dumps(self.server_capabilities, default=default_serializer) + json.dumps(self.server_capabilities, default=self._serialize_message) ) root_path = params.root_path @@ -791,7 +829,7 @@ def log_trace(self, message: str, verbose: Optional[str] = None) -> None: return params = LogTraceParams(message=message) - if verbose and self.trace == TraceValues.VERBOSE: + if verbose and self.trace == TraceValues.Verbose: params.verbose = verbose self.notify(LOG_TRACE, params) diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 860a6bbb..8f1b8bf3 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -26,17 +26,27 @@ from pygls.exceptions import JsonRpcException, JsonRpcInvalidParams from lsprotocol.types import ( + PROGRESS, + TEXT_DOCUMENT_COMPLETION, ClientCapabilities, CompletionItem, CompletionItemKind, + CompletionParams, InitializeParams, InitializeResult, ProgressParams, + Position, ShutdownResponse, TextDocumentCompletionResponse, + TextDocumentIdentifier, WorkDoneProgressBegin, ) -from pygls.protocol import JsonRPCProtocol, JsonRPCRequestMessage +from pygls.protocol import ( + JsonRPCProtocol, + JsonRPCRequestMessage, + JsonRPCResponseMessage, + JsonRPCNotification +) EXAMPLE_NOTIFICATION = "example/notification" EXAMPLE_REQUEST = "example/request" @@ -48,6 +58,7 @@ class IntResult: result: int jsonrpc: str = attrs.field(default='2.0') + @attrs.define class ExampleParams: @attrs.define @@ -64,6 +75,7 @@ class ExampleNotification: method: str = EXAMPLE_NOTIFICATION params: ExampleParams = attrs.field(default=None) + @attrs.define class ExampleRequest: id: str @@ -77,6 +89,7 @@ class ExampleRequest: EXAMPLE_REQUEST: (ExampleRequest, None, ExampleParams, None), } + class ExampleProtocol(JsonRPCProtocol): def get_message_type(self, method: str): @@ -115,6 +128,30 @@ def test_deserialize_notification_message_valid_params(protocol): assert result.params.field_b.inner_field == "test_inner" +def test_deserialize_notification_message_unknown_type(protocol): + params = """ + { + "jsonrpc": "2.0", + "method": "random", + "params": { + "field_a": "test_a", + "field_b": { + "inner_field": "test_inner" + } + } + } + """ + + result = json.loads(params, object_hook=protocol._deserialize_message) + + assert isinstance(result, JsonRPCNotification) + assert result.jsonrpc == "2.0" + assert result.method == "random" + + assert result.params.field_a == "test_a" + assert result.params.field_b.inner_field == "test_inner" + + def test_deserialize_notification_message_bad_params_should_raise_error(protocol): params = f""" {{ @@ -134,20 +171,21 @@ def test_deserialize_notification_message_bad_params_should_raise_error(protocol @pytest.mark.parametrize( - "params, expected", + "method, params, expected", [ ( + # Known notification type. + PROGRESS, ProgressParams( token="id1", value=WorkDoneProgressBegin( - kind='begin', title="Begin progress", percentage=0, ), ), { "jsonrpc": "2.0", - "method": "test/notification", + "method": "$/progress", "params": { "token": "id1", "value": { @@ -158,9 +196,49 @@ def test_deserialize_notification_message_bad_params_should_raise_error(protocol }, }, ), + ( + # Custom notification type. + EXAMPLE_NOTIFICATION, + ExampleParams( + field_a="field one", + field_b=ExampleParams.InnerType( + inner_field="field two" + ), + ), + { + "jsonrpc": "2.0", + "method": EXAMPLE_NOTIFICATION, + "params": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two", + }, + }, + }, + ), + ( + # Custom notification with dict params. + EXAMPLE_NOTIFICATION, + { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + { + "jsonrpc": "2.0", + "method": EXAMPLE_NOTIFICATION, + "params": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two", + }, + }, + }, + ) ], ) -def test_serialize_notification_message(params, expected): +def test_serialize_notification_message(method, params, expected): """ Ensure that we can serialize notification messages, retaining all expected fields. @@ -172,7 +250,7 @@ def test_serialize_notification_message(params, expected): protocol._send_only_body = True protocol.connection_made(buffer) - protocol.notify("test/notification", params=params) + protocol.notify(method, params=params) actual = json.loads(buffer.getvalue()) assert actual == expected @@ -195,6 +273,30 @@ def test_deserialize_response_message(protocol): assert result.result == 1 +def test_deserialize_response_message_unknown_type(protocol): + params = """ + { + "jsonrpc": "2.0", + "id": "id", + "result": { + "field_a": "test_a", + "field_b": { + "inner_field": "test_inner" + } + } + } + """ + protocol._result_types["id"] = JsonRPCResponseMessage + result = json.loads(params, object_hook=protocol._deserialize_message) + + assert isinstance(result, JsonRPCResponseMessage) + assert result.jsonrpc == "2.0" + assert result.id == "id" + + assert result.result.field_a == "test_a" + assert result.result.field_b.inner_field == "test_inner" + + def test_deserialize_request_message_with_registered_type(protocol): params = f""" {{ @@ -277,6 +379,42 @@ def test_deserialize_request_message_without_registered_type(protocol): ], }, ), + ( # Unknown type with object params. + JsonRPCResponseMessage, + ExampleParams( + field_a="field one", + field_b=ExampleParams.InnerType(inner_field="field two") + ), + { + "jsonrpc": "2.0", + "id": "1", + "result": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + } + ), + ( # Unknown type with dict params. + JsonRPCResponseMessage, + { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + { + "jsonrpc": "2.0", + "id": "1", + "result": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + } + ) ], ) def test_serialize_response_message(msg_type, result, expected): @@ -299,6 +437,83 @@ def test_serialize_response_message(msg_type, result, expected): assert actual == expected +@pytest.mark.parametrize( + "method, params, expected", + [ + ( + TEXT_DOCUMENT_COMPLETION, + CompletionParams( + text_document=TextDocumentIdentifier(uri="file:///file.txt"), + position=Position(line=1, character=0) + ), + { + "jsonrpc": "2.0", + "id": "1", + "method": TEXT_DOCUMENT_COMPLETION, + "params": { + "textDocument": { "uri": "file:///file.txt" }, + "position": { "line": 1, "character": 0 } + }, + }, + ), + ( # Unknown type with object params. + EXAMPLE_REQUEST, + ExampleParams( + field_a="field one", + field_b=ExampleParams.InnerType(inner_field="field two") + ), + { + "jsonrpc": "2.0", + "id": "1", + "method": EXAMPLE_REQUEST, + "params": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + } + ), + ( # Unknown type with dict params. + EXAMPLE_REQUEST, + { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + { + "jsonrpc": "2.0", + "id": "1", + "method": EXAMPLE_REQUEST, + "params": { + "fieldA": "field one", + "fieldB": { + "innerField": "field two" + } + }, + } + ) + ], +) +def test_serialize_request_message(method, params, expected): + """ + Ensure that we can serialize request messages, retaining all expected + fields. + """ + + buffer = io.StringIO() + + protocol = JsonRPCProtocol(None) + protocol._send_only_body = True + protocol.connection_made(buffer) + + protocol.send_request(method, params, callback=None, msg_id="1") + actual = json.loads(buffer.getvalue()) + + assert actual == expected + + def test_data_received_without_content_type(client_server): _, server = client_server body = json.dumps( From 7037432d5fc32fadd6fc481a1a351794011ff803 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Wed, 9 Nov 2022 00:15:25 +0000 Subject: [PATCH 12/20] Make it possible to provide a custom converter --- pygls/protocol.py | 12 +++++++----- pygls/server.py | 23 ++++++++++++++++------ tests/test_protocol.py | 44 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/pygls/protocol.py b/pygls/protocol.py index a9a90e17..2fb5d081 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -175,6 +175,7 @@ def default_converter(): return converter + class JsonRPCProtocol(asyncio.Protocol): """Json RPC protocol implementation using on top of `asyncio.Protocol`. @@ -196,8 +197,10 @@ class JsonRPCProtocol(asyncio.Protocol): VERSION = '2.0' - def __init__(self, server): + def __init__(self, server, converter): self._server = server + self._converter = converter + self._shutdown = False # Book keeping for in-flight requests @@ -209,7 +212,6 @@ def __init__(self, server): self._message_buf = [] self._send_only_body = False - self._converter = default_converter() def __call__(self): return self @@ -556,7 +558,7 @@ def notify(self, method: str, params=None): params=params, jsonrpc=JsonRPCProtocol.VERSION ) - # breakpoint() + self._send_data(notification) def send_request(self, method, params=None, callback=None, msg_id=None): @@ -652,8 +654,8 @@ class LanguageServerProtocol(JsonRPCProtocol, metaclass=LSPMeta): workspace(Workspace): In memory workspace """ - def __init__(self, server): - super().__init__(server) + def __init__(self, server, converter): + super().__init__(server, converter) self.workspace = None self.trace = None diff --git a/pygls/server.py b/pygls/server.py index 4d62c0e1..9220de40 100644 --- a/pygls/server.py +++ b/pygls/server.py @@ -34,7 +34,7 @@ WorkspaceApplyEditResponse, WorkspaceEdit ) from pygls.progress import Progress -from pygls.protocol import LanguageServerProtocol +from pygls.protocol import LanguageServerProtocol, default_converter from pygls.workspace import Workspace if not IS_PYODIDE: @@ -148,6 +148,8 @@ class Server: protocol_cls(Protocol): Protocol implementation that must be derived from `asyncio.Protocol` + converter_factory: Factory function to use when constructing a cattrs converter. + loop(AbstractEventLoop): asyncio event loop max_workers(int, optional): Number of workers for `ThreadPool` and @@ -169,7 +171,7 @@ class Server: - lazy instantiated """ - def __init__(self, protocol_cls, loop=None, max_workers=2, + def __init__(self, protocol_cls, converter_factory, loop=None, max_workers=2, sync_kind=TextDocumentSyncKind.Incremental): if not issubclass(protocol_cls, asyncio.Protocol): raise TypeError('Protocol class should be subclass of asyncio.Protocol') @@ -194,7 +196,7 @@ def __init__(self, protocol_cls, loop=None, max_workers=2, except NotImplementedError: pass - self.lsp = protocol_cls(self) + self.lsp = protocol_cls(self, converter_factory()) def shutdown(self): """Shutdown server.""" @@ -337,13 +339,22 @@ class LanguageServer(Server): exception. """ - def __init__(self, name: str = None, version: str = None, loop=None, - protocol_cls=LanguageServerProtocol, max_workers: int = 2): + def __init__( + self, + name: str = None, + version: str = None, + loop=None, + protocol_cls=LanguageServerProtocol, + converter_factory=default_converter, + max_workers: int = 2 + ): + if not issubclass(protocol_cls, LanguageServerProtocol): raise TypeError('Protocol class should be subclass of LanguageServerProtocol') + self.name = name self.version = version - super().__init__(protocol_cls, loop, max_workers) + super().__init__(protocol_cls, converter_factory, loop, max_workers) def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> WorkspaceApplyEditResponse: """Sends apply edit request to the client.""" diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 8f1b8bf3..b47dde36 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -42,6 +42,7 @@ WorkDoneProgressBegin, ) from pygls.protocol import ( + default_converter, JsonRPCProtocol, JsonRPCRequestMessage, JsonRPCResponseMessage, @@ -98,7 +99,7 @@ def get_message_type(self, method: str): @pytest.fixture() def protocol(): - return ExampleProtocol(None) + return ExampleProtocol(None, default_converter()) def test_deserialize_notification_message_valid_params(protocol): @@ -170,6 +171,41 @@ def test_deserialize_notification_message_bad_params_should_raise_error(protocol json.loads(params, object_hook=protocol._deserialize_message) +def test_deserialize_response_message_custom_converter(): + params = """ + { + "jsonrpc": "2.0", + "id": "id", + "result": "1" + } + """ + # Just for fun, let's create a converter that reverses all the keys in a dict. + # + @attrs.define + class egasseM: + cprnosj: str + di: str + tluser: str + + def structure_hook(obj, cls): + params = {k[::-1]: v for k, v in obj.items()} + return cls(**params) + + def custom_converter(): + converter = default_converter() + converter.register_structure_hook(egasseM, structure_hook) + return converter + + protocol = JsonRPCProtocol(None, custom_converter()) + protocol._result_types["id"] = egasseM + result = json.loads(params, object_hook=protocol._deserialize_message) + + assert isinstance(result, egasseM) + assert result.cprnosj == "2.0" + assert result.di == "id" + assert result.tluser == "1" + + @pytest.mark.parametrize( "method, params, expected", [ @@ -246,7 +282,7 @@ def test_serialize_notification_message(method, params, expected): buffer = io.StringIO() - protocol = JsonRPCProtocol(None) + protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) @@ -425,7 +461,7 @@ def test_serialize_response_message(msg_type, result, expected): buffer = io.StringIO() - protocol = JsonRPCProtocol(None) + protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) @@ -504,7 +540,7 @@ def test_serialize_request_message(method, params, expected): buffer = io.StringIO() - protocol = JsonRPCProtocol(None) + protocol = JsonRPCProtocol(None, default_converter()) protocol._send_only_body = True protocol.connection_made(buffer) From 9b583cf09e1f40dda8b3fac79ea1b1ec0e019465 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sat, 12 Nov 2022 22:36:09 +0000 Subject: [PATCH 13/20] Fix construction of `workspace.fileOperations` server capabilities --- pygls/capabilities.py | 61 ++------ pygls/lsp/__init__.py | 13 ++ tests/test_capabilities.py | 299 ++++++++++++++++++++++++++++++++++++- 3 files changed, 326 insertions(+), 47 deletions(-) diff --git a/pygls/capabilities.py b/pygls/capabilities.py index fa2d3240..0ff7422f 100644 --- a/pygls/capabilities.py +++ b/pygls/capabilities.py @@ -322,54 +322,23 @@ def _with_workspace_symbol(self): def _with_workspace_capabilities(self): # File operations file_operations = FileOperationOptions() + operations = [ + (WORKSPACE_WILL_CREATE_FILES, "will_create"), + (WORKSPACE_DID_CREATE_FILES, "did_create"), + (WORKSPACE_WILL_DELETE_FILES, "will_delete"), + (WORKSPACE_DID_DELETE_FILES, "did_delete"), + (WORKSPACE_WILL_RENAME_FILES, "will_rename"), + (WORKSPACE_DID_RENAME_FILES, "did_rename"), + ] - will_create = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.willCreate') - if WORKSPACE_WILL_CREATE_FILES in self.features - else None - ) - if will_create is not None: - file_operations.will_create = will_create - - did_create = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.didCreate') - if WORKSPACE_DID_CREATE_FILES in self.features - else None - ) - if did_create is not None: - file_operations.did_create = did_create - - will_rename = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.willRename') - if WORKSPACE_WILL_RENAME_FILES in self.features - else None - ) - if will_rename is not None: - file_operations.will_rename = will_rename - - did_rename = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.didRename') - if WORKSPACE_DID_RENAME_FILES in self.features - else None - ) - if did_rename is not None: - file_operations.did_rename = did_rename - - will_delete = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.willDelete') - if WORKSPACE_WILL_DELETE_FILES in self.features - else None - ) - if will_delete is not None: - file_operations.will_delete = will_delete + for method_name, capability_name in operations: + client_supports_method = get_capability( + self.client_capabilities, f'workspace.file_operations.{capability_name}' + ) - did_delete = ( - get_capability(self.client_capabilities, 'workspace.fileOperations.didDelete') - if WORKSPACE_DID_DELETE_FILES in self.features - else None - ) - if did_delete is not None: - file_operations.did_delete = did_delete + if client_supports_method: + value = self._provider_options(method_name, None) + setattr(file_operations, capability_name, value) self.server_cap.workspace = ServerCapabilitiesWorkspaceType( workspace_folders=WorkspaceFoldersServerCapabilities( diff --git a/pygls/lsp/__init__.py b/pygls/lsp/__init__.py index 11ea0b12..51347110 100644 --- a/pygls/lsp/__init__.py +++ b/pygls/lsp/__init__.py @@ -22,6 +22,13 @@ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL, TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA, TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, + WORKSPACE_DID_CREATE_FILES, + WORKSPACE_DID_DELETE_FILES, + WORKSPACE_DID_RENAME_FILES, + WORKSPACE_WILL_CREATE_FILES, + WORKSPACE_WILL_DELETE_FILES, + WORKSPACE_WILL_RENAME_FILES, + FileOperationRegistrationOptions, SemanticTokensLegend, SemanticTokensRegistrationOptions, ShowDocumentResult @@ -37,6 +44,12 @@ TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: Union[SemanticTokensLegend, SemanticTokensRegistrationOptions], + WORKSPACE_DID_CREATE_FILES: FileOperationRegistrationOptions, + WORKSPACE_DID_DELETE_FILES: FileOperationRegistrationOptions, + WORKSPACE_DID_RENAME_FILES: FileOperationRegistrationOptions, + WORKSPACE_WILL_CREATE_FILES: FileOperationRegistrationOptions, + WORKSPACE_WILL_DELETE_FILES: FileOperationRegistrationOptions, + WORKSPACE_WILL_RENAME_FILES: FileOperationRegistrationOptions, } diff --git a/tests/test_capabilities.py b/tests/test_capabilities.py index cb69027a..1b84aa85 100644 --- a/tests/test_capabilities.py +++ b/tests/test_capabilities.py @@ -7,20 +7,31 @@ TEXT_DOCUMENT_DID_SAVE, TEXT_DOCUMENT_WILL_SAVE, TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, + WORKSPACE_DID_CREATE_FILES, + WORKSPACE_WILL_CREATE_FILES, + WORKSPACE_DID_DELETE_FILES, + WORKSPACE_WILL_DELETE_FILES, + WORKSPACE_DID_RENAME_FILES, + WORKSPACE_WILL_RENAME_FILES, ClientCapabilities, + FileOperationClientCapabilities, + FileOperationFilter, + FileOperationOptions, + FileOperationPattern, + FileOperationRegistrationOptions, SaveOptions, TextDocumentClientCapabilities, TextDocumentSaveRegistrationOptions, TextDocumentSyncClientCapabilities, TextDocumentSyncKind, TextDocumentSyncOptions, + WorkspaceClientCapabilities, ) from pygls.capabilities import ServerCapabilitiesBuilder @pytest.mark.parametrize("capabilities,features,options,expected", [ - # textDocument/didOpen ( ClientCapabilities(), @@ -134,3 +145,289 @@ def test_text_doc_sync_capabilities( actual = builder.build().text_document_sync assert expected == actual + + +@pytest.mark.parametrize("capabilities,features,options,expected", [ + ( + ClientCapabilities(), + set(), + {}, + FileOperationOptions() + ), + # workspace/willCreateFiles + ( + ClientCapabilities(), + {WORKSPACE_WILL_CREATE_FILES}, + { + WORKSPACE_WILL_CREATE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + will_create=True + ) + ) + ), + {WORKSPACE_WILL_CREATE_FILES}, + { + WORKSPACE_WILL_CREATE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + will_create=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), + # workspace/didCreateFiles + ( + ClientCapabilities(), + {WORKSPACE_DID_CREATE_FILES}, + { + WORKSPACE_DID_CREATE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + did_create=True + ) + ) + ), + {WORKSPACE_DID_CREATE_FILES}, + { + WORKSPACE_DID_CREATE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + did_create=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), + # workspace/willDeleteFiles + ( + ClientCapabilities(), + {WORKSPACE_WILL_DELETE_FILES}, + { + WORKSPACE_WILL_DELETE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + will_delete=True + ) + ) + ), + {WORKSPACE_WILL_DELETE_FILES}, + { + WORKSPACE_WILL_DELETE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + will_delete=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), + # workspace/didDeleteFiles + ( + ClientCapabilities(), + {WORKSPACE_DID_DELETE_FILES}, + { + WORKSPACE_DID_DELETE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + did_delete=True + ) + ) + ), + {WORKSPACE_DID_DELETE_FILES}, + { + WORKSPACE_DID_DELETE_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + did_delete=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), + # workspace/willRenameFiles + ( + ClientCapabilities(), + {WORKSPACE_WILL_RENAME_FILES}, + { + WORKSPACE_WILL_RENAME_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + will_rename=True + ) + ) + ), + {WORKSPACE_WILL_RENAME_FILES}, + { + WORKSPACE_WILL_RENAME_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + will_rename=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), + # workspace/didRenameFiles + ( + ClientCapabilities(), + {WORKSPACE_DID_RENAME_FILES}, + { + WORKSPACE_DID_RENAME_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions() + ), + ( + ClientCapabilities( + workspace=WorkspaceClientCapabilities( + file_operations=FileOperationClientCapabilities( + did_rename=True + ) + ) + ), + {WORKSPACE_DID_RENAME_FILES}, + { + WORKSPACE_DID_RENAME_FILES: FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + }, + FileOperationOptions( + did_rename=FileOperationRegistrationOptions( + filters=[ + FileOperationFilter( + pattern=FileOperationPattern(glob="**/*.py") + ) + ] + ) + ) + ), +]) +def test_file_operations_capabilities( + capabilities: ClientCapabilities, + features: Set[str], + options, + expected: FileOperationOptions +): + """Ensure that `pygls` can correctly construct server capabilities for the file + operations set of features.""" + + builder = ServerCapabilitiesBuilder( + capabilities, features, options, [], TextDocumentSyncKind.Incremental + ) + + server = builder.build() + assert server.workspace is not None + + actual = server.workspace.file_operations + assert expected == actual From 90c0f8d4d470bee400ad2ef3022760924cad8043 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Mon, 7 Nov 2022 23:16:17 +0000 Subject: [PATCH 14/20] Initial draft of a migration guide --- docs/source/conf.py | 4 +- docs/source/index.rst | 1 + docs/source/pages/migrating-to-v1.rst | 216 ++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 docs/source/pages/migrating-to-v1.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index d03c4b6d..32a7221f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -61,7 +61,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -88,7 +88,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +#html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. diff --git a/docs/source/index.rst b/docs/source/index.rst index 6add03fb..9c716a60 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -42,6 +42,7 @@ User Guide pages/tutorial pages/advanced_usage pages/testing + pages/migrating-to-v1 .. _Language Server Protocol: https://microsoft.github.io/language-server-protocol/specification diff --git a/docs/source/pages/migrating-to-v1.rst b/docs/source/pages/migrating-to-v1.rst new file mode 100644 index 00000000..413ca819 --- /dev/null +++ b/docs/source/pages/migrating-to-v1.rst @@ -0,0 +1,216 @@ +Migrating to v1.0 +================= + +The most notable change of the ``v1.0`` release of ``pygls`` is the removal of its hand written LSP type and method definitions in favour of relying on the types provided by the `lsprotocol`_ library which are automatically generated from the LSP specification. +As as side effect this has also meant the removal of `pydantic`_ as a dependency, since ``lsprotocol`` uses `attrs`_ and `cattrs`_ for serialisation and validation. + +This guide outlines how to adapt an existing server to the breaking changes introduced in this release. + +Updating Imports +---------------- + +The ``pygls.lsp.methods`` and ``pygls.lsp.types`` modules no longer exist. +Instead, all types and method names should now be imported from the ``lsprotocol.types`` module. + +Additionally, the following types and constants have been renamed. + +================================================================== ============== +pygls lsprotocol +================================================================== ============== +``CODE_ACTION`` ``TEXT_DOCUMENT_CODE_ACTION`` +``CODE_LENS`` ``TEXT_DOCUMENT_CODE_LENS`` +``COLOR_PRESENTATION`` ``TEXT_DOCUMENT_COLOR_PRESENTATION`` +``COMPLETION`` ``TEXT_DOCUMENT_COMPLETION`` +``DECLARATION`` ``TEXT_DOCUMENT_DECLARATION`` +``DEFINITION`` ``TEXT_DOCUMENT_DEFINITION`` +``DOCUMENT_COLOR`` ``TEXT_DOCUMENT_DOCUMENT_COLOR`` +``DOCUMENT_HIGHLIGHT`` ``TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT`` +``DOCUMENT_LINK`` ``TEXT_DOCUMENT_DOCUMENT_LINK`` +``DOCUMENT_SYMBOL`` ``TEXT_DOCUMENT_DOCUMENT_SYMBOL`` +``FOLDING_RANGE`` ``TEXT_DOCUMENT_FOLDING_RANGE`` +``FORMATTING`` ``TEXT_DOCUMENT_FORMATTING`` +``HOVER`` ``TEXT_DOCUMENT_HOVER`` +``IMPLEMENTATION`` ``TEXT_DOCUMENT_IMPLEMENTATION`` +``LOG_TRACE_NOTIFICATION`` ``LOG_TRACE`` +``ON_TYPE_FORMATTING`` ``TEXT_DOCUMENT_ON_TYPE_FORMATTING`` +``PREPARE_RENAME`` ``TEXT_DOCUMENT_PREPARE_RENAME`` +``PROGRESS_NOTIFICATION`` ``PROGRESS`` +``RANGE_FORMATTING`` ``TEXT_DOCUMENT_RANGE_FORMATTING`` +``REFERENCES`` ``TEXT_DOCUMENT_REFERENCES`` +``RENAME`` ``TEXT_DOCUMENT_RENAME`` +``SELECTION_RANGE`` ``TEXT_DOCUMENT_SELECTION_RANGE`` +``SET_TRACE_NOTIFICATION`` ``SET_TRACE`` +``SIGNATURE_HELP`` ``TEXT_DOCUMENT_SIGNATURE_HELP`` +``TEXT_DOCUMENT_CALL_HIERARCHY_INCOMING_CALLS`` ``CALL_HIERARCHY_INCOMING_CALLS`` +``TEXT_DOCUMENT_CALL_HIERARCHY_OUTGOING_CALLS`` ``CALL_HIERARCHY_OUTGOING_CALLS`` +``TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE`` ``TEXT_DOCUMENT_PREPARE_CALL_HIERARCHY`` +``TYPE_DEFINITION`` ``TEXT_DOCUMENT_TYPE_DEFINITION`` +``WORKSPACE_FOLDERS`` ``WORKSPACE_WORKSPACE_FOLDERS`` +``ApplyWorkspaceEditResponse`` ``ApplyWorkspaceEditResult`` +``ClientInfo`` ``InitializeParamsClientInfoType`` +``CodeActionDisabled`` ``CodeActionDisabledType`` +``CodeActionLiteralSupportActionKindClientCapabilities`` ``CodeActionClientCapabilitiesCodeActionLiteralSupportTypeCodeActionKindType`` +``CodeActionLiteralSupportClientCapabilities`` ``CodeActionClientCapabilitiesCodeActionLiteralSupportType`` +``CompletionItemClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemType`` +``CompletionItemKindClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemKindType`` +``CompletionTagSupportClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemTypeTagSupportType`` +``DocumentSymbolCapabilitiesTagSupport`` ``DocumentSymbolClientCapabilitiesTagSupportType`` +``InsertTextModeSupportClientCapabilities`` ``CompletionClientCapabilitiesCompletionItemTypeInsertTextModeSupportType`` +``MarkedStringType`` ``MarkedString`` +``MarkedString`` ``MarkedString_Type1`` +``PrepareRename`` ``PrepareRenameResult_Type1`` +``PublishDiagnosticsTagSupportClientCapabilities`` ``PublishDiagnosticsClientCapabilitiesTagSupportType`` +``ResolveSupportClientCapabilities`` ``CodeActionClientCapabilitiesResolveSupportType`` +``SemanticTokensRequestsFull`` ``SemanticTokensRegistrationOptionsFullType1`` +``SemanticTokensRequests`` ``SemanticTokensClientCapabilitiesRequestsType`` +``ServerInfo`` ``InitializeResultServerInfoType`` +``ShowMessageRequestActionItem`` ``ShowMessageRequestClientCapabilitiesMessageActionItemType`` +``SignatureHelpInformationClientCapabilities`` ``SignatureHelpClientCapabilitiesSignatureInformationType`` +``SignatureHelpInformationParameterInformationClientCapabilities`` ``SignatureHelpClientCapabilitiesSignatureInformationTypeParameterInformationType`` +``TextDocumentContentChangeEvent`` ``TextDocumentContentChangeEvent_Type1`` +``TextDocumentContentChangeTextEvent`` ``TextDocumentContentChangeEvent_Type2`` +``TextDocumentSyncOptionsServerCapabilities`` ``TextDocumentSyncOptions`` +``Trace`` ``TraceValues`` +``URI`` ``str`` +``WorkspaceCapabilitiesSymbolKind`` ``WorkspaceSymbolClientCapabilitiesSymbolKindType`` +``WorkspaceCapabilitiesTagSupport`` ``WorkspaceSymbolClientCapabilitiesTagSupportType`` +``WorkspaceFileOperationsServerCapabilities`` ``FileOperationOptions`` +``WorkspaceServerCapabilities`` ``ServerCapabilitiesWorkspaceType`` +================================================================== ============== + +Custom Models +------------- + +One of the most obvious changes is the switch to `attrs`_ and `cattrs`_ for serialization and deserialisation. +This means that any custom models used by your language server will need to be converted to an ``attrs`` style class. + +.. code-block:: python + + # Before + from pydantic import BaseModel, Field + + class ExampleConfig(BaseModel): + build_dir: Optional[str] = Field(None, alias="buildDir") + + builder_name: str = Field("html", alias="builderName") + + conf_dir: Optional[str] = Field(None, alias="confDir") + +.. code-block:: python + + # After + import attrs + + @attrs.define + class ExampleConfig: + build_dir: Optional[str] = attrs.field(default=None) + + builder_name: str = attrs.field(default="html") + + conf_dir: Optional[str] = attrs.field(default=None) + + +Pygls provides a default `converter`_ that it will use when converting your models to/from JSON, which should be sufficient for most scenarios. + +.. code-block:: pycon + + >>> from pygls.protocol import default_converter + >>> converter = default_converter() + + >>> config = ExampleConfig(builder_name='epub', conf_dir='/path/to/conf') + >>> converter.unstructure(config) + {'builderName': 'epub', 'confDir': '/path/to/conf'} # Note how snake_case is converted to camelCase + + >>> converter.structure({'builderName': 'epub', 'confDir': '/path/to/conf'}, ExampleConfig) + ExampleConfig(build_dir=None, builder_name='epub', conf_dir='/path/to/conf') + +However, depending on the complexity of your type definitions you may find the default converter fail to parse some of your types. + +.. code-block:: pycon + + >>> from typing import Literal, Union + + >>> @attrs.define + ... class ExampleConfig: + ... num_jobs: Union[Literal["auto"], int] = attrs.field(default='auto') + ... + + >>> converter.structure({'numJobs': 'auto'}, ExampleConfig) + + Exception Group Traceback (most recent call last): + | File "", line 1, in + | File "/.../python3.10/site-packages/cattrs/converters.py", li + ne 309, in structure + | return self._structure_func.dispatch(cl)(obj, cl) + | File "", line 10, in structure_ExampleConfig + | if errors: raise __c_cve('While structuring ' + 'ExampleConfig', errors, __cl) + | cattrs.errors.ClassValidationError: While structuring ExampleConfig (1 sub-exception) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "", line 6, in structure_ExampleConfig + | res['num_jobs'] = __c_structure_num_jobs(o['numJobs'], __c_type_num_jobs) + | File "/.../python3.10/site-packages/cattrs/converters.py", + line 377, in _structure_error + | raise StructureHandlerNotFoundError(msg, type_=cl) + | cattrs.errors.StructureHandlerNotFoundError: Unsupported type: typing.Union[typing.Literal['auto'], int]. + Register a structure hook for it. + | Structuring class ExampleConfig @ attribute num_jobs + +------------------------------------ + +In which case you can extend the converter provided by ``pygls`` with your own `structure hooks`_ + +.. code-block:: python + + from pygls.protocol import default_converter + + def custom_converter(): + converter = default_converter() + converter.register_structure_hook(Union[Literal['auto', int], lambda obj, _: obj) + + return converter + +You can then override the default converter used by ``pygls`` when constructing your language server instance + +.. code-block:: python + + server = LanguageServer( + name="my-language-server", version="1.0", converter_factory=custom_converter + ) + +See the `hooks.py`_ module in ``lsprotocol`` for some example structure hooks + +Miscellaneous +------------- + +``ClientCapabilities.get_capability`` is now ``get_capability`` +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. code-block:: python + + # Before + from pygls.lsp.types import ClientCapabilities + + client_capabilities = ClientCapabilities() + commit_character_support = client_capabilities.get_capability( + "text_document.completion.completion_item.commit_characters_support", False + ) + +.. code-block:: python + + # After + from lsprotocol.types import ClientCapabilities + from pygls.capabilities import get_capability + + client_capabilities = ClientCapabilities() + commit_character_support = get_capability( + client_capabilities, + "text_document.completion.completion_item.commit_characters_support", + False + ) + +.. _attrs: https://www.attrs.org/en/stable/index.html +.. _cattrs: https://cattrs.readthedocs.io/en/stable/ +.. _converter: https://cattrs.readthedocs.io/en/stable/converters.html +.. _hooks.py: https://github.com/microsoft/lsprotocol/blob/main/lsprotocol/_hooks.py +.. _lsprotocol: https://github.com/microsoft/lsprotocol +.. _pydantic: https://pydantic-docs.helpmanual.io/ +.. _structure hooks: https://cattrs.readthedocs.io/en/stable/structuring.html#registering-custom-structuring-hooks From 34beab87986ec646d93677e9baef1bb6c5505238 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Thu, 17 Nov 2022 22:56:47 +0000 Subject: [PATCH 15/20] Test on released version of Python 3.11 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89836d8c..edf371d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] runs-on: ${{ matrix.os }} From 369a048d9750733c28b84c29996e9f5edee8d1bd Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Thu, 17 Nov 2022 23:18:22 +0000 Subject: [PATCH 16/20] Bump pyodide version --- pyodide_testrunner/test-runner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyodide_testrunner/test-runner.js b/pyodide_testrunner/test-runner.js index b7874515..dbbc01fd 100644 --- a/pyodide_testrunner/test-runner.js +++ b/pyodide_testrunner/test-runner.js @@ -1,4 +1,4 @@ -importScripts("https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js") +importScripts("https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js") // Used to redirect pyodide's stdout to the webpage. function patchedStdout(...args) { @@ -8,7 +8,7 @@ function patchedStdout(...args) { async function runTests(whl) { console.log("Loading pyodide") let pyodide = await loadPyodide({ - indexURL: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/" + indexURL: "https://cdn.jsdelivr.net/pyodide/v0.21.3/full/" }) console.log("Installing dependencies") From 4b7e9ee82c43f12c24732a8566c0f3c139fa9359 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Thu, 17 Nov 2022 23:58:58 +0000 Subject: [PATCH 17/20] Align fountain extension to breaking changes --- examples/fountain-extension/package-lock.json | 12 ++++++------ examples/fountain-extension/src/server.py | 5 ----- examples/fountain-extension/src/server.ts | 16 +++++++++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/fountain-extension/package-lock.json b/examples/fountain-extension/package-lock.json index 2106689a..bdf6386f 100644 --- a/examples/fountain-extension/package-lock.json +++ b/examples/fountain-extension/package-lock.json @@ -983,9 +983,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2413,9 +2413,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } diff --git a/examples/fountain-extension/src/server.py b/examples/fountain-extension/src/server.py index 646e02c2..94e07183 100644 --- a/examples/fountain-extension/src/server.py +++ b/examples/fountain-extension/src/server.py @@ -3,12 +3,7 @@ from lsprotocol.types import TEXT_DOCUMENT_COMPLETION from lsprotocol.types import (CompletionItem, CompletionParams, CompletionList, CompletionOptions) -# The following imports are required for the glue code in 'server.ts' -import json -from pygls.protocol import deserialize_message - server = LanguageServer("foutain-language-server", "v0.1") -server.start_pyodide() CHARACTER = re.compile(r"^[A-Z][A-Z ]+$", re.MULTILINE) diff --git a/examples/fountain-extension/src/server.ts b/examples/fountain-extension/src/server.ts index 6c9e3357..256f716d 100644 --- a/examples/fountain-extension/src/server.ts +++ b/examples/fountain-extension/src/server.ts @@ -1,4 +1,4 @@ -importScripts("https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js") +importScripts("https://cdn.jsdelivr.net/pyodide/v0.21.3/full/pyodide.js") /* @ts-ignore */ import * as languageServer from "./server.py"; @@ -19,17 +19,19 @@ async function initPyodide() { /* @ts-ignore */ let pyodide = await loadPyodide({ - indexURL: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/" + indexURL: "https://cdn.jsdelivr.net/pyodide/v0.21.3/full/" }) console.log("Installing dependencies.") await pyodide.loadPackage(["micropip"]) await pyodide.runPythonAsync(` + import json import sys import micropip + await micropip.install('pygls') - # Uncomment to use a local build of pygls + # Uncomment to use a local build of pygls -- see README for details. # await micropip.install('https://xxx.loca.lt/out/pygls--py3-none-any.whl') `) @@ -37,7 +39,11 @@ async function initPyodide() { // Patch stdout to redirect the output. pyodide.globals.get('sys').stdout.write = patchedStdout - await pyodide.runPythonAsync(languageServer) + await pyodide.runPythonAsync(` +${languageServer} + +server.start_pyodide() +`) return pyodide } @@ -55,7 +61,7 @@ onmessage = async (event) => { await pyodide.runPythonAsync(` from js import client_message - message = json.loads(client_message, object_hook=deserialize_message) + message = json.loads(client_message, object_hook=server.lsp._deserialize_message) server.lsp._procedure_handler(message) `) } From 900d40a6272c27b73ced947ee3267a4b6b3344b9 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 2 Dec 2022 09:38:29 -0300 Subject: [PATCH 18/20] docs: Updates for v1 release --- README.md | 17 +++++++---------- RELEASING.md | 2 ++ docs/source/pages/getting_started.rst | 15 +++------------ docs/source/pages/migrating-to-v1.rst | 10 ++++++++++ docs/source/pages/tutorial.rst | 8 +++++--- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 45024879..c7aa08f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -> **Note** -> We will soon release a major version bump with breaking changes. We recommend starting new projects with the v1 alpha release at https://github.com/openlawlibrary/pygls/pull/273 - # _pygls_ [![PyPI Version](https://img.shields.io/pypi/v/pygls.svg)](https://pypi.org/project/pygls/) [![Build Status](https://dev.azure.com/openlawlibrary/pygls/_apis/build/status/openlawlibrary.pygls?branchName=master)](https://dev.azure.com/openlawlibrary/pygls/_build/latest?definitionId=2&branchName=master) ![!pyversions](https://img.shields.io/pypi/pyversions/pygls.svg) ![license](https://img.shields.io/pypi/l/pygls.svg) [![Documentation Status](https://img.shields.io/badge/docs-latest-green.svg)](https://pygls.readthedocs.io/en/latest/) @@ -8,17 +5,17 @@ _pygls_ (pronounced like "pie glass") is a pythonic generic implementation of the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/specification) for use as a foundation for writing language servers using Python (e.g. Python, XML, etc.). It allows you to write your own [language server](https://langserver.org/) in just a few lines of code. ## Quick Intro - -> **_IMPORTANT NOTE:_** -> -> In order to support type-checking, we added `pydantic` library which requires passing keyword arguments when creating [LSP models](https://github.com/openlawlibrary/pygls/blob/master/pygls/lsp/methods.py). - Here's how to create a server and register a code completion feature: ```python -from pygls.capabilities import COMPLETION from pygls.server import LanguageServer -from pygls.lsp import CompletionItem, CompletionList, CompletionOptions, CompletionParams +from lsprotocol.types import ( + TEXT_DOCUMENT_COMPLETION, + CompletionItem, + CompletionList, + CompletionOptions, + CompletionParams +) server = LanguageServer('example-server', 'v0.1') diff --git a/RELEASING.md b/RELEASING.md index 1e5a96a4..0d4cdc55 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -22,8 +22,10 @@ Release notes are kept in CHANGELOG.md # Python's `setuptools` automatically derives the version from the latest Git tag. # NB. If the latest commit doesn't have a tag, then `setuptools` will add `dev-[hash]` to the version. git tag v"$(python -c 'from pygls import __version__; print(__version__)')" +git push --tags # not required for releasing, just needed because normal `git push` won't send the tags # Build the project into the Source and Wheel formats (they go into `./dist`) +rm -rf dist pygls.egg-info python -m build # Upload to Pypi diff --git a/docs/source/pages/getting_started.rst b/docs/source/pages/getting_started.rst index 35c88155..636eac32 100644 --- a/docs/source/pages/getting_started.rst +++ b/docs/source/pages/getting_started.rst @@ -17,12 +17,6 @@ servers that are based on it. Installation ------------ -.. note:: - *December 2022* - We will soon release a major version bump with breaking changes. - We recommend starting new projects with the v1 alpha release at: - https://github.com/openlawlibrary/pygls/pull/273 - To get the latest release from *PyPI*, simply run: .. code:: console @@ -64,7 +58,7 @@ Register Features and Commands .. code:: python - @server.feature(COMPLETION, CompletionOptions(trigger_characters=[','])) + @server.feature(TEXT_DOCUMENT_COMPLETION, CompletionOptions(trigger_characters=[','])) def completions(params: CompletionParams): """Returns completion items.""" return CompletionList( @@ -84,9 +78,7 @@ Register Features and Commands def cmd_return_hello_world(ls, *args): return 'Hello World!' -Features that are currently supported by the LSP specification can be -found in `pygls.lsp.methods`_ module, while corresponding request/response -classes can be found in `pygls.lsp.types`_ module. +See the `lsprotocol`_ module for the complete and canonical list of avaiable features. Advanced usage -------------- @@ -102,5 +94,4 @@ haven't worked with language servers before. .. _GitHub: https://github.com/openlawlibrary/pygls -.. _pygls.lsp.methods: https://github.com/openlawlibrary/pygls/blob/master/pygls/lsp/methods.py -.. _pygls.lsp.types: https://github.com/openlawlibrary/pygls/tree/master/pygls/lsp/types +.. _lsprotocol: https://github.com/microsoft/lsprotocol/blob/main/lsprotocol/types.py diff --git a/docs/source/pages/migrating-to-v1.rst b/docs/source/pages/migrating-to-v1.rst index 413ca819..7bc86781 100644 --- a/docs/source/pages/migrating-to-v1.rst +++ b/docs/source/pages/migrating-to-v1.rst @@ -6,6 +6,13 @@ As as side effect this has also meant the removal of `pydantic`_ as a dependency This guide outlines how to adapt an existing server to the breaking changes introduced in this release. +Known Migrations +---------------- +You may find insight and inspiration from these projects that have already successfully migrated to v1: +- `jedi-language-server`_ +- `vscode-ruff`_ +- `esbonio`_ + Updating Imports ---------------- @@ -214,3 +221,6 @@ Miscellaneous .. _lsprotocol: https://github.com/microsoft/lsprotocol .. _pydantic: https://pydantic-docs.helpmanual.io/ .. _structure hooks: https://cattrs.readthedocs.io/en/stable/structuring.html#registering-custom-structuring-hooks +.. _jedi-language-serer: https://github.com/pappasam/jedi-language-server/pull/230 +.. _vscode-ruff: https://github.com/charliermarsh/vscode-ruff/pull/37 +.. _esbonio: https://github.com/swyddfa/esbonio/pull/484 diff --git a/docs/source/pages/tutorial.rst b/docs/source/pages/tutorial.rst index a42a226d..04c036de 100644 --- a/docs/source/pages/tutorial.rst +++ b/docs/source/pages/tutorial.rst @@ -3,13 +3,15 @@ Tutorial ======== -In order to help you with *pygls*, we have created a simple -`json-extension`_ example. +In order to help you with using *pygls* in VSCode, we have created a simple `json-extension`_ example. + +.. note:: + You do not need this extension when using *pygls* with other text editors. Prerequisites ------------- -In order to setup and run the example extension, you need following software +In order to setup and run the example VSCode extension, you need following software installed: * `Visual Studio Code `_ editor From 288f33b25d8139b929144e9a5e972d6d69830ea3 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 2 Dec 2022 09:41:23 -0300 Subject: [PATCH 19/20] chore: Bump to v1 --- CHANGELOG.md | 4 ++++ pygls/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4501ff..4c8bbe5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning][semver]. ### Changed ### Fixed +## [1.0.0] - 2/12/2022 +### Changed +BREAKING CHANGE: Replaced `pydantic` with [`lsprotocol`](https://github.com/microsoft/lsprotocol) + ## [0.13.1] - 1/12/2022 ### Changed Docs now state that the v1 alpha branch is the recommended way to start new projects diff --git a/pygls/__init__.py b/pygls/__init__.py index 4ad79ed1..8f7112bc 100644 --- a/pygls/__init__.py +++ b/pygls/__init__.py @@ -19,7 +19,7 @@ import os import sys -__version__ = "0.13.1" +__version__ = "1.0.0" IS_WIN = os.name == 'nt' IS_PYODIDE = 'pyodide' in sys.modules From dd484ab4ea4b491151d8c8b15b74661fa58aa759 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sat, 3 Dec 2022 18:07:07 +0000 Subject: [PATCH 20/20] Make name and version arguments mandatory --- docs/source/pages/migrating-to-v1.rst | 14 +++++++++++++- pygls/protocol.py | 14 ++++---------- pygls/server.py | 4 ++-- tests/test_server_connection.py | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/source/pages/migrating-to-v1.rst b/docs/source/pages/migrating-to-v1.rst index 7bc86781..a4c3e1a4 100644 --- a/docs/source/pages/migrating-to-v1.rst +++ b/docs/source/pages/migrating-to-v1.rst @@ -180,7 +180,7 @@ You can then override the default converter used by ``pygls`` when constructing .. code-block:: python server = LanguageServer( - name="my-language-server", version="1.0", converter_factory=custom_converter + name="my-language-server", version="v1.0", converter_factory=custom_converter ) See the `hooks.py`_ module in ``lsprotocol`` for some example structure hooks @@ -188,6 +188,18 @@ See the `hooks.py`_ module in ``lsprotocol`` for some example structure hooks Miscellaneous ------------- +Mandatory ``name`` and ``version`` +"""""""""""""""""""""""""""""""""" + +It is now necessary to provide a name and version when constructing an instance of the ``LanguageServer`` class + +.. code-block:: python + + from pygls.server import LanguageServer + + server = LanguageServer(name="my-language-server", version="v1.0") + + ``ClientCapabilities.get_capability`` is now ``get_capability`` """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/pygls/protocol.py b/pygls/protocol.py index 2fb5d081..8917ea03 100644 --- a/pygls/protocol.py +++ b/pygls/protocol.py @@ -663,16 +663,10 @@ def __init__(self, server, converter): from pygls.progress import Progress self.progress = Progress(self) - if server.name is None or server.version is None: - self.server_info = None - logger.warning("Name or version is not set. " - "This will be mandatory: " - "https://github.com/openlawlibrary/pygls/pull/276") - else: - self.server_info = InitializeResultServerInfoType( - name=server.name, - version=server.version, - ) + self.server_info = InitializeResultServerInfoType( + name=server.name, + version=server.version, + ) self._register_builtin_features() diff --git a/pygls/server.py b/pygls/server.py index 9220de40..de690541 100644 --- a/pygls/server.py +++ b/pygls/server.py @@ -341,8 +341,8 @@ class LanguageServer(Server): def __init__( self, - name: str = None, - version: str = None, + name: str, + version: str, loop=None, protocol_cls=LanguageServerProtocol, converter_factory=default_converter, diff --git a/tests/test_server_connection.py b/tests/test_server_connection.py index e6ef85a4..dfce294d 100644 --- a/tests/test_server_connection.py +++ b/tests/test_server_connection.py @@ -94,7 +94,7 @@ async def test_ws_server(): """Smoke test to ensure we can send/receive messages over websockets""" loop = asyncio.new_event_loop() - server = LanguageServer(loop=loop) + server = LanguageServer('pygls-test', 'v1', loop=loop) # Run the server over Websockets in a separate thread server_thread = Thread(