Skip to content

Commit 8faa3dd

Browse files
observerwCopilot
andauthored
feat: add Deno LSP client (#5)
* feat: add Deno LSP client implementation * feat(deno): complete custom capabilities and support experimental api * refactor(deno): prefix models with Deno and add cattrs hooks * Update src/lsp_client/capability/build.py Co-authored-by: Copilot <[email protected]> * feat: update Deno installation to use shell script only - Simplify ensure_installed method to use only the official shell script - Remove fallback installation methods for cleaner implementation - Use curl -fsSL https://deno.land/install.sh | sh for installation * fix: correct subprocess import for Deno installation - Add missing subprocess import - Ensure proper shell command execution for piped install script * refactor: align DenoClient with other clients - Replace DockerServer with ContainerServer for consistency - Add create_default_server method following standard pattern - Update __init__.py to export DenoContainerServer - Remove unused DenoLocalServer and DenoDockerServer partials - Follow the same import and structure patterns as other clients --------- Co-authored-by: Copilot <[email protected]>
1 parent 50f950c commit 8faa3dd

File tree

8 files changed

+966
-1
lines changed

8 files changed

+966
-1
lines changed

src/lsp_client/capability/build.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Any
44

55
from lsp_client.protocol import (
6+
ExperimentalCapabilityProtocol,
67
GeneralCapabilityProtocol,
78
NotebookCapabilityProtocol,
89
ServerRequestHookProtocol,
@@ -22,6 +23,7 @@ def build_client_capabilities(cls: type) -> lsp_type.ClientCapabilities:
2223
)
2324
window = lsp_type.WindowClientCapabilities()
2425
general = lsp_type.GeneralClientCapabilities()
26+
experimental: dict[str, Any] = {}
2527

2628
if issubclass(cls, WorkspaceCapabilityProtocol):
2729
cls.register_workspace_capability(workspace)
@@ -33,13 +35,16 @@ def build_client_capabilities(cls: type) -> lsp_type.ClientCapabilities:
3335
cls.register_window_capability(window)
3436
if issubclass(cls, GeneralCapabilityProtocol):
3537
cls.register_general_capability(general)
38+
if issubclass(cls, ExperimentalCapabilityProtocol):
39+
cls.register_experimental_capability(experimental)
3640

3741
return lsp_type.ClientCapabilities(
3842
workspace=workspace,
3943
text_document=text_document,
4044
notebook_document=notebook_document,
4145
window=window,
4246
general=general,
47+
experimental=experimental or None,
4348
)
4449

4550

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Deno LSP Custom Capabilities
2+
3+
This document summarizes the custom LSP capabilities and protocol extensions provided by the Deno Language Server.
4+
5+
## Custom Requests (Client → Server)
6+
7+
| Request | Python Method | Purpose |
8+
| ----------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
9+
| `deno/cache` | `request_deno_cache` | Instructs Deno to cache a module and its dependencies. Usually sent as a response to a code action for un-cached modules. |
10+
| `deno/performance` | `request_deno_performance` | Requests timing averages for Deno's internal instrumentation for monitoring and debugging. |
11+
| `deno/reloadImportRegistries` | `request_deno_reload_import_registries` | Reloads cached responses from import registries. |
12+
| `deno/virtualTextDocument` | `request_deno_virtual_text_document` | Requests read-only virtual text documents (e.g., cached remote modules or Deno library files) using the `deno:` schema. |
13+
| `deno/task` | `request_deno_task` | Retrieves a list of available Deno tasks defined in `deno.json` or `deno.jsonc`. |
14+
15+
## Custom Notifications (Server → Client)
16+
17+
| Notification | Python Method | Default Behavior |
18+
| -------------------- | ----------------------------- | ---------------------------------------------------------------------------------- |
19+
| `deno/registryState` | `receive_deno_registry_state` | Logs the registry discovery status and configuration suggestions at `DEBUG` level. |
20+
21+
## Testing API (Experimental)
22+
23+
Requires `experimental.testingApi` capability to be enabled by both client and server.
24+
25+
### Requests (Client → Server)
26+
27+
| Request | Python Method | Purpose |
28+
| -------------------- | ------------------------------ | ---------------------------------------------------------- |
29+
| `deno/testRun` | `request_deno_test_run` | Initiates a test execution for specified modules or tests. |
30+
| `deno/testRunCancel` | `request_deno_test_run_cancel` | Cancels an ongoing test run by ID. |
31+
32+
### Notifications (Server → Client)
33+
34+
| Notification | Python Method | Default Behavior |
35+
| ----------------------- | --------------------------------- | ------------------------------------------------------------------------------------------ |
36+
| `deno/testModule` | `receive_deno_test_module` | Logs discovered test module information at `DEBUG` level. |
37+
| `deno/testModuleDelete` | `receive_deno_test_module_delete` | Logs deleted test module information at `DEBUG` level. |
38+
| `deno/testRunProgress` | `receive_deno_test_run_progress` | Logs test run state (`enqueued`, `started`, `passed`, etc.) and progress at `DEBUG` level. |
39+
40+
## Other Experimental Capabilities
41+
42+
- `denoConfigTasks`: Support for tasks defined in Deno configuration files.
43+
- `didRefreshDenoConfigurationTreeNotifications`: Notifications for when the configuration tree is refreshed.
44+
45+
## Deno LSP Documentation
46+
47+
### Official Documentation
48+
49+
- [Language Server Integration](https://docs.deno.com/runtime/reference/lsp_integration/) - Comprehensive guide for integrating with Deno's LSP, including custom capabilities and testing API
50+
- [deno lsp CLI Reference](https://docs.deno.com/runtime/reference/cli/lsp/) - Basic information about the `deno lsp` subcommand
51+
- [Deno & Visual Studio Code](https://docs.deno.com/runtime/reference/vscode/) - Complete integration guide and setup
52+
53+
### Source Code & Development
54+
55+
- [Deno CLI LSP Source](https://github.com/denoland/deno/tree/main/cli/lsp) - The actual LSP implementation in Rust
56+
- [VS Code Extension](https://github.com/denoland/vscode_deno) - Official VS Code extension source code
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from __future__ import annotations
2+
3+
from .client import DenoClient, DenoContainerServer
4+
5+
__all__ = [
6+
"DenoClient",
7+
"DenoContainerServer",
8+
]
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
from __future__ import annotations
2+
3+
import shutil
4+
from functools import partial
5+
from subprocess import CalledProcessError
6+
from typing import Any, override
7+
8+
import anyio
9+
from attrs import Factory, define
10+
from loguru import logger
11+
12+
from lsp_client.capability.request import (
13+
WithRequestCallHierarchy,
14+
WithRequestDefinition,
15+
WithRequestDocumentSymbol,
16+
WithRequestHover,
17+
WithRequestImplementation,
18+
WithRequestReferences,
19+
WithRequestTypeDefinition,
20+
WithRequestWorkspaceSymbol,
21+
)
22+
from lsp_client.capability.server_notification import (
23+
WithReceivePublishDiagnostics,
24+
)
25+
from lsp_client.capability.server_notification.log_message import WithReceiveLogMessage
26+
from lsp_client.client.abc import LSPClient
27+
from lsp_client.server.abc import LSPServer
28+
from lsp_client.server.container import ContainerServer
29+
from lsp_client.server.local import LocalServer
30+
from lsp_client.utils.types import lsp_type
31+
32+
from .extension import (
33+
WithReceiveDenoRegistryStatus,
34+
WithReceiveDenoTestModule,
35+
WithReceiveDenoTestModuleDelete,
36+
WithReceiveDenoTestRunProgress,
37+
WithRequestDenoCache,
38+
WithRequestDenoPerformance,
39+
WithRequestDenoReloadImportRegistries,
40+
WithRequestDenoTask,
41+
WithRequestDenoTestRun,
42+
WithRequestDenoTestRunCancel,
43+
WithRequestDenoVirtualTextDocument,
44+
)
45+
46+
DenoContainerServer = partial(
47+
ContainerServer, image="ghcr.io/observerw/lsp-client/deno:latest"
48+
)
49+
50+
51+
@define
52+
class DenoClient(
53+
LSPClient,
54+
WithRequestHover,
55+
WithRequestDefinition,
56+
WithRequestReferences,
57+
WithRequestImplementation,
58+
WithRequestTypeDefinition,
59+
WithRequestCallHierarchy,
60+
WithRequestDocumentSymbol,
61+
WithRequestWorkspaceSymbol,
62+
WithReceiveLogMessage,
63+
WithReceivePublishDiagnostics,
64+
WithRequestDenoCache,
65+
WithRequestDenoPerformance,
66+
WithRequestDenoReloadImportRegistries,
67+
WithRequestDenoVirtualTextDocument,
68+
WithRequestDenoTask,
69+
WithRequestDenoTestRun,
70+
WithRequestDenoTestRunCancel,
71+
WithReceiveDenoRegistryStatus,
72+
WithReceiveDenoTestModule,
73+
WithReceiveDenoTestModuleDelete,
74+
WithReceiveDenoTestRunProgress,
75+
):
76+
"""
77+
- Language: TypeScript, JavaScript
78+
- Homepage: https://deno.land/
79+
- Doc: https://docs.deno.com/runtime/reference/lsp_integration/
80+
- Github: https://github.com/denoland/deno
81+
- VSCode Extension: https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno
82+
"""
83+
84+
enable: bool = True
85+
unstable: bool = False
86+
lint: bool = True
87+
88+
config: str | None = None
89+
import_map: str | None = None
90+
91+
code_lens_implementations: bool = True
92+
code_lens_references: bool = True
93+
code_lens_references_all_functions: bool = True
94+
code_lens_test: bool = True
95+
96+
suggest_complete_function_calls: bool = True
97+
suggest_names: bool = True
98+
suggest_paths: bool = True
99+
suggest_auto_imports: bool = True
100+
suggest_imports_auto_discover: bool = True
101+
suggest_imports_hosts: list[str] = Factory(list)
102+
103+
testing_enable: bool = False
104+
testing_args: list[str] = Factory(list)
105+
106+
@override
107+
def get_language_id(self) -> lsp_type.LanguageKind:
108+
return lsp_type.LanguageKind.TypeScript
109+
110+
@override
111+
def create_default_server(self) -> LSPServer:
112+
return LocalServer(command=["deno", "lsp"])
113+
114+
@override
115+
def create_initialization_options(self) -> dict[str, Any]:
116+
options: dict[str, Any] = {
117+
"enable": self.enable,
118+
"unstable": self.unstable,
119+
"lint": self.lint,
120+
"codeLens": {
121+
"implementations": self.code_lens_implementations,
122+
"references": self.code_lens_references,
123+
"referencesAllFunctions": self.code_lens_references_all_functions,
124+
"test": self.code_lens_test,
125+
},
126+
"suggest": {
127+
"completeFunctionCalls": self.suggest_complete_function_calls,
128+
"names": self.suggest_names,
129+
"paths": self.suggest_paths,
130+
"autoImports": self.suggest_auto_imports,
131+
"imports": {
132+
"autoDiscover": self.suggest_imports_auto_discover,
133+
"hosts": list(self.suggest_imports_hosts),
134+
},
135+
},
136+
}
137+
138+
if self.config:
139+
options["config"] = self.config
140+
141+
if self.import_map:
142+
options["importMap"] = self.import_map
143+
144+
if self.testing_enable:
145+
options["testing"] = {
146+
"enable": self.testing_enable,
147+
"args": list(self.testing_args),
148+
}
149+
150+
return options
151+
152+
@override
153+
def check_server_compatibility(self, info: lsp_type.ServerInfo | None) -> None:
154+
return
155+
156+
@override
157+
async def ensure_installed(self) -> None:
158+
if shutil.which("deno"):
159+
return
160+
161+
logger.warning("deno not found, attempting to install...")
162+
163+
try:
164+
# Use shell to execute the piped command
165+
await anyio.run_process(
166+
["sh", "-c", "curl -fsSL https://deno.land/install.sh | sh"]
167+
)
168+
logger.info("Successfully installed deno via shell script")
169+
return
170+
except CalledProcessError as e:
171+
raise RuntimeError(
172+
"Could not install deno. Please install it manually with:\n"
173+
"curl -fsSL https://deno.land/install.sh | sh\n\n"
174+
"See https://deno.land/ for more information."
175+
) from e

0 commit comments

Comments
 (0)