Merge pull request #1778 from shiersa/feat/deno-language-server

feat: add Deno language server support
This commit is contained in:
Michael Panchenko authored and GitHub committed 2026-08-08 18:50:38 +02:00
commit 27d7d0e0ec
13 files changed
+328 -14

No files matched your search

+13 -12
View File
@@ -4,18 +4,18 @@ project_name: "project_name"
# list of language servers to start when using the LSP backend; choose from:
# ada al angular ansible bash
# bsl clojure cpp cpp_ccls crystal
# csharp csharp_omnisharp cue dart elixir
# elm erlang fortran fsharp gdscript
# go groovy haskell haxe hlsl
# html java json julia kotlin
# latex lean4 lua luau markdown
# matlab msl nix ocaml pascal
# perl php php_phpactor php_phpantom powershell
# python python_basedpyright python_jedi python_pyrefly python_ty
# qml r rego ruby ruby_solargraph
# rust scala scss solidity svelte
# swift systemverilog terraform toml typescript
# typescript_vts vue yaml zig
# csharp csharp_omnisharp cue dart deno
# elixir elm erlang fortran fsharp
# gdscript go groovy haskell haxe
# hlsl html java json julia
# kotlin latex lean4 lua luau
# markdown matlab msl nix ocaml
# pascal perl php php_phpactor php_phpantom
# powershell python python_basedpyright python_jedi python_pyrefly
# python_ty qml r rego ruby
# ruby_solargraph rust scala scss solidity
# svelte swift systemverilog terraform toml
# typescript typescript_vts vue yaml zig
# (This list may be outdated; generated with scripts/print_language_list.py;
# For the current list, see values of the LanguageServerId enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py)
@@ -25,6 +25,7 @@ project_name: "project_name"
# - For JavaScript, use typescript
# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root)
# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm)
# - For Deno projects, use deno (serves the same .ts/.js files as typescript; requires the deno CLI on PATH)
# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three)
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
@@ -0,0 +1,152 @@
"""
Provides Deno-specific instantiation of the LanguageServer class, using the
language server built into the Deno CLI (``deno lsp``).
"""
import logging
import shutil
from overrides import override
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer
from solidlsp.ls_config import LanguageServerConfig
from solidlsp.settings import SolidLSPSettings
log = logging.getLogger(__name__)
class DenoLanguageServer(SolidLanguageServer):
"""
Deno instantiation of the LanguageServer class, backed by ``deno lsp``.
Serves TypeScript/JavaScript in Deno projects. Unlike the plain
typescript-language-server, ``deno lsp`` understands Deno-specific module
resolution (``npm:`` / ``jsr:`` / ``https:`` imports) and the ``Deno.*``
global namespace.
This server overlaps the TypeScript server on file extensions and is therefore
marked experimental: it is not auto-detected and must be selected explicitly via
``languages: [deno]`` in ``project.yml``.
"""
@classmethod
def supports_implementation_request(cls) -> bool:
return True
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
super().__init__(
config,
repository_root_path,
None,
"typescript",
solidlsp_settings,
)
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)
@override
def is_ignored_dirname(self, dirname: str) -> bool:
# node_modules appears in Deno projects using npm compatibility; vendor/ holds
# vendored remote dependencies. Neither should be indexed as project sources.
return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "dist", "build"]
class DependencyProvider(LanguageServerDependencyProviderSinglePath):
def _get_or_install_core_dependency(self) -> str:
"""Return the path to the ``deno`` executable (the language server ships with it)."""
deno_path = shutil.which("deno")
if deno_path is None:
raise FileNotFoundError(
"The 'deno' executable was not found on PATH. Install Deno "
"(https://docs.deno.com/runtime/getting_started/installation/) — it bundles "
"the language server used here — and ensure 'deno' is on your PATH."
)
return deno_path
def _create_launch_command(self, core_path: str) -> list[str]:
return [core_path, "lsp"]
def _get_language_id_for_file(self, relative_file_path: str) -> str:
# deno lsp relies on the correct languageId; .tsx/.jsx in particular must not
# be sent as plain "typescript" or symbol ranges get truncated at JSX expressions.
if relative_file_path.endswith(".tsx"):
return "typescriptreact"
if relative_file_path.endswith(".jsx"):
return "javascriptreact"
if relative_file_path.endswith((".js", ".mjs", ".cjs")):
return "javascript"
return "typescript"
def _create_base_initialize_params(self) -> dict:
return {
"locale": "en",
"capabilities": {
"textDocument": {
"synchronization": {"didSave": True, "dynamicRegistration": True},
"definition": {"dynamicRegistration": True},
"references": {"dynamicRegistration": True},
"documentSymbol": {
"dynamicRegistration": True,
"hierarchicalDocumentSymbolSupport": True,
"symbolKind": {"valueSet": list(range(1, 27))},
},
"hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]},
"completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": True}},
"rename": {"dynamicRegistration": True, "prepareSupport": True},
"publishDiagnostics": {"relatedInformation": True},
},
"workspace": {
"workspaceFolders": True,
"configuration": True,
"didChangeConfiguration": {"dynamicRegistration": True},
"symbol": {"dynamicRegistration": True},
},
},
# deno lsp reads its settings from initializationOptions; enabling the server
# and the linter mirrors the defaults of the official VS Code Deno extension.
"initializationOptions": {
"enable": True,
"lint": True,
"unstable": False,
},
}
def _start_server(self) -> None:
"""Start the ``deno lsp`` process and drive the LSP initialize handshake."""
def register_capability_handler(params: dict) -> None:
return
def window_log_message(msg: dict) -> None:
log.info(f"LSP: window/logMessage: {msg}")
def do_nothing(params: dict) -> None:
return
def configuration_handler(params: dict) -> list:
# deno lsp requests workspace/configuration during startup; return an empty
# settings object per requested item so it proceeds with its defaults.
return [{} for _ in params.get("items", [])]
self.server.on_request("client/registerCapability", register_capability_handler)
self.server.on_request("workspace/configuration", configuration_handler)
self.server.on_notification("window/logMessage", window_log_message)
self.server.on_notification("$/progress", do_nothing)
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
# Deno-specific notifications emitted after config discovery; no action needed.
self.server.on_notification("deno/didRefreshDenoConfigurationTree", do_nothing)
self.server.on_notification("deno/didChangeDenoConfiguration", do_nothing)
log.info("Starting deno lsp server process")
self.server.start()
initialize_params = self._create_initialize_params()
log.info("Sending initialize request from LSP client to deno lsp and awaiting response")
init_response = self.server.send.initialize(initialize_params)
assert "textDocumentSync" in init_response["capabilities"]
assert "definitionProvider" in init_response["capabilities"]
assert "documentSymbolProvider" in init_response["capabilities"]
assert "referencesProvider" in init_response["capabilities"]
self.server.notify.initialized({})
+21
View File
@@ -286,6 +286,14 @@ class LanguageServerId(str, Enum):
project.yml — Angular LS supersedes both for Angular projects.
Must be explicitly specified in project.yml.
"""
DENO = "deno"
"""Deno's built-in language server (``deno lsp``) for Deno TypeScript/JavaScript projects.
Understands Deno module resolution (``npm:`` / ``jsr:`` / ``https:`` imports) and the
``Deno.*`` globals, which the plain typescript-language-server does not. Overlaps the
TypeScript server on file extensions (.ts/.tsx/.js/.jsx/.mts/.cts/.mjs/.cjs), so it is
experimental and must be explicitly specified via ``languages: [deno]`` in project.yml;
do not also enable typescript for the same files. Requires the ``deno`` CLI on PATH.
"""
@classmethod
def iter_all(cls, include_experimental: bool = True, include_non_programming_languages: bool = True) -> Iterable[Self]:
@@ -321,6 +329,7 @@ class LanguageServerId(str, Enum):
self.HTML,
self.SCSS,
self.ANGULAR,
self.DENO,
}
def is_programming_language(self) -> bool:
@@ -588,6 +597,14 @@ class LanguageServerId(str, Enum):
for postfix in ["x", ""]:
path_patterns.append(f".{prefix}ts{postfix}")
return FilenameMatcher(*path_patterns)
case self.DENO:
# Deno serves the same TS/JS family as the TypeScript server.
path_patterns = []
for prefix in ["c", "m", ""]:
for postfix in ["x", ""]:
for base_pattern in ["ts", "js"]:
path_patterns.append(f".{prefix}{base_pattern}{postfix}")
return FilenameMatcher(*path_patterns)
case _:
raise ValueError(f"Unhandled language: {self}")
@@ -871,6 +888,10 @@ class LanguageServerId(str, Enum):
from solidlsp.language_servers.angular_language_server import AngularLanguageServer
return AngularLanguageServer
case self.DENO:
from solidlsp.language_servers.deno_language_server import DenoLanguageServer
return DenoLanguageServer
case _:
raise ValueError(f"Unhandled language: {self}")