Skip to content

Commit e8cbb19

Browse files
committed
feat: add Deno LSP client implementation
1 parent 1d6d4cf commit e8cbb19

File tree

5 files changed

+475
-0
lines changed

5 files changed

+475
-0
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 | Purpose |
8+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
9+
| `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` | Requests timing averages for Deno's internal instrumentation for monitoring and debugging. |
11+
| `deno/reloadImportRegistries` | Reloads cached responses from import registries. |
12+
| `deno/virtualTextDocument` | Requests read-only virtual text documents (e.g., cached remote modules or Deno library files) using the `deno:` schema. |
13+
| `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 | Purpose |
18+
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
19+
| `deno/registryState` | Notifies the client about import registry discovery status, providing suggestions for configuration updates. |
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+
- `deno/testRun`: Initiates a test execution for specified modules or tests.
28+
- `deno/testRunCancel`: Cancels an ongoing test run by ID.
29+
30+
### Notifications (Server → Client)
31+
32+
- `deno/testModule`: Sent when a test module is discovered.
33+
- `deno/testModuleDelete`: Sent when a test module is deleted.
34+
- `deno/testRunProgress`: Tracks the progress and state of a test run (`enqueued`, `started`, `passed`, `failed`, etc.).
35+
36+
## Other Experimental Capabilities
37+
38+
- `denoConfigTasks`: Support for tasks defined in Deno configuration files.
39+
- `didRefreshDenoConfigurationTreeNotifications`: Notifications for when the configuration tree is refreshed.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from .client import DenoClient, DenoDockerServer, DenoLocalServer
4+
5+
__all__ = [
6+
"DenoClient",
7+
"DenoDockerServer",
8+
"DenoLocalServer",
9+
]
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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.docker import DockerServer
28+
from lsp_client.server.local import LocalServer
29+
from lsp_client.utils.types import lsp_type
30+
31+
DenoLocalServer = partial(LocalServer, command=["deno", "lsp"])
32+
DenoDockerServer = partial(DockerServer, image="lspcontainers/denols:2.4.2")
33+
34+
35+
@define
36+
class DenoClient(
37+
LSPClient,
38+
WithRequestHover,
39+
WithRequestDefinition,
40+
WithRequestReferences,
41+
WithRequestImplementation,
42+
WithRequestTypeDefinition,
43+
WithRequestCallHierarchy,
44+
WithRequestDocumentSymbol,
45+
WithRequestWorkspaceSymbol,
46+
WithReceiveLogMessage,
47+
WithReceivePublishDiagnostics,
48+
):
49+
"""
50+
- Language: TypeScript, JavaScript
51+
- Homepage: https://deno.land/
52+
- Doc: https://docs.deno.com/runtime/reference/lsp_integration/
53+
- Github: https://github.com/denoland/deno
54+
- VSCode Extension: https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno
55+
"""
56+
57+
enable: bool = True
58+
unstable: bool = False
59+
lint: bool = True
60+
61+
config: str | None = None
62+
import_map: str | None = None
63+
64+
code_lens_implementations: bool = True
65+
code_lens_references: bool = True
66+
code_lens_references_all_functions: bool = True
67+
code_lens_test: bool = True
68+
69+
suggest_complete_function_calls: bool = True
70+
suggest_names: bool = True
71+
suggest_paths: bool = True
72+
suggest_auto_imports: bool = True
73+
suggest_imports_auto_discover: bool = True
74+
suggest_imports_hosts: list[str] = Factory(list)
75+
76+
testing_enable: bool = False
77+
testing_args: list[str] = Factory(list)
78+
79+
@override
80+
def get_language_id(self) -> lsp_type.LanguageKind:
81+
return lsp_type.LanguageKind.TypeScript
82+
83+
@override
84+
def create_initialization_options(self) -> dict[str, Any]:
85+
options: dict[str, Any] = {
86+
"enable": self.enable,
87+
"unstable": self.unstable,
88+
"lint": self.lint,
89+
"codeLens": {
90+
"implementations": self.code_lens_implementations,
91+
"references": self.code_lens_references,
92+
"referencesAllFunctions": self.code_lens_references_all_functions,
93+
"test": self.code_lens_test,
94+
},
95+
"suggest": {
96+
"completeFunctionCalls": self.suggest_complete_function_calls,
97+
"names": self.suggest_names,
98+
"paths": self.suggest_paths,
99+
"autoImports": self.suggest_auto_imports,
100+
"imports": {
101+
"autoDiscover": self.suggest_imports_auto_discover,
102+
"hosts": list(self.suggest_imports_hosts),
103+
},
104+
},
105+
}
106+
107+
if self.config:
108+
options["config"] = self.config
109+
110+
if self.import_map:
111+
options["importMap"] = self.import_map
112+
113+
if self.testing_enable:
114+
options["testing"] = {
115+
"enable": self.testing_enable,
116+
"args": list(self.testing_args),
117+
}
118+
119+
return options
120+
121+
@override
122+
def check_server_compatibility(self, info: lsp_type.ServerInfo | None) -> None:
123+
return
124+
125+
@override
126+
async def ensure_installed(self) -> None:
127+
if shutil.which("deno"):
128+
return
129+
130+
logger.warning("deno not found, attempting to install...")
131+
132+
try:
133+
await anyio.run_process(["npm", "install", "-g", "deno"])
134+
logger.info("Successfully installed deno via npm")
135+
return
136+
except CalledProcessError as e:
137+
raise RuntimeError(
138+
"Could not install deno. Please install it manually with 'npm install -g deno'. "
139+
"See https://deno.land/ for more information."
140+
) from e
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Sequence
4+
from typing import Protocol, override
5+
6+
from lsp_client.protocol import (
7+
CapabilityClientProtocol,
8+
CapabilityProtocol,
9+
ServerRequestHookProtocol,
10+
)
11+
12+
13+
class WithDenoCacheRequest(
14+
ServerRequestHookProtocol,
15+
CapabilityProtocol,
16+
CapabilityClientProtocol,
17+
Protocol,
18+
):
19+
@override
20+
@classmethod
21+
def methods(cls) -> Sequence[str]:
22+
return ("deno/cache",)

0 commit comments

Comments
 (0)