Home

Awesome

cookiecutter-pylsp-plugin

Using cookiecutter-pylsp-plugin is the easiest way to start writing pylsp plugin. This repo also aims to document how python-lsp-plugins plugins are wired if you want to do-it-yourself instead of using the template.

Features

Quickstart

Install the latest Cookiecutter if you haven't installed it yet:

pip install -U cookiecutter

Generate a pylsp plugin project:

cookiecutter https://github.com/python-lsp/cookiecutter-pylsp-plugin

Follow the prompts to configure your plugin project.

Install your plugin to the same environment as pylsp so that it can discover your plugin.

cd pylsp-myplugin
pip install --editable '.[dev]'

Don't forget to commit the generated package to your version control, for example with Git:

git init
git add .
git commit -m "Initial commit"

Writing hooks

The most important file in the generated package is plugin.py, this is where you will write pylsp hooks. The pylsp hooks corresponds to Language Server Protocol messages, you should read through the LSP specification. for the hook that you want to use.

A pylsp hook is a function with a specific name, decorated by @hookimpl:

@hookimpl
def pylsp_definitions(config, workspace, document, position):
    ...

Refer to list of hookspecs for the full list of all supported hooks.

Don't confuse @hookimpl with the @hookspec decorator, which is used to define hooks. You'll almost never need to use @hookspec when writing a plugin.

You should also write some tests for your hooks. If you choose to use pytest when generating the template, you should have everything you needed to start writing basic test in the test/test_plugin.py file. The test/fixtures.py contains some useful functions that you'll likely need to test your hooks. Refer to pytest documentation if you are unfamiliar with how to write tests in pytest.

pylsp plugin developer documentation

The following section documents how pylsp plugin is wired to pylsp. You do not need to understand this section if you are creating your plugin from this cookiecutter template, but it may help you if you needed to make changes to the entry_points setup when using this template, or if you want to replicate what this template does in your own project that does not use this template.

On startup, pylsp will automatically discover plugins by querying pkg_resources for entrypoints. So that your plugin can be discovered by pylsp, you must configure the following in the setup.py/setup.cfg of your plugin's package:

[options.entry_points]
pylsp = pylsp_myplugin = pylsp_myplugin.plugin

The entrypoint name can be anything but it's recommended to use the same name as your plugin package's import name.

Next, you need to install your plugin package. During development, usually you'd want to install with something like:

pip install --editable 'path/to/package/'

where path/to/package/ is the folder where your plugin's setup.py/setup.cfg is found.

This installs your plugin as an editable package, which is suitable during development.

pylsp uses pluggy for plugin management. It would be helpful to be familiar and read through pluggy documentation, but we'll go through a quick rundown here.

pylsp plugin is composed of a set of callbacks that are registered using @hookimpl decorator:

from pylsp import hookimpl, uris, _utils

@hookimpl
def pylsp_definitions(config, workspace, document, position):
    logger.info('Retrieving definitions: %s %s %s %s', config, workspace, document, position)
    filename = __file__
    uri = uris.uri_with(document.uri, path=filename)
    with open(filename) as f:
        lines = f.readlines()
        for lineno, line in enumerate(lines):
            if 'def pylsp_definitions' in line:
                break
    return [
        {
            'uri': uri,
            'range': {
                'start': {
                    'line': lineno,
                    'character': 4,
                },
                'end': {
                    'line': lineno,
                    'character': line.find(')') + 1,
                },
            }
        }
    ]

Refer to list of hookspecs for the full list of all supported hooks.

And that's all you need to make a pylsp plugin. Once you have your entrypoint configured and register some @hookimpl, pylsp will call your callbacks to perform its functions.

What comes next, is that you'll want to publish your package in a repository like Github and in PyPI so your package can be easily used and discovered by your users. You can use any way to publish your plugin package, but this cookiecutter will set you up to publish to PyPI using twine.