diff options
Diffstat (limited to 'docs/source/pages/migrating-to-v1.rst')
-rw-r--r-- | docs/source/pages/migrating-to-v1.rst | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/docs/source/pages/migrating-to-v1.rst b/docs/source/pages/migrating-to-v1.rst new file mode 100644 index 0000000..527a810 --- /dev/null +++ b/docs/source/pages/migrating-to-v1.rst @@ -0,0 +1,241 @@ +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. + +Known Migrations +---------------- +You may find insight and inspiration from these projects that have already successfully migrated to v1: + +* `jedi-language-server`_ +* `vscode-ruff`_ +* `esbonio`_ +* `yara-language-server`_ + +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 "<stdin>", line 1, in <module> + | File "/.../python3.10/site-packages/cattrs/converters.py", li + ne 309, in structure + | return self._structure_func.dispatch(cl)(obj, cl) + | File "<cattrs generated structure __main__.ExampleConfig-2>", 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 "<cattrs generated structure __main__.ExampleConfig-2>", 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="v1.0", converter_factory=custom_converter + ) + +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`` +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. 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 +.. _jedi-language-server: https://github.com/pappasam/jedi-language-server/pull/230 +.. _yara-language-server: https://github.com/avast/yls/pull/34 +.. _vscode-ruff: https://github.com/charliermarsh/vscode-ruff/pull/37 +.. _esbonio: https://github.com/swyddfa/esbonio/pull/484 |