From acd234793c0999c72d6dc05f19d38bf13e6b4ae7 Mon Sep 17 00:00:00 2001 From: Christian Kissig Date: Wed, 3 Jun 2026 20:06:08 +0100 Subject: [PATCH] Expand ServerCapabilities to the full spec field set ServerCapabilities previously exposed only positionEncoding. Add the documented provider/capability fields from the LSP initialize spec: sync, completion, hover, definition, references, formatting, rename, code actions/lens, semantic tokens, diagnostics, workspace, experimental and the rest. Following this module's convention, dedicated XxxOptions objects are typed as dict and `boolean | XxxOptions` unions as bool|dict. Also add the TextDocumentSyncKind enum used by textDocumentSync. Co-Authored-By: Claude Opus 4.8 (1M context) --- lsp_client/__init__.py | 2 + lsp_client/protocol.py | 90 +++++++++++++++++++++++++++++++++++++++++- tests/test_protocol.py | 52 ++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/lsp_client/__init__.py b/lsp_client/__init__.py index fb203bf..f67a9c1 100644 --- a/lsp_client/__init__.py +++ b/lsp_client/__init__.py @@ -43,6 +43,7 @@ TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + TextDocumentSyncKind, WorkDoneProgressBegin, WorkDoneProgressCancelNotification, WorkDoneProgressCancelParams, @@ -104,6 +105,7 @@ "TextDocumentIdentifier", "TextDocumentItem", "TextDocumentPositionParams", + "TextDocumentSyncKind", "WorkDoneProgressBegin", "WorkDoneProgressCancelNotification", "WorkDoneProgressCancelParams", diff --git a/lsp_client/protocol.py b/lsp_client/protocol.py index 34d069b..2d7ecfd 100644 --- a/lsp_client/protocol.py +++ b/lsp_client/protocol.py @@ -207,15 +207,101 @@ class ClientCapabilities(BaseModel): experimental: dict | None = None +class TextDocumentSyncKind(IntEnum): + """Defines how the host (editor) should sync document changes to the + language server.""" + + #: Documents should not be synced at all. + None_ = 0 + #: Documents are synced by always sending the full content of the document. + Full = 1 + #: Documents are synced by sending the full content on open, then + #: incremental change notifications afterwards. + Incremental = 2 + + class ServerCapabilities(BaseModel): - """Subset of server capabilities relevant to position encoding negotiation. + """The capabilities a language server provides. - Returned by the server in the ``InitializeResult``. + Returned by the server in the :class:`InitializeResult`. + + Provider options that the spec models as dedicated ``XxxOptions`` / + ``XxxRegistrationOptions`` objects are accepted here as ``dict`` (consistent + with the rest of this module), while ``boolean | XxxOptions`` unions accept + ``bool | dict``. """ #: The position encoding the server picked from the client's advertised #: ``positionEncodings``. If absent, ``utf-16`` is assumed. @since 3.17.0 positionEncoding: PositionEncodingKind | None = None + #: Defines how text documents are synced. + textDocumentSync: TextDocumentSyncKind | dict | None = None + #: Defines how notebook documents are synced. @since 3.17.0 + notebookDocumentSync: dict | None = None + #: The server provides completion support. + completionProvider: dict | None = None + #: The server provides hover support. + hoverProvider: bool | dict | None = None + #: The server provides signature help support. + signatureHelpProvider: dict | None = None + #: The server provides go to declaration support. @since 3.14.0 + declarationProvider: bool | dict | None = None + #: The server provides goto definition support. + definitionProvider: bool | dict | None = None + #: The server provides goto type definition support. @since 3.6.0 + typeDefinitionProvider: bool | dict | None = None + #: The server provides goto implementation support. @since 3.6.0 + implementationProvider: bool | dict | None = None + #: The server provides find references support. + referencesProvider: bool | dict | None = None + #: The server provides document highlight support. + documentHighlightProvider: bool | dict | None = None + #: The server provides document symbol support. + documentSymbolProvider: bool | dict | None = None + #: The server provides code actions. + codeActionProvider: bool | dict | None = None + #: The server provides code lens. + codeLensProvider: dict | None = None + #: The server provides document link support. + documentLinkProvider: dict | None = None + #: The server provides color provider support. @since 3.6.0 + colorProvider: bool | dict | None = None + #: The server provides document formatting. + documentFormattingProvider: bool | dict | None = None + #: The server provides document range formatting. + documentRangeFormattingProvider: bool | dict | None = None + #: The server provides document formatting on typing. + documentOnTypeFormattingProvider: dict | None = None + #: The server provides rename support. + renameProvider: bool | dict | None = None + #: The server provides folding provider support. @since 3.10.0 + foldingRangeProvider: bool | dict | None = None + #: The server provides execute command support. + executeCommandProvider: dict | None = None + #: The server provides selection range support. @since 3.15.0 + selectionRangeProvider: bool | dict | None = None + #: The server provides linked editing range support. @since 3.16.0 + linkedEditingRangeProvider: bool | dict | None = None + #: The server provides call hierarchy support. @since 3.16.0 + callHierarchyProvider: bool | dict | None = None + #: The server provides semantic tokens support. @since 3.16.0 + semanticTokensProvider: dict | None = None + #: The server provides moniker support. @since 3.16.0 + monikerProvider: bool | dict | None = None + #: The server provides type hierarchy support. @since 3.17.0 + typeHierarchyProvider: bool | dict | None = None + #: The server provides inline values. @since 3.17.0 + inlineValueProvider: bool | dict | None = None + #: The server provides inlay hints. @since 3.17.0 + inlayHintProvider: bool | dict | None = None + #: The server has support for pull model diagnostics. @since 3.17.0 + diagnosticProvider: dict | None = None + #: The server provides workspace symbol support. + workspaceSymbolProvider: bool | dict | None = None + #: Workspace-specific server capabilities. + workspace: dict | None = None + #: Experimental server capabilities. + experimental: Any | None = None class TextDocumentClientCapabilities(BaseModel): diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 03d101c..aaa9483 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -44,6 +44,7 @@ TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, + TextDocumentSyncKind, WorkDoneProgressBegin, WorkDoneProgressCancelNotification, WorkDoneProgressCancelParams, @@ -464,6 +465,57 @@ def test_server_capabilities_position_encoding_optional(): assert ServerCapabilities().model_dump(exclude_none=True) == {} +def test_text_document_sync_kind_values(): + assert TextDocumentSyncKind.None_ == 0 + assert TextDocumentSyncKind.Full == 1 + assert TextDocumentSyncKind.Incremental == 2 + + +def test_server_capabilities_providers_serialise(): + caps = ServerCapabilities( + textDocumentSync=TextDocumentSyncKind.Incremental, + hoverProvider=True, + definitionProvider=True, + completionProvider={"triggerCharacters": ["."]}, + renameProvider={"prepareProvider": True}, + ) + data = caps.model_dump(exclude_none=True) + assert data == { + "textDocumentSync": 2, + "hoverProvider": True, + "definitionProvider": True, + "completionProvider": {"triggerCharacters": ["."]}, + "renameProvider": {"prepareProvider": True}, + } + + +def test_server_capabilities_text_document_sync_accepts_options_dict(): + # textDocumentSync may also be a TextDocumentSyncOptions object (dict). + caps = ServerCapabilities( + textDocumentSync={"openClose": True, "change": 2}, + ) + assert caps.model_dump(exclude_none=True) == { + "textDocumentSync": {"openClose": True, "change": 2} + } + + +def test_server_capabilities_parses_from_response_payload(): + caps = ServerCapabilities.model_validate( + { + "positionEncoding": "utf-8", + "textDocumentSync": 1, + "hoverProvider": True, + "diagnosticProvider": {"interFileDependencies": False}, + "experimental": {"customFeature": True}, + } + ) + assert caps.positionEncoding == PositionEncodingKind.UTF8 + assert caps.textDocumentSync == TextDocumentSyncKind.Full + assert caps.hoverProvider is True + assert caps.diagnosticProvider == {"interFileDependencies": False} + assert caps.experimental == {"customFeature": True} + + def test_initialize_result_minimal(): result = InitializeResult(capabilities=ServerCapabilities()) data = result.model_dump(exclude_none=True)