CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-python-lsp-server

Python Language Server Protocol implementation providing code intelligence features for Python development

Pending
Overview
Eval results
Files

plugin-development.mddocs/

Plugin Development

Comprehensive plugin system using Pluggy hooks for extending Python LSP Server functionality. Provides 40+ hook specifications covering all LSP features, document lifecycle, server management, and custom extensions.

Capabilities

Plugin Markers and Decorators

Core decorators for plugin development.

hookimpl = pluggy.HookimplMarker("pylsp")  # Mark hook implementations
hookspec = pluggy.HookspecMarker("pylsp")  # Mark hook specifications

Document Lifecycle Hooks

Hooks called during document open, save, and update operations.

@hookimpl
def pylsp_document_did_open(config, workspace, document):
    """
    Called when a document is opened.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance  
    - document: Document instance
    """

@hookimpl
def pylsp_document_did_save(config, workspace, document):
    """
    Called when a document is saved.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    """

Language Feature Hooks

Hooks for implementing core LSP language features.

@hookimpl
def pylsp_completions(config, workspace, document, position, ignored_names):
    """
    Provide code completions.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    - ignored_names: list of names to ignore
    
    Returns:
    list: Completion items as dicts
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_completion_item_resolve(config, workspace, document, completion_item):
    """
    Resolve additional completion item information.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance  
    - completion_item: dict, completion item to resolve
    
    Returns:
    dict: Enhanced completion item
    """

@hookimpl
def pylsp_definitions(config, workspace, document, position):
    """
    Provide go-to-definition locations.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    
    Returns:
    list: Location dicts with 'uri' and 'range'
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_type_definition(config, document, position):
    """
    Provide go-to-type-definition locations.
    
    Parameters:
    - config: Config instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    
    Returns:
    list: Location dicts with 'uri' and 'range'
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_hover(config, workspace, document, position):
    """
    Provide hover information.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    
    Returns:
    dict: Hover response with 'contents' and optional 'range'
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_signature_help(config, workspace, document, position):
    """
    Provide signature help.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    
    Returns:
    dict: Signature help with 'signatures' list
    """

@hookimpl
def pylsp_document_symbols(config, workspace, document):
    """
    Provide document symbols.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    
    Returns:
    list: Document symbol dicts
    """

@hookimpl  
def pylsp_references(config, workspace, document, position, exclude_declaration):
    """
    Find references to symbol.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    - exclude_declaration: bool, exclude declaration from results
    
    Returns:
    list: Location dicts with 'uri' and 'range'
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_rename(config, workspace, document, position, new_name):
    """
    Rename symbol.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    - new_name: str, new symbol name
    
    Returns:
    dict: WorkspaceEdit with document changes
    """

@hookimpl
def pylsp_document_highlight(config, workspace, document, position):
    """
    Highlight occurrences of symbol.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - position: dict with 'line' and 'character' keys
    
    Returns:
    list: DocumentHighlight dicts with 'range' and 'kind'
    """

Code Actions and Formatting Hooks

Hooks for code actions, formatting, and refactoring.

@hookimpl
def pylsp_code_actions(config, workspace, document, range, context):
    """
    Provide code actions.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - range: dict with 'start' and 'end' positions
    - context: dict with diagnostics and action kinds
    
    Returns:
    list: CodeAction dicts
    """

@hookimpl
def pylsp_code_lens(config, workspace, document):
    """
    Provide code lenses.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    
    Returns:
    list: CodeLens dicts with 'range' and 'command'
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_format_document(config, workspace, document, options):
    """
    Format entire document.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - options: dict with formatting options
    
    Returns:
    list: TextEdit dicts
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_format_range(config, workspace, document, range, options):
    """
    Format document range.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - range: dict with 'start' and 'end' positions
    - options: dict with formatting options
    
    Returns:
    list: TextEdit dicts
    """

@hookimpl
def pylsp_folding_range(config, workspace, document):
    """
    Provide folding ranges.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    
    Returns:
    list: FoldingRange dicts
    """

Diagnostic and Linting Hooks

Hooks for providing diagnostics and linting information.

@hookimpl
def pylsp_lint(config, workspace, document, is_saved):
    """
    Provide linting diagnostics.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - document: Document instance
    - is_saved: bool, whether document was just saved
    
    Returns:
    list: Diagnostic dicts with 'range', 'message', 'severity'
    """

Server Lifecycle Hooks

Hooks called during server initialization, configuration changes, and shutdown.

@hookimpl
def pylsp_initialize(config, workspace):
    """
    Called during server initialization.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    """

@hookimpl
def pylsp_initialized():
    """Called after server initialization is complete."""

@hookimpl
def pylsp_shutdown(config, workspace):
    """
    Called during server shutdown.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    """

@hookimpl
def pylsp_workspace_configuration_changed(config, workspace):
    """
    Called when workspace configuration changes.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    """

Extension and Command Hooks

Hooks for custom commands and experimental features.

@hookimpl
def pylsp_commands(config, workspace):
    """
    Provide custom command names.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    
    Returns:
    list: Command name strings
    """

@hookimpl(hookwrapper=True)  # firstresult hook
def pylsp_execute_command(config, workspace, command, arguments):
    """
    Execute custom command.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    - command: str, command name
    - arguments: list, command arguments
    
    Returns:
    any: Command result
    """

@hookimpl
def pylsp_dispatchers(config, workspace):
    """
    Provide custom message dispatchers.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    
    Returns:
    dict: Method name to handler mappings
    """

@hookimpl
def pylsp_experimental_capabilities(config, workspace):
    """
    Provide experimental server capabilities.
    
    Parameters:
    - config: Config instance
    - workspace: Workspace instance
    
    Returns:
    dict: Experimental capabilities
    """

@hookimpl
def pylsp_settings(config):
    """
    Provide plugin settings schema.
    
    Parameters:
    - config: Config instance
    
    Returns:
    dict: Settings schema
    """

Built-in Plugins

Entry points for built-in plugins automatically loaded by the server.

# From pyproject.toml [project.entry-points.pylsp]
BUILTIN_PLUGINS = {
    "autopep8": "pylsp.plugins.autopep8_format",
    "folding": "pylsp.plugins.folding", 
    "flake8": "pylsp.plugins.flake8_lint",
    "jedi_completion": "pylsp.plugins.jedi_completion",
    "jedi_definition": "pylsp.plugins.definition",
    "jedi_type_definition": "pylsp.plugins.type_definition",
    "jedi_hover": "pylsp.plugins.hover",
    "jedi_highlight": "pylsp.plugins.highlight",
    "jedi_references": "pylsp.plugins.references",
    "jedi_rename": "pylsp.plugins.jedi_rename",
    "jedi_signature_help": "pylsp.plugins.signature",
    "jedi_symbols": "pylsp.plugins.symbols",
    "mccabe": "pylsp.plugins.mccabe_lint",
    "preload": "pylsp.plugins.preload_imports",
    "pycodestyle": "pylsp.plugins.pycodestyle_lint",
    "pydocstyle": "pylsp.plugins.pydocstyle_lint",
    "pyflakes": "pylsp.plugins.pyflakes_lint",
    "pylint": "pylsp.plugins.pylint_lint",
    "rope_completion": "pylsp.plugins.rope_completion",
    "rope_autoimport": "pylsp.plugins.rope_autoimport",
    "yapf": "pylsp.plugins.yapf_format"
}

Usage Examples

Simple Linting Plugin

from pylsp import hookimpl

@hookimpl
def pylsp_lint(config, workspace, document, is_saved):
    """Custom linter that flags TODO comments."""
    diagnostics = []
    lines = document.source.splitlines()
    
    for line_num, line in enumerate(lines):
        if 'TODO' in line:
            todo_pos = line.find('TODO')
            diagnostics.append({
                "range": {
                    "start": {"line": line_num, "character": todo_pos},
                    "end": {"line": line_num, "character": todo_pos + 4}
                },
                "message": "TODO comment found",
                "severity": 3,  # Information
                "source": "todo-linter"
            })
    
    return diagnostics

Custom Completion Plugin

from pylsp import hookimpl

@hookimpl  
def pylsp_completions(config, workspace, document, position, ignored_names):
    """Provide custom completions for common patterns."""
    completions = []
    
    # Add custom completions based on context
    word = document.word_at_position(position)
    if word.startswith('log'):
        completions.append({
            "label": "logging.getLogger(__name__)",
            "kind": 15,  # Snippet
            "detail": "Create logger instance",
            "insertText": "logging.getLogger(__name__)",
            "documentation": "Standard logger initialization pattern"
        })
    
    return completions

Configuration-Driven Plugin

from pylsp import hookimpl

@hookimpl
def pylsp_settings(config):
    """Define plugin settings schema."""
    return {
        "plugins": {
            "my_plugin": {
                "type": "object",
                "properties": {
                    "enabled": {"type": "boolean", "default": True},
                    "severity": {"type": "string", "default": "warning"}
                }
            }
        }
    }

@hookimpl
def pylsp_lint(config, workspace, document, is_saved):
    """Use plugin configuration."""
    settings = config.plugin_settings("my_plugin", document.path)
    if not settings.get("enabled", True):
        return []
    
    severity_map = {"error": 1, "warning": 2, "info": 3, "hint": 4}
    severity = severity_map.get(settings.get("severity", "warning"), 2)
    
    # Perform linting with configured severity
    return [{
        "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 1}},
        "message": "Custom diagnostic",
        "severity": severity,
        "source": "my_plugin"
    }]

Install with Tessl CLI

npx tessl i tessl/pypi-python-lsp-server

docs

configuration-system.md

index.md

plugin-development.md

server-management.md

utilities-helpers.md

workspace-management.md

tile.json