LSP Client
Semantic code analysis using Language Server Protocol (LSP) clients. Provides language-aware code navigation, symbol search, and safe refactoring capabilities.
Quick Start
Three ways to use the library:
1. Use Pre-built Language Clients
import anyio
from pathlib import Path
from lsp_client import Position
from lsp_client.clients.pyright import PyrightClient
async def main():
workspace = Path.cwd()
async with PyrightClient(workspace=workspace) as client:
definitions = await client.request_definition_locations(
file_path="src/main.py",
position=Position(line=10, character=5)
)
for def_loc in definitions:
file = client.from_uri(def_loc.uri)
print(f"Definition at {file}:{def_loc.range.start.line}")
anyio.run(main)
2. Create Custom Client with Selected Capabilities
See scripts/custom_client_template.py for a complete template.
Key pattern - inherit from capability mixins:
from attrs import define
from lsp_client.clients.base import PythonClientBase
from lsp_client.capability.request import (
WithRequestHover,
WithRequestDefinition,
WithRequestReferences,
)
@define
class MyClient(
PythonClientBase,
WithRequestHover,
WithRequestDefinition,
WithRequestReferences,
):
...
3. Use Ready-Made Scripts
Run standalone analysis scripts without writing code:
# Analyze symbol at position python scripts/basic_analysis.py src/main.py 10 5 # Find all symbols in file python scripts/find_all_symbols.py file src/main.py # Search workspace symbols python scripts/find_all_symbols.py workspace MyClass # Safe rename with preview python scripts/safe_rename.py src/main.py 10 5 new_name
Common Workflows
Exploring Unfamiliar Codebase
Task: Understand how a function is used across the project
Approach:
- •Find definition:
client.request_definition_locations() - •Find all references:
client.request_references() - •Get type info:
client.request_hover()
Script: python scripts/basic_analysis.py <file> <line> <char>
Safe Refactoring
Task: Rename a symbol across the entire workspace
Approach:
- •Preview changes:
client.request_rename_edits() - •Review affected files
- •Apply:
client.apply_workspace_edit()
Script: python scripts/safe_rename.py <file> <line> <char> <new_name>
Symbol Search
Task: Find all classes/functions matching a pattern
Approach:
- •Workspace-wide:
client.request_workspace_symbol_list(query="MyClass") - •File-specific:
client.request_document_symbol_list(file_path=...)
Script:
- •
python scripts/find_all_symbols.py workspace <query> - •
python scripts/find_all_symbols.py file <path>
Available Clients
See references/language_servers.md for full list.
Quick reference:
- •Python:
PyrightClient,PyreflyClient,BasedpyrightClient,TyClient - •Rust:
RustAnalyzerClient - •TypeScript/JS:
TypescriptClient,DenoClient - •Go:
GoplsClient - •Java:
JdtlsClient
All clients support both local (subprocess) and container (Docker) modes.
Capability System
The library uses capability mixins to compose exactly the features needed.
Selecting Capabilities
See references/capabilities.md for complete list.
Common combinations:
Basic navigation:
- •
WithRequestDefinition - •
WithRequestReferences - •
WithRequestHover
IDE-like features:
- •Add
WithRequestCompletion - •Add
WithRequestSignatureHelp - •Add
WithRequestCodeAction
Symbol search:
- •
WithRequestDocumentSymbol(file-level) - •
WithRequestWorkspaceSymbol(project-wide)
Refactoring:
- •
WithRequestRename - •
WithRequestCodeAction - •
WithRequestWorkspaceEdit
Why Capability Mixins?
- •Type safety: Only methods for registered capabilities exist
- •Automatic negotiation: Client tells server what it supports
- •Zero boilerplate: No manual capability checking
- •Composability: Mix exactly what you need
Local vs Container Servers
Every client supports dual server backends:
Local Server (Subprocess)
from lsp_client.clients.pyright import PyrightClient
async with PyrightClient() as client:
# Uses local pyright-langserver if available
...
Pros: Fast startup, direct file access
Cons: Requires server installed locally
Container Server (Docker)
from lsp_client.clients.pyright import PyrightClient, PyrightContainerServer
async with PyrightClient(server=PyrightContainerServer()) as client:
# Uses ghcr.io/lsp-client/pyright:latest
...
Pros: Zero installation, consistent environment
Cons: Docker overhead, path translation
Automatic Fallback
If no server specified, library tries:
- •Explicit
server=argument (if provided) - •Local installation
- •Container fallback
- •Auto-install hook (if defined)
Key Concepts
Position
LSP uses 0-indexed line and character positions:
from lsp_client import Position # Line 10, character 5 (0-indexed) pos = Position(line=10, character=5)
URI vs File Path
LSP servers work with URIs. The client handles conversion:
# Server returns URI location = await client.request_definition_locations(...) # Convert back to file path file_path = client.from_uri(location.uri)
Async Context Manager
Always use async with to ensure proper cleanup:
async with Client(...) as client:
# Client initialized, server running
result = await client.request_hover(...)
# Server automatically shut down
Advanced Features
Configuration Management
Clients come with sensible defaults. Override as needed:
@define
class MyClient(PyrightClient):
def create_default_config(self):
return {
"python": {
"analysis": {
"typeCheckingMode": "strict",
}
}
}
Server Lifecycle Hooks
Customize server behavior with hooks:
from lsp_client.server.local import LocalServer
@define
class CustomServer(LocalServer):
async def setup(self, workspace):
# Before server starts
...
async def on_started(self, workspace, sender):
# After server ready
...
async def on_shutdown(self):
# Before cleanup
...
See library's examples/custom_hooks.py for details.
Troubleshooting
Server Not Found
Symptom: "Could not find language server"
Solution: Either install locally or use container:
from lsp_client.clients.pyright import PyrightContainerServer client = PyrightClient(server=PyrightContainerServer())
Path Translation Issues
When using containers, always work with workspace-relative paths:
# Good await client.request_hover(file_path="src/main.py", ...) # Bad (absolute paths won't translate correctly) await client.request_hover(file_path="/Users/me/project/src/main.py", ...)
Resources
Scripts
- •
basic_analysis.py- Hover, definition, references for a symbol - •
find_all_symbols.py- Document/workspace symbol search - •
safe_rename.py- Preview and apply rename refactoring - •
custom_client_template.py- Template for creating custom clients
References
- •
capabilities.md- Complete list of available LSP capabilities - •
language_servers.md- Supported language servers and selection guide