Skip to content

Commit 114c971

Browse files
committed
fix: fix default config
1 parent 4bca82e commit 114c971

File tree

19 files changed

+1143
-623
lines changed

19 files changed

+1143
-623
lines changed

LSP_Technical_Documentation.md

Lines changed: 772 additions & 0 deletions
Large diffs are not rendered by default.

examples/default_configuration.py

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

src/lsp_client/capability/server_request/configuration.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
from loguru import logger
77

8+
from lsp_client.capability.notification.did_change_configuration import (
9+
WithNotifyDidChangeConfiguration,
10+
)
811
from lsp_client.protocol import (
912
CapabilityClientProtocol,
1013
ServerRequestHook,
@@ -27,7 +30,8 @@ class WithRespondConfigurationRequest(
2730
`workspace/configuration` - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_configuration
2831
"""
2932

30-
configuration_map: ConfigurationMap | None = None
33+
def create_default_config(self) -> dict[str, Any] | None:
34+
return
3135

3236
@override
3337
@classmethod
@@ -54,14 +58,16 @@ def get_configuration(self, scope_uri: str | None, section: str | None) -> Any:
5458
5559
Default implementation uses `self.configuration_map` if available.
5660
"""
57-
if self.configuration_map:
58-
return self.configuration_map.get(scope_uri, section)
59-
return None
61+
62+
if not (config := self.get_config_map()):
63+
return
64+
return config.get(scope_uri, section)
6065

6166
async def _respond_configuration(
6267
self, params: lsp_type.ConfigurationParams
6368
) -> list[Any]:
6469
logger.debug("Responding to configuration request")
70+
6571
return [
6672
self.get_configuration(item.scope_uri, item.section)
6773
for item in params.items
@@ -81,24 +87,17 @@ def register_server_request_hooks(
8187
) -> None:
8288
super().register_server_request_hooks(registry)
8389

84-
# Automatically bind change notification if both capabilities are present
85-
from lsp_client.capability.notification.did_change_configuration import (
86-
WithNotifyDidChangeConfiguration,
87-
)
88-
89-
if self.configuration_map and isinstance(
90-
self, WithNotifyDidChangeConfiguration
91-
):
92-
# We use a lambda to avoid sync/async issues if the notification
93-
# needs to be scheduled on an event loop
94-
import asyncer
90+
# Notify server when configuration changes
91+
if isinstance(self, WithNotifyDidChangeConfiguration):
9592

96-
def on_config_change(config_map: ConfigurationMap, **kwargs: Any):
93+
async def notify_change(config_map: ConfigurationMap) -> None:
9794
with logger.contextualize(method="didChangeConfiguration"):
98-
logger.debug("Configuration changed, notifying server")
99-
asyncer.runnify(self.notify_change_configuration)()
95+
logger.debug(
96+
"Configuration changed: {}, notifying server", config_map
97+
)
98+
await self.notify_change_configuration()
10099

101-
self.configuration_map.on_change(on_config_change)
100+
self.get_config_map().on_change(notify_change)
102101

103102
registry.register(
104103
lsp_type.WORKSPACE_CONFIGURATION,

src/lsp_client/client/abc.py

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from collections.abc import AsyncGenerator
66
from contextlib import asynccontextmanager, suppress
77
from pathlib import Path
8-
from typing import Literal, Self, override
8+
from typing import Any, Literal, Self, override
99

1010
import anyio
1111
import asyncer
1212
from anyio import AsyncContextManagerMixin
13-
from attrs import define, field
13+
from attrs import Factory, define, field
1414
from loguru import logger
1515

1616
from lsp_client.capability.build import (
@@ -19,18 +19,14 @@
1919
)
2020
from lsp_client.capability.notification import WithNotifyTextDocumentSynchronize
2121
from lsp_client.client.buffer import LSPFileBuffer
22-
from lsp_client.client.lang import LanguageConfig
2322
from lsp_client.jsonrpc.convert import (
2423
notification_serialize,
2524
request_deserialize,
2625
request_serialize,
2726
response_deserialize,
2827
response_serialize,
2928
)
30-
from lsp_client.protocol import (
31-
CapabilityClientProtocol,
32-
CapabilityProtocol,
33-
)
29+
from lsp_client.protocol import CapabilityClientProtocol, CapabilityProtocol
3430
from lsp_client.server import DefaultServers, Server, ServerRuntimeError
3531
from lsp_client.server.types import ServerRequest
3632
from lsp_client.utils.channel import Receiver, channel
@@ -64,6 +60,7 @@ class Client(
6460
_server: Server = field(init=False)
6561
_workspace: Workspace = field(init=False)
6662
_buffer: LSPFileBuffer = field(factory=LSPFileBuffer, init=False)
63+
_config: ConfigurationMap = Factory(ConfigurationMap)
6764

6865
async def _iter_candidate_servers(self) -> AsyncGenerator[Server]:
6966
"""
@@ -95,53 +92,25 @@ async def _iter_candidate_servers(self) -> AsyncGenerator[Server]:
9592
def get_workspace(self) -> Workspace:
9693
return self._workspace
9794

95+
@override
96+
def get_config_map(self) -> ConfigurationMap:
97+
return self._config
98+
9899
def get_server(self) -> Server:
99100
return self._server
100101

101-
@abstractmethod
102-
def get_language_config(self) -> LanguageConfig:
103-
"""Get language-specific configuration for this client."""
104-
105102
@abstractmethod
106103
def create_default_servers(self) -> DefaultServers:
107104
"""Create default servers for this client."""
108105

106+
def create_default_config(self) -> dict[str, Any] | None:
107+
"""Create default configuration map for this client."""
108+
return
109+
109110
@abstractmethod
110111
def check_server_compatibility(self, info: lsp_type.ServerInfo | None) -> None:
111112
"""Check if the available server capabilities are compatible with the client."""
112113

113-
def create_default_configuration_map(self) -> ConfigurationMap | None:
114-
"""
115-
Create default configuration map for this client.
116-
117-
This method can be overridden by subclasses to provide default configurations
118-
that enable extra features like inlay hints, diagnostics, etc.
119-
120-
The base implementation returns None, meaning no default configuration.
121-
Subclasses that support configuration should override this method to provide
122-
sensible defaults that enable commonly-used features.
123-
124-
Returns:
125-
ConfigurationMap with default settings, or None if no defaults are needed.
126-
127-
Example:
128-
Override this method in a client subclass to provide defaults:
129-
130-
```python
131-
@override
132-
def create_default_configuration_map(self) -> ConfigurationMap | None:
133-
config_map = ConfigurationMap()
134-
config_map.update_global({
135-
"myserver": {
136-
"inlayHints": {"enable": True},
137-
"diagnostics": {"enable": True},
138-
}
139-
})
140-
return config_map
141-
```
142-
"""
143-
return None
144-
145114
@override
146115
@asynccontextmanager
147116
async def open_files(self, *file_paths: AnyPath) -> AsyncGenerator[None]:
@@ -173,7 +142,6 @@ async def open_files(self, *file_paths: AnyPath) -> AsyncGenerator[None]:
173142
tg.soonify(self.notify_text_document_closed)(item.file_path)
174143

175144
@override
176-
# @retry(stop=tenacity.stop_after_attempt(3), reraise=True)
177145
async def request[R](
178146
self,
179147
req: Request,
@@ -185,7 +153,6 @@ async def request[R](
185153
return response_deserialize(raw_resp, schema)
186154

187155
@override
188-
# @retry(stop=tenacity.stop_after_attempt(3), reraise=True)
189156
async def notify(self, msg: Notification) -> None:
190157
noti = notification_serialize(msg)
191158
with anyio.fail_after(self.request_timeout):
@@ -279,17 +246,8 @@ async def _run_server(
279246
async def __asynccontextmanager__(self) -> AsyncGenerator[Self]:
280247
self._workspace = format_workspace(self._workspace_arg)
281248

282-
# Initialize default configuration map if the client supports configuration
283-
# and no configuration map has been set yet
284-
from lsp_client.capability.server_request import WithRespondConfigurationRequest
285-
286-
if (
287-
isinstance(self, WithRespondConfigurationRequest)
288-
and self.configuration_map is None
289-
):
290-
default_config = self.create_default_configuration_map()
291-
if default_config is not None:
292-
self.configuration_map = default_config
249+
if init_config := self.create_default_config():
250+
await self._config.update_global(init_config)
293251

294252
async with (
295253
asyncer.create_task_group() as tg,

src/lsp_client/clients/__init__.py

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,25 @@
44

55
from .deno import DenoClient
66
from .gopls import GoplsClient
7-
from .language_client import Language, LanguageClient
87
from .pyrefly import PyreflyClient
98
from .pyright import PyrightClient
109
from .rust_analyzer import RustAnalyzerClient
1110
from .ty import TyClient
1211
from .typescript import TypescriptClient
1312

14-
GoClient = GoplsClient
15-
PythonClient = PyrightClient
16-
RustClient = RustAnalyzerClient
17-
TypeScriptClient = TypescriptClient
18-
19-
clients: Final = (
20-
GoplsClient,
21-
PyreflyClient,
22-
PyrightClient,
23-
RustAnalyzerClient,
24-
DenoClient,
25-
TypescriptClient,
26-
TyClient,
27-
)
13+
clients: Final = {
14+
"gopls": GoplsClient,
15+
"pyrefly": PyreflyClient,
16+
"pyright": PyrightClient,
17+
"rust_analyzer": RustAnalyzerClient,
18+
"deno": DenoClient,
19+
"typescript": TypescriptClient,
20+
"ty": TyClient,
21+
}
2822

2923
__all__ = [
3024
"DenoClient",
31-
"GoClient",
3225
"GoplsClient",
33-
"Language",
34-
"LanguageClient",
3526
"PyreflyClient",
36-
"PythonClient",
37-
"RustClient",
3827
"TyClient",
39-
"TypeScriptClient",
4028
]

0 commit comments

Comments
 (0)