Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/lsp_client/capability/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Any

from lsp_client.protocol import (
ExperimentalCapabilityProtocol,
GeneralCapabilityProtocol,
NotebookCapabilityProtocol,
ServerRequestHookProtocol,
Expand All @@ -22,6 +23,7 @@ def build_client_capabilities(cls: type) -> lsp_type.ClientCapabilities:
)
window = lsp_type.WindowClientCapabilities()
general = lsp_type.GeneralClientCapabilities()
experimental: dict[str, Any] = {}

if issubclass(cls, WorkspaceCapabilityProtocol):
cls.register_workspace_capability(workspace)
Expand All @@ -33,13 +35,16 @@ def build_client_capabilities(cls: type) -> lsp_type.ClientCapabilities:
cls.register_window_capability(window)
if issubclass(cls, GeneralCapabilityProtocol):
cls.register_general_capability(general)
if issubclass(cls, ExperimentalCapabilityProtocol):
cls.register_experimental_capability(experimental)

return lsp_type.ClientCapabilities(
workspace=workspace,
text_document=text_document,
notebook_document=notebook_document,
window=window,
general=general,
experimental=experimental if experimental else None,
)


Expand Down
56 changes: 56 additions & 0 deletions src/lsp_client/clients/deno/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Deno LSP Custom Capabilities

This document summarizes the custom LSP capabilities and protocol extensions provided by the Deno Language Server.

## Custom Requests (Client → Server)

| Request | Python Method | Purpose |
| ----------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `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. |
| `deno/performance` | `request_deno_performance` | Requests timing averages for Deno's internal instrumentation for monitoring and debugging. |
| `deno/reloadImportRegistries` | `request_deno_reload_import_registries` | Reloads cached responses from import registries. |
| `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. |
| `deno/task` | `request_deno_task` | Retrieves a list of available Deno tasks defined in `deno.json` or `deno.jsonc`. |

## Custom Notifications (Server → Client)

| Notification | Python Method | Default Behavior |
| -------------------- | ----------------------------- | ---------------------------------------------------------------------------------- |
| `deno/registryState` | `receive_deno_registry_state` | Logs the registry discovery status and configuration suggestions at `DEBUG` level. |

## Testing API (Experimental)

Requires `experimental.testingApi` capability to be enabled by both client and server.

### Requests (Client → Server)

| Request | Python Method | Purpose |
| -------------------- | ------------------------------ | ---------------------------------------------------------- |
| `deno/testRun` | `request_deno_test_run` | Initiates a test execution for specified modules or tests. |
| `deno/testRunCancel` | `request_deno_test_run_cancel` | Cancels an ongoing test run by ID. |

### Notifications (Server → Client)

| Notification | Python Method | Default Behavior |
| ----------------------- | --------------------------------- | ------------------------------------------------------------------------------------------ |
| `deno/testModule` | `receive_deno_test_module` | Logs discovered test module information at `DEBUG` level. |
| `deno/testModuleDelete` | `receive_deno_test_module_delete` | Logs deleted test module information at `DEBUG` level. |
| `deno/testRunProgress` | `receive_deno_test_run_progress` | Logs test run state (`enqueued`, `started`, `passed`, etc.) and progress at `DEBUG` level. |

## Other Experimental Capabilities

- `denoConfigTasks`: Support for tasks defined in Deno configuration files.
- `didRefreshDenoConfigurationTreeNotifications`: Notifications for when the configuration tree is refreshed.

## Deno LSP Documentation

### Official Documentation

- [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
- [deno lsp CLI Reference](https://docs.deno.com/runtime/reference/cli/lsp/) - Basic information about the `deno lsp` subcommand
- [Deno & Visual Studio Code](https://docs.deno.com/runtime/reference/vscode/) - Complete integration guide and setup

### Source Code & Development

- [Deno CLI LSP Source](https://github.com/denoland/deno/tree/main/cli/lsp) - The actual LSP implementation in Rust
- [VS Code Extension](https://github.com/denoland/vscode_deno) - Official VS Code extension source code
9 changes: 9 additions & 0 deletions src/lsp_client/clients/deno/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import annotations

from .client import DenoClient, DenoDockerServer, DenoLocalServer

__all__ = [
"DenoClient",
"DenoDockerServer",
"DenoLocalServer",
]
165 changes: 165 additions & 0 deletions src/lsp_client/clients/deno/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
from __future__ import annotations

import shutil
from functools import partial
from subprocess import CalledProcessError
from typing import Any, override

import anyio
from attrs import Factory, define
from loguru import logger

from lsp_client.capability.request import (
WithRequestCallHierarchy,
WithRequestDefinition,
WithRequestDocumentSymbol,
WithRequestHover,
WithRequestImplementation,
WithRequestReferences,
WithRequestTypeDefinition,
WithRequestWorkspaceSymbol,
)
from lsp_client.capability.server_notification import (
WithReceivePublishDiagnostics,
)
from lsp_client.capability.server_notification.log_message import WithReceiveLogMessage
from lsp_client.client.abc import LSPClient
from lsp_client.server.docker import DockerServer
from lsp_client.server.local import LocalServer
from lsp_client.utils.types import lsp_type

from .extension import (
WithReceiveDenoRegistryStatus,
WithReceiveDenoTestModule,
WithReceiveDenoTestModuleDelete,
WithReceiveDenoTestRunProgress,
WithRequestDenoCache,
WithRequestDenoPerformance,
WithRequestDenoReloadImportRegistries,
WithRequestDenoTask,
WithRequestDenoTestRun,
WithRequestDenoTestRunCancel,
WithRequestDenoVirtualTextDocument,
)

DenoLocalServer = partial(LocalServer, command=["deno", "lsp"])
DenoDockerServer = partial(DockerServer, image="lspcontainers/denols:2.4.2")


@define
class DenoClient(
LSPClient,
WithRequestHover,
WithRequestDefinition,
WithRequestReferences,
WithRequestImplementation,
WithRequestTypeDefinition,
WithRequestCallHierarchy,
WithRequestDocumentSymbol,
WithRequestWorkspaceSymbol,
WithReceiveLogMessage,
WithReceivePublishDiagnostics,
WithRequestDenoCache,
WithRequestDenoPerformance,
WithRequestDenoReloadImportRegistries,
WithRequestDenoVirtualTextDocument,
WithRequestDenoTask,
WithRequestDenoTestRun,
WithRequestDenoTestRunCancel,
WithReceiveDenoRegistryStatus,
WithReceiveDenoTestModule,
WithReceiveDenoTestModuleDelete,
WithReceiveDenoTestRunProgress,
):
"""
- Language: TypeScript, JavaScript
- Homepage: https://deno.land/
- Doc: https://docs.deno.com/runtime/reference/lsp_integration/
- Github: https://github.com/denoland/deno
- VSCode Extension: https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno
"""

enable: bool = True
unstable: bool = False
lint: bool = True

config: str | None = None
import_map: str | None = None

code_lens_implementations: bool = True
code_lens_references: bool = True
code_lens_references_all_functions: bool = True
code_lens_test: bool = True

suggest_complete_function_calls: bool = True
suggest_names: bool = True
suggest_paths: bool = True
suggest_auto_imports: bool = True
suggest_imports_auto_discover: bool = True
suggest_imports_hosts: list[str] = Factory(list)

testing_enable: bool = False
testing_args: list[str] = Factory(list)

@override
def get_language_id(self) -> lsp_type.LanguageKind:
return lsp_type.LanguageKind.TypeScript

@override
def create_initialization_options(self) -> dict[str, Any]:
options: dict[str, Any] = {
"enable": self.enable,
"unstable": self.unstable,
"lint": self.lint,
"codeLens": {
"implementations": self.code_lens_implementations,
"references": self.code_lens_references,
"referencesAllFunctions": self.code_lens_references_all_functions,
"test": self.code_lens_test,
},
"suggest": {
"completeFunctionCalls": self.suggest_complete_function_calls,
"names": self.suggest_names,
"paths": self.suggest_paths,
"autoImports": self.suggest_auto_imports,
"imports": {
"autoDiscover": self.suggest_imports_auto_discover,
"hosts": list(self.suggest_imports_hosts),
},
},
}

if self.config:
options["config"] = self.config

if self.import_map:
options["importMap"] = self.import_map

if self.testing_enable:
options["testing"] = {
"enable": self.testing_enable,
"args": list(self.testing_args),
}

return options

@override
def check_server_compatibility(self, info: lsp_type.ServerInfo | None) -> None:
return

@override
async def ensure_installed(self) -> None:
if shutil.which("deno"):
return

logger.warning("deno not found, attempting to install...")

try:
await anyio.run_process(["npm", "install", "-g", "deno"])
logger.info("Successfully installed deno via npm")
return
except CalledProcessError as e:
raise RuntimeError(
"Could not install deno. Please install it manually with 'npm install -g deno'. "
"See https://deno.land/ for more information."
) from e
Loading
Loading