Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,13 @@ if __name__ == "__main__":

The library includes pre-configured clients for popular language servers:

| Language Server | Module Path | Language |
| --------------- | ---------------------------------- | --------------------- |
| Pyright | `lsp_client.clients.pyright` | Python |
| Pyrefly | `lsp_client.clients.pyrefly` | Python |
| Rust Analyzer | `lsp_client.clients.rust_analyzer` | Rust |
| Deno | `lsp_client.clients.deno` | TypeScript/JavaScript |
| Language Server | Module Path | Language |
| ---------------------------- | ------------------------------------ | --------------------- |
| Pyright | `lsp_client.clients.pyright` | Python |
| Pyrefly | `lsp_client.clients.pyrefly` | Python |
| Rust Analyzer | `lsp_client.clients.rust_analyzer` | Rust |
| Deno | `lsp_client.clients.deno` | TypeScript/JavaScript |
| TypeScript Language Server | `lsp_client.clients.typescript` | TypeScript/JavaScript |

## Contributing

Expand Down
44 changes: 44 additions & 0 deletions examples/typescript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Example: Using TypeScript Language Server for TypeScript code analysis
#
# This example demonstrates how to use the TypeScript Language Server
# to find definition locations in TypeScript code. The typescript-language-server
# provides powerful static analysis for TypeScript and JavaScript projects.

from __future__ import annotations

from pathlib import Path

import anyio

from lsp_client import Position # noqa: F401 - Used in commented example code
from lsp_client.clients.typescript import TypescriptClient, TypescriptServer


async def main():
# Set up workspace directory
workspace = Path.cwd()
async with TypescriptClient(
server=TypescriptServer(),
workspace=workspace,
) as client:
# Example: Find definition of TypescriptClient at line 13, column 28
# This demonstrates the definition lookup capability
print(f"Using language server for: {client.get_language_id()}")

# In a real scenario, you would provide a TypeScript file path
# refs = await client.request_definition_locations(
# file_path="src/index.ts",
# position=Position(10, 15),
# )
#
# if not refs:
# print("No definition locations found.")
# return
#
# # Display all definition locations found
# for ref in refs:
# print(f"Definition location found at {ref.uri} - Range: {ref.range}")


if __name__ == "__main__":
anyio.run(main) # Run the async example
2 changes: 2 additions & 0 deletions src/lsp_client/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
from .pyrefly import PyreflyClient
from .pyright import PyrightClient
from .rust_analyzer import RustAnalyzerClient
from .typescript import TypescriptClient

clients: Final = (
DenoClient,
PyreflyClient,
PyrightClient,
RustAnalyzerClient,
TypescriptClient,
)

PythonClient = PyrightClient
Expand Down
106 changes: 106 additions & 0 deletions src/lsp_client/clients/typescript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from __future__ import annotations

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

import anyio
from attrs import define
from loguru import logger

from lsp_client.capability.request import (
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

TypescriptServer = partial(
LocalServer, command=["typescript-language-server", "--stdio"]
)
TypescriptDockerServer = partial(DockerServer, image="docker.io/lspcontainers/tsserver")


@define
class TypescriptClient(
LSPClient,
WithRequestHover,
WithRequestDefinition,
WithRequestReferences,
WithRequestImplementation,
WithRequestTypeDefinition,
WithRequestDocumentSymbol,
WithRequestWorkspaceSymbol,
WithReceiveLogMessage,
WithReceivePublishDiagnostics,
):
"""
- Language: TypeScript, JavaScript
- Homepage: https://github.com/typescript-language-server/typescript-language-server
- Doc: https://github.com/typescript-language-server/typescript-language-server#readme
- Github: https://github.com/typescript-language-server/typescript-language-server
- VSCode Extension: Built-in TypeScript support in VS Code
"""

# Preferences for TypeScript/JavaScript language features
# Reference: https://github.com/typescript-language-server/typescript-language-server#initializationoptions
suggest_complete_function_calls: bool = True
include_automatic_optional_chain_completions: bool = True
include_completions_for_module_exports: bool = True
include_completions_with_insert_text: bool = True

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

@override
def create_initialization_options(self) -> dict[str, Any]:
return {
"preferences": {
"includeCompletionsForModuleExports": self.include_completions_for_module_exports,
"includeAutomaticOptionalChainCompletions": self.include_automatic_optional_chain_completions,
"includeCompletionsWithInsertText": self.include_completions_with_insert_text,
},
"suggest": {
"completeFunctionCalls": self.suggest_complete_function_calls,
},
}

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

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

logger.warning(
"typescript-language-server not found, attempting to install via npm..."
)

try:
# typescript-language-server requires the TypeScript compiler as a peer dependency
# Reference: https://github.com/typescript-language-server/typescript-language-server#installing
await anyio.run_process(
["npm", "install", "-g", "typescript-language-server", "typescript"]
)
logger.info("Successfully installed typescript-language-server via npm")
return
except CalledProcessError as e:
raise RuntimeError(
"Could not install typescript-language-server and typescript. Please install them manually with 'npm install -g typescript-language-server typescript'. "
"See https://github.com/typescript-language-server/typescript-language-server for more information."
) from e
Loading