Python Language Server Protocol implementation providing code intelligence features for Python development
—
Document and workspace management with Jedi integration, configuration handling, and LSP workspace features. Provides comprehensive document lifecycle management, notebook support, and workspace-level operations like diagnostics publishing and progress reporting.
Main workspace management class handling documents, configuration, and LSP workspace operations.
class Workspace:
# LSP method constants
M_PUBLISH_DIAGNOSTICS = "textDocument/publishDiagnostics"
M_PROGRESS = "$/progress"
M_INITIALIZE_PROGRESS = "window/workDoneProgress/create"
M_APPLY_EDIT = "workspace/applyEdit"
M_SHOW_MESSAGE = "window/showMessage"
M_LOG_MESSAGE = "window/logMessage"
def __init__(self, root_uri, endpoint, config=None):
"""
Initialize workspace.
Parameters:
- root_uri: str, workspace root URI
- endpoint: LSP endpoint for communication
- config: Config instance
"""
@property
def documents(self):
"""
Get managed documents.
Returns:
dict: URI to Document mapping
"""
@property
def root_path(self):
"""
Get workspace root filesystem path.
Returns:
str: Root path
"""
@property
def root_uri(self):
"""
Get workspace root URI.
Returns:
str: Root URI
"""
def is_local(self):
"""
Check if workspace is on local filesystem.
Returns:
bool: True if local filesystem
"""
def get_document(self, doc_uri):
"""
Get or create document.
Parameters:
- doc_uri: str, document URI
Returns:
Document: Document instance
"""
def get_cell_document(self, doc_uri):
"""
Get cell document for notebooks.
Parameters:
- doc_uri: str, cell document URI
Returns:
Cell: Cell document instance
"""
def get_maybe_document(self, doc_uri):
"""
Get document if it exists.
Parameters:
- doc_uri: str, document URI
Returns:
Document or None: Document if exists
"""
def put_document(self, doc_uri, source, version=None):
"""
Store document.
Parameters:
- doc_uri: str, document URI
- source: str, document source code
- version: int, document version
Returns:
Document: Document instance
"""
def put_notebook_document(self, doc_uri, notebook_type, cells, version=None, metadata=None):
"""
Store notebook document.
Parameters:
- doc_uri: str, notebook URI
- notebook_type: str, notebook type
- cells: list, notebook cells
- version: int, notebook version
- metadata: dict, notebook metadata
Returns:
Notebook: Notebook instance
"""
def add_notebook_cells(self, doc_uri, cells, start):
"""
Add cells to notebook.
Parameters:
- doc_uri: str, notebook URI
- cells: list, cells to add
- start: int, insertion index
"""
def remove_notebook_cells(self, doc_uri, start, delete_count):
"""
Remove cells from notebook.
Parameters:
- doc_uri: str, notebook URI
- start: int, start index
- delete_count: int, number of cells to remove
"""
def update_notebook_metadata(self, doc_uri, metadata):
"""
Update notebook metadata.
Parameters:
- doc_uri: str, notebook URI
- metadata: dict, new metadata
"""
def put_cell_document(self, doc_uri, notebook_uri, language_id, source, version=None):
"""
Store cell document.
Parameters:
- doc_uri: str, cell URI
- notebook_uri: str, parent notebook URI
- language_id: str, cell language
- source: str, cell source code
- version: int, cell version
Returns:
Cell: Cell instance
"""
def rm_document(self, doc_uri):
"""
Remove document.
Parameters:
- doc_uri: str, document URI
"""
def update_document(self, doc_uri, change, version=None):
"""
Update document with incremental changes.
Parameters:
- doc_uri: str, document URI
- change: dict, LSP text document content change
- version: int, new document version
Returns:
Document: Updated document
"""
def temp_document(self, source, path=None):
"""
Create temporary document context manager.
Parameters:
- source: str, document source
- path: str, optional file path
Returns:
ContextManager[Document]: Temporary document
"""
def update_config(self, settings):
"""
Update workspace configuration.
Parameters:
- settings: dict, new settings
"""
def apply_edit(self, edit):
"""
Apply workspace edit.
Parameters:
- edit: dict, LSP WorkspaceEdit
Returns:
dict: Applied edit response
"""
def publish_diagnostics(self, doc_uri, diagnostics, doc_version=None):
"""
Publish diagnostics for document.
Parameters:
- doc_uri: str, document URI
- diagnostics: list, diagnostic dicts
- doc_version: int, document version
"""
def report_progress(self, title, message=None, percentage=None, skip_token_initialization=False):
"""
Report progress to client.
Parameters:
- title: str, progress title
- message: str, progress message
- percentage: int, completion percentage
- skip_token_initialization: bool, skip token creation
"""
def log_message(self, message, msg_type=1):
"""
Send log message to client.
Parameters:
- message: str, log message
- msg_type: int, message type (1=Error, 2=Warning, 3=Info, 4=Log)
"""
def show_message(self, message, msg_type=3):
"""
Show message to user.
Parameters:
- message: str, message text
- msg_type: int, message type (1=Error, 2=Warning, 3=Info, 4=Log)
"""
def source_roots(self, document_path):
"""
Get source roots for document.
Parameters:
- document_path: str, document path
Returns:
list: Source root paths
"""
def close(self):
"""Clean up workspace resources."""Represents a text document with LSP functionality and Jedi integration.
class Document:
def __init__(self, uri, workspace, source=None, version=None):
"""
Initialize document.
Parameters:
- uri: str, document URI
- workspace: Workspace instance
- source: str, document source code
- version: int, document version
"""
@property
def uri(self):
"""
Get document URI.
Returns:
str: Document URI
"""
@property
def version(self):
"""
Get document version.
Returns:
int: Document version
"""
@property
def path(self):
"""
Get document filesystem path.
Returns:
str: Filesystem path
"""
@property
def dot_path(self):
"""
Get Python dot-notation path.
Returns:
str: Python module path
"""
@property
def filename(self):
"""
Get document filename.
Returns:
str: Base filename
"""
@property
def shared_data(self):
"""
Get shared plugin data storage.
Returns:
dict: Shared data dictionary
"""
@property
def source(self):
"""
Get document source code.
Returns:
str: Source code
"""
@property
def lines(self):
"""
Get document lines.
Returns:
list: Document lines
"""
def update_config(self, settings):
"""
Update document configuration.
Parameters:
- settings: dict, new settings
"""
def apply_change(self, change):
"""
Apply incremental change to document.
Parameters:
- change: dict, LSP TextDocumentContentChangeEvent
"""
def offset_at_position(self, position):
"""
Get byte offset for LSP position.
Parameters:
- position: dict, LSP position with 'line' and 'character'
Returns:
int: Byte offset
"""
def word_at_position(self, position):
"""
Get word at LSP position.
Parameters:
- position: dict, LSP position with 'line' and 'character'
Returns:
str: Word at position
"""
def jedi_script(self, position=None, use_document_path=False):
"""
Get Jedi Script for code intelligence.
Parameters:
- position: dict, LSP position
- use_document_path: bool, use document path for Jedi
Returns:
jedi.Script: Jedi script instance
"""
def jedi_names(self, all_scopes=False, definitions=True, references=False):
"""
Get Jedi names from document.
Parameters:
- all_scopes: bool, include all scopes
- definitions: bool, include definitions
- references: bool, include references
Returns:
list: Jedi Name objects
"""
def sys_path(self, environment=None):
"""
Get sys.path for document.
Parameters:
- environment: jedi.Environment, Jedi environment
Returns:
list: sys.path entries
"""Represents a notebook document with multiple cells.
class Notebook:
def __init__(self, uri, workspace, notebook_type, cells=None, version=None, metadata=None):
"""
Initialize notebook.
Parameters:
- uri: str, notebook URI
- workspace: Workspace instance
- notebook_type: str, notebook type
- cells: list, notebook cells
- version: int, notebook version
- metadata: dict, notebook metadata
"""
@property
def uri(self):
"""Get notebook URI."""
@property
def notebook_type(self):
"""Get notebook type."""
@property
def workspace(self):
"""Get parent workspace."""
@property
def version(self):
"""Get notebook version."""
@property
def cells(self):
"""Get notebook cells."""
@property
def metadata(self):
"""Get notebook metadata."""
def add_cells(self, new_cells, start):
"""
Add cells at position.
Parameters:
- new_cells: list, cells to add
- start: int, insertion position
"""
def remove_cells(self, start, delete_count):
"""
Remove cells.
Parameters:
- start: int, start position
- delete_count: int, number of cells to remove
"""
def cell_data(self):
"""
Get cell data mapping.
Returns:
dict: Cell URI to data mapping
"""
def jedi_names(self, up_to_cell_uri=None, all_scopes=False, definitions=True, references=False):
"""
Get Jedi names up to specific cell.
Parameters:
- up_to_cell_uri: str, stop at this cell URI
- all_scopes: bool, include all scopes
- definitions: bool, include definitions
- references: bool, include references
Returns:
list: Jedi Name objects
"""Represents a notebook cell as a document.
class Cell(Document):
def __init__(self, uri, notebook_uri, workspace, language_id, source=None, version=None):
"""
Initialize cell.
Parameters:
- uri: str, cell URI
- notebook_uri: str, parent notebook URI
- workspace: Workspace instance
- language_id: str, cell language
- source: str, cell source code
- version: int, cell version
"""
@property
def language_id(self):
"""
Get cell language.
Returns:
str: Language identifier
"""
@property
def notebook_uri(self):
"""
Get parent notebook URI.
Returns:
str: Notebook URI
"""
@property
def line_count(self):
"""
Get number of lines in cell.
Returns:
int: Line count
"""from pylsp.workspace import Workspace
# Create workspace
workspace = Workspace("file:///path/to/project", endpoint, config)
# Add document
doc = workspace.put_document("file:///path/to/file.py", "print('hello')", version=1)
# Update document
workspace.update_document("file:///path/to/file.py", {
"range": {
"start": {"line": 0, "character": 0},
"end": {"line": 0, "character": 5}
},
"text": "print"
}, version=2)
# Get document
doc = workspace.get_document("file:///path/to/file.py")
print(doc.source) # Updated source
# Remove document
workspace.rm_document("file:///path/to/file.py")from pylsp.workspace import Document
# Get Jedi script for completions
script = document.jedi_script({"line": 0, "character": 5})
completions = script.complete()
# Get definitions
definitions = script.goto(follow_imports=True)
# Get word at position
word = document.word_at_position({"line": 0, "character": 5})
# Get byte offset
offset = document.offset_at_position({"line": 1, "character": 10})# Create notebook
notebook = workspace.put_notebook_document(
"file:///notebook.ipynb",
"jupyter-notebook",
[
{"kind": 2, "language": "python", "source": "print('hello')"},
{"kind": 1, "language": "markdown", "source": "# Title"}
],
version=1
)
# Add cells
notebook.add_cells([
{"kind": 2, "language": "python", "source": "x = 42"}
], start=1)
# Get cell document
cell_doc = workspace.get_cell_document("file:///notebook.ipynb#cell-1")# Publish diagnostics
workspace.publish_diagnostics("file:///file.py", [
{
"range": {
"start": {"line": 0, "character": 0},
"end": {"line": 0, "character": 5}
},
"message": "Syntax error",
"severity": 1 # Error
}
])
# Report progress
workspace.report_progress("Processing", "Analyzing file.py", 50)
# Show message
workspace.show_message("Analysis complete", msg_type=3) # Info# Use temporary document for analysis
with workspace.temp_document("def func(): pass", "/tmp/temp.py") as temp_doc:
script = temp_doc.jedi_script()
completions = script.complete()
print(f"Found {len(completions)} completions")
# Document automatically cleaned upInstall with Tessl CLI
npx tessl i tessl/pypi-python-lsp-server