Skip to content

Commit 2b950fb

Browse files
committed
Fix LSP client capability declarations to match server implementations
- Update all clients to correctly declare supported capabilities - Add missing inlay hint and signature help support where servers provide it - Remove unsupported diagnostic capabilities from clients - Add comprehensive capability support to DenoClient - Ensure client-server capability alignment for reliable LSP communication
1 parent 2695d98 commit 2b950fb

File tree

9 files changed

+110
-64
lines changed

9 files changed

+110
-64
lines changed

src/lsp_client/clients/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from __future__ import annotations
22

3+
from typing import Final
4+
5+
from .deno import DenoClient
36
from .gopls import GoplsClient
47
from .pyrefly import PyreflyClient
58
from .pyright import PyrightClient
@@ -11,11 +14,22 @@
1114
RustClient = RustAnalyzerClient
1215
TypeScriptClient = TypescriptClient
1316

17+
clients: Final = (
18+
GoplsClient,
19+
PyreflyClient,
20+
PyrightClient,
21+
RustAnalyzerClient,
22+
DenoClient,
23+
TypescriptClient,
24+
)
25+
1426
__all__ = [
27+
"DenoClient",
1528
"GoClient",
1629
"GoplsClient",
1730
"PyreflyClient",
1831
"PythonClient",
1932
"RustClient",
2033
"TypeScriptClient",
34+
"client",
2135
]

src/lsp_client/clients/deno/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,36 @@
99
from attrs import Factory, define
1010
from loguru import logger
1111

12+
from lsp_client.capability.notification import (
13+
WithNotifyDidChangeConfiguration,
14+
)
1215
from lsp_client.capability.request import (
1316
WithRequestCallHierarchy,
1417
WithRequestCompletion,
1518
WithRequestDefinition,
1619
WithRequestDocumentSymbol,
1720
WithRequestHover,
1821
WithRequestImplementation,
22+
WithRequestInlayHint,
1923
WithRequestPullDiagnostic,
2024
WithRequestReferences,
25+
WithRequestSignatureHelp,
2126
WithRequestTypeDefinition,
2227
WithRequestWorkspaceSymbol,
2328
)
2429
from lsp_client.capability.server_notification import (
30+
WithReceiveLogTrace,
2531
WithReceivePublishDiagnostics,
32+
WithReceiveShowMessage,
2633
)
2734
from lsp_client.capability.server_notification.log_message import WithReceiveLogMessage
35+
from lsp_client.capability.server_request import (
36+
WithRespondConfigurationRequest,
37+
WithRespondInlayHintRefresh,
38+
WithRespondShowDocumentRequest,
39+
WithRespondShowMessageRequest,
40+
WithRespondWorkspaceFoldersRequest,
41+
)
2842
from lsp_client.client.abc import Client
2943
from lsp_client.server import DefaultServers, ServerInstallationError
3044
from lsp_client.server.container import ContainerServer
@@ -80,6 +94,7 @@ async def ensure_deno_installed() -> None:
8094
@define
8195
class DenoClient(
8296
Client,
97+
WithNotifyDidChangeConfiguration,
8398
WithRequestHover,
8499
WithRequestCompletion,
85100
WithRequestDefinition,
@@ -88,10 +103,19 @@ class DenoClient(
88103
WithRequestTypeDefinition,
89104
WithRequestCallHierarchy,
90105
WithRequestDocumentSymbol,
106+
WithRequestInlayHint,
91107
WithRequestPullDiagnostic,
108+
WithRequestSignatureHelp,
92109
WithRequestWorkspaceSymbol,
93110
WithReceiveLogMessage,
111+
WithReceiveLogTrace,
94112
WithReceivePublishDiagnostics,
113+
WithReceiveShowMessage,
114+
WithRespondConfigurationRequest,
115+
WithRespondInlayHintRefresh,
116+
WithRespondShowDocumentRequest,
117+
WithRespondShowMessageRequest,
118+
WithRespondWorkspaceFoldersRequest,
95119
WithRequestDenoCache,
96120
WithRequestDenoPerformance,
97121
WithRequestDenoReloadImportRegistries,

src/lsp_client/clients/gopls.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@
4141
from lsp_client.server.local import LocalServer
4242
from lsp_client.utils.types import lsp_type
4343

44-
GoplsContainerServer = partial(
45-
ContainerServer, image="ghcr.io/observerw/lsp-client/gopls:latest"
46-
)
44+
GoplsContainerServer = partial(ContainerServer, image="ghcr.io/lsp-client/gopls:latest")
4745

4846

4947
async def ensure_gopls_installed() -> None:

src/lsp_client/clients/pyrefly.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
WithRequestDocumentSymbol,
2121
WithRequestHover,
2222
WithRequestImplementation,
23-
WithRequestPullDiagnostic,
23+
WithRequestInlayHint,
2424
WithRequestReferences,
25+
WithRequestSignatureHelp,
2526
WithRequestTypeDefinition,
2627
WithRequestWorkspaceSymbol,
2728
)
@@ -33,6 +34,7 @@
3334
)
3435
from lsp_client.capability.server_request import (
3536
WithRespondConfigurationRequest,
37+
WithRespondInlayHintRefresh,
3638
WithRespondShowDocumentRequest,
3739
WithRespondShowMessageRequest,
3840
WithRespondWorkspaceFoldersRequest,
@@ -87,15 +89,17 @@ class PyreflyClient(
8789
WithRequestDocumentSymbol,
8890
WithRequestHover,
8991
WithRequestImplementation,
90-
WithRequestPullDiagnostic,
92+
WithRequestInlayHint,
9193
WithRequestReferences,
94+
WithRequestSignatureHelp,
9295
WithRequestTypeDefinition,
9396
WithRequestWorkspaceSymbol,
9497
WithReceiveLogMessage,
9598
WithReceiveLogTrace,
9699
WithReceivePublishDiagnostics,
97100
WithReceiveShowMessage,
98101
WithRespondConfigurationRequest,
102+
WithRespondInlayHintRefresh,
99103
WithRespondShowDocumentRequest,
100104
WithRespondShowMessageRequest,
101105
WithRespondWorkspaceFoldersRequest,

src/lsp_client/clients/pyright.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
WithRequestDefinition,
2020
WithRequestDocumentSymbol,
2121
WithRequestHover,
22-
WithRequestPullDiagnostic,
2322
WithRequestReferences,
23+
WithRequestSignatureHelp,
2424
WithRequestTypeDefinition,
2525
WithRequestWorkspaceSymbol,
2626
)
@@ -32,6 +32,7 @@
3232
)
3333
from lsp_client.capability.server_request import (
3434
WithRespondConfigurationRequest,
35+
WithRespondInlayHintRefresh,
3536
WithRespondShowDocumentRequest,
3637
WithRespondShowMessageRequest,
3738
WithRespondWorkspaceFoldersRequest,
@@ -82,15 +83,16 @@ class PyrightClient(
8283
WithRequestDefinition,
8384
WithRequestDocumentSymbol,
8485
WithRequestHover,
85-
WithRequestPullDiagnostic,
8686
WithRequestReferences,
87+
WithRequestSignatureHelp,
8788
WithRequestTypeDefinition,
8889
WithRequestWorkspaceSymbol,
8990
WithReceiveLogMessage,
9091
WithReceiveLogTrace,
9192
WithReceivePublishDiagnostics,
9293
WithReceiveShowMessage,
9394
WithRespondConfigurationRequest,
95+
WithRespondInlayHintRefresh,
9496
WithRespondShowDocumentRequest,
9597
WithRespondShowMessageRequest,
9698
WithRespondWorkspaceFoldersRequest,

src/lsp_client/clients/rust_analyzer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
WithRequestDocumentSymbol,
2121
WithRequestHover,
2222
WithRequestImplementation,
23+
WithRequestInlayHint,
2324
WithRequestPullDiagnostic,
2425
WithRequestReferences,
26+
WithRequestSignatureHelp,
2527
WithRequestTypeDefinition,
2628
WithRequestWorkspaceSymbol,
2729
)
@@ -33,6 +35,7 @@
3335
)
3436
from lsp_client.capability.server_request import (
3537
WithRespondConfigurationRequest,
38+
WithRespondInlayHintRefresh,
3639
WithRespondShowDocumentRequest,
3740
WithRespondShowMessageRequest,
3841
WithRespondWorkspaceFoldersRequest,
@@ -83,15 +86,18 @@ class RustAnalyzerClient(
8386
WithRequestDocumentSymbol,
8487
WithRequestHover,
8588
WithRequestImplementation,
89+
WithRequestInlayHint,
8690
WithRequestPullDiagnostic,
8791
WithRequestReferences,
92+
WithRequestSignatureHelp,
8893
WithRequestTypeDefinition,
8994
WithRequestWorkspaceSymbol,
9095
WithReceiveLogMessage,
9196
WithReceiveLogTrace,
9297
WithReceivePublishDiagnostics,
9398
WithReceiveShowMessage,
9499
WithRespondConfigurationRequest,
100+
WithRespondInlayHintRefresh,
95101
WithRespondShowDocumentRequest,
96102
WithRespondShowMessageRequest,
97103
WithRespondWorkspaceFoldersRequest,

src/lsp_client/clients/typescript.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
WithRequestDocumentSymbol,
1919
WithRequestHover,
2020
WithRequestImplementation,
21-
WithRequestPullDiagnostic,
21+
WithRequestInlayHint,
2222
WithRequestReferences,
23+
WithRequestSignatureHelp,
2324
WithRequestTypeDefinition,
2425
WithRequestWorkspaceSymbol,
2526
)
@@ -31,6 +32,7 @@
3132
from lsp_client.capability.server_notification.log_message import WithReceiveLogMessage
3233
from lsp_client.capability.server_request import (
3334
WithRespondConfigurationRequest,
35+
WithRespondInlayHintRefresh,
3436
WithRespondShowDocumentRequest,
3537
WithRespondShowMessageRequest,
3638
WithRespondWorkspaceFoldersRequest,
@@ -88,13 +90,15 @@ class TypescriptClient(
8890
WithRequestImplementation,
8991
WithRequestTypeDefinition,
9092
WithRequestDocumentSymbol,
91-
WithRequestPullDiagnostic,
93+
WithRequestInlayHint,
94+
WithRequestSignatureHelp,
9295
WithRequestWorkspaceSymbol,
9396
WithReceiveLogMessage,
9497
WithReceiveLogTrace,
9598
WithReceivePublishDiagnostics,
9699
WithReceiveShowMessage,
97100
WithRespondConfigurationRequest,
101+
WithRespondInlayHintRefresh,
98102
WithRespondShowDocumentRequest,
99103
WithRespondShowMessageRequest,
100104
WithRespondWorkspaceFoldersRequest,

tests/test_capabilities.py

Lines changed: 0 additions & 55 deletions
This file was deleted.

tests/test_inspect_clients.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from __future__ import annotations
2+
3+
import shutil
4+
import subprocess
5+
6+
import pytest
7+
8+
from lsp_client.clients import clients
9+
from lsp_client.utils.inspect import inspect_capabilities
10+
11+
12+
def has_docker() -> bool:
13+
if not shutil.which("docker"):
14+
return False
15+
try:
16+
subprocess.run(["docker", "info"], check=True, capture_output=True)
17+
return True
18+
except (subprocess.CalledProcessError, FileNotFoundError):
19+
return False
20+
21+
22+
@pytest.mark.skipif(not has_docker(), reason="Docker not available")
23+
@pytest.mark.asyncio
24+
@pytest.mark.parametrize("client_cls", clients)
25+
async def test_client_capabilities_match_container(client_cls):
26+
client = client_cls()
27+
servers = client.create_default_servers()
28+
server = servers.container
29+
30+
if server is None:
31+
pytest.skip(f"No container server defined for {client_cls.__name__}")
32+
33+
mismatches = []
34+
async for result in inspect_capabilities(server, client_cls):
35+
# We only care about capabilities that the client CLAIMS to support but the server doesn't,
36+
# or vice versa (though usually we care more about the client expecting something the server doesn't provide)
37+
if result.client != result.server:
38+
mismatches.append(
39+
f"{result.capability}: client={result.client}, server={result.server}"
40+
)
41+
42+
if mismatches:
43+
# For now, let's just log them or fail if we want strict matching.
44+
# The user asked to "detect", so failing on mismatch is a good way to ensure they stay in sync.
45+
# However, some servers might legitimately not support some things that we've implemented in the client (maybe for newer versions).
46+
# But usually they should match if we are targeting a specific version in the container.
47+
pytest.fail(
48+
f"Capability mismatch for {client_cls.__name__}:\n" + "\n".join(mismatches)
49+
)

0 commit comments

Comments
 (0)