Skip to main content

a library to create language servers

Project description

lsp-tree-sitter

readthedocs pre-commit.ci status github/workflow codecov DeepSource

github/downloads github/downloads/latest github/issues github/issues-closed github/issues-pr github/issues-pr-closed github/discussions github/milestones github/forks github/stars github/watchers github/contributors github/commit-activity github/last-commit github/release-date

github/license github/languages github/languages/top github/directory-file-count github/code-size github/repo-size github/v

pypi/status pypi/v pypi/downloads pypi/format pypi/implementation pypi/pyversions

A core library to support language servers.

I write many language servers and they share some same code so I extract the shared code to this library.

I've had enough of writing many DSLs in my editor without any LSP support (completion, hover, ...). So I decide to sacrifice my time to do this work.

Language servers

Usage

Schema

A Trie to convert a file to a json, then you can use json schema to validate it to get diagnostics.

Take termux-language-server as an example.

PKGBUILD:

pkgname=hello
pkgver=0.0.1
pkgrel=1
pkgdesc="hello"
arch=(wrong_arch)
license=(GPL3)

build() {
    cat <<EOF > hello
#!/usr/bin/env sh
echo hello
EOF
}

package() {
    install -D hello -t $pkgdir/usr/bin
}
termux-language-server --convert PKGBUILD
{
  "pkgname": "hello",
  "pkgver": "0.0.1",
  "pkgrel": "1",
  "pkgdesc": "hello",
  "arch": [
    "wrong_arch"
  ],
  "license": [
    "GPL3"
  ],
  "build": 0,
  "package": 0
}

So, we can validate the json by a json schema:

$ termux-language-server --check PKGBUILD
PKGBUILD:5:7-5:17:error: 'wrong_arch' is not one of ['any', 'pentium4', 'i486', 'i686', 'x86_64', 'x86_64_v3', 'arm', 'armv6h', 'armv7h', 'armv8', 'aarch64']

PKGBUILD

Sometimes it will be more complicated:

neomuttrc:

set allow_ansi=yes sleep_time = no ispell = aspell
set query_command = 'mutt_ldap_query.pl %s'
mutt-language-server --convert neomuttrc
{
  "set": {
    "allow_ansi": "yes",
    "sleep_time": "no",
    "ispell": "aspell",
    "query_command": "mutt_ldap_query.pl %s"
  }
}
$ mutt-language-server --check neomuttrc
neomuttrc:1:33-1:35:error: 'no' is not of type 'number'

neomuttrc

We put the result to the json's .set not . just in order to reserve the other keys for other usages.

Finders

Some finders to find the required node in tree-sitter's AST. Such as, if you want to get the node under the cursor:

@self.feature(TEXT_DOCUMENT_COMPLETION)
def completions(params: CompletionParams) -> CompletionList:
    document = self.workspace.get_document(params.text_document.uri)
    uni = PositionFinder(params.position, right_equal=True).find(
        document.uri, self.trees[document.uri]
    )
    # ...

UNI (Universal Node Identifier) is URI + node.

Utilities

This library also provides many utility functions. Such as converting man page to markdown and tokenizing it in order to generate the json schema.

mutt-language-server --generate-schema neomuttrc
{
  "$id": "https://github.com/neomutt/mutt-language-server/blob/main/src/termux_language_server/assets/json/neomuttrc.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$comment": "Don't edit this file directly! It is generated by `mutt-language-server --generate-schema=neomuttrc`.",
  "type": "object",
  "properties": {
    "account-hook": {
      "description": "```neomuttrc\naccount-hook regex command\n```\nThis hook is executed whenever you access a remote mailbox. Useful to adjust configuration settings to different IMAP or POP servers."
    },
    "$comment": "..."
  }
}

hover

Template

This project provides a template for copier.

For example, you want to create a language server for a filetype named zathurarc. Please follow the following steps:

Create a tree-sitter parser

  1. Create a tree-sitter-parser from template.
  2. Publish it to PYPI

You can see if py-tree-sitter-languages supports the language where you want to create a language server.

Copy a template

$ copier copy -rHEAD gh:neomutt/lsp-tree-sitter /path/to/your/XXX-language-server
๐ŸŽค What is your language name?
zathurarc
๐ŸŽค What is your file patterns? split by " "
*.zathurarc zathurarc
๐ŸŽค What is your project name?
zathura-language-server
๐ŸŽค What is your Python module name?
zathura_language_server
๐ŸŽค What is your Python class name?
ZathuraLanguageServer
๐ŸŽค What is your tree-sitter parser name?
tree-sitter-zathurarc
๐ŸŽค What is your user name?
wzy
๐ŸŽค What is your email?
32936898+Freed-Wu@users.noreply.github.com

Copying from template version None
create  .
...
$ cd /path/to/your/XXX-language-server
$ tree .
๎—ฟ .
โ”œโ”€โ”€ ๎—ฟ docs  # documents
โ”‚  โ”œโ”€โ”€ ๎—ฟ api
โ”‚  โ”‚  โ””โ”€โ”€ ๏’Š zathura-language-server.md
โ”‚  โ”œโ”€โ”€ ๎˜† conf.py
โ”‚  โ”œโ”€โ”€ ๏’Š index.md
โ”‚  โ”œโ”€โ”€ ๎˜† requirements.txt
โ”‚  โ””โ”€โ”€ ๎—ฟ resources
โ”‚     โ”œโ”€โ”€ ๏’Š configure.md
โ”‚     โ”œโ”€โ”€ ๏’Š install.md
โ”‚     โ””โ”€โ”€ ๏’Š requirements.md
โ”œโ”€โ”€ ๏€ญ LICENSE
โ”œโ”€โ”€ ๎˜† pyproject.toml
โ”œโ”€โ”€ ๏’Š README.md
โ”œโ”€โ”€ ๎—ฟ requirements  # optional dependencies
โ”‚  โ”œโ”€โ”€ ๏…œ colorize.txt
โ”‚  โ”œโ”€โ”€ ๏…œ dev.txt
โ”‚  โ””โ”€โ”€ ๏…œ misc.txt
โ”œโ”€โ”€ ๎˜† requirements.txt
โ”œโ”€โ”€ ๎—ฟ src
โ”‚  โ””โ”€โ”€ ๎—ฟ zathura_language_server
โ”‚     โ”œโ”€โ”€ ๎˜† __init__.py
โ”‚     โ”œโ”€โ”€ ๎˜† __main__.py
โ”‚     โ”œโ”€โ”€ ๎˜† _shtab.py
โ”‚     โ”œโ”€โ”€ ๎—ฟ assets
โ”‚     โ”‚  โ”œโ”€โ”€ ๎—ฟ json  # json schemas generated by misc/XXX.py
โ”‚     โ”‚  โ”‚  โ””โ”€โ”€ ๎˜‹ zathurarc.json
โ”‚     โ”‚  โ””โ”€โ”€ ๎—ฟ queries  # tree-sitter queries
โ”‚     โ”‚     โ””โ”€โ”€ ๏…› import.scm
โ”‚     โ”œโ”€โ”€ ๎˜† finders.py  # project specific finders
โ”‚     โ”œโ”€โ”€ ๎—ฟ misc
โ”‚     โ”‚  โ”œโ”€โ”€ ๎˜† __init__.py
โ”‚     โ”‚  โ””โ”€โ”€ ๎˜† zathurarc.py
โ”‚     โ”œโ”€โ”€ ๏…› py.typed
โ”‚     โ”œโ”€โ”€ ๎˜† schema.py  # project specific schemas
โ”‚     โ”œโ”€โ”€ ๎˜† server.py  # main file for server
โ”‚     โ””โ”€โ”€ ๎˜† utils.py
โ”œโ”€โ”€ ๎—ฟ templates
โ”‚  โ”œโ”€โ”€ ๏…œ class.txt
โ”‚  โ”œโ”€โ”€ ๏…œ def.txt
โ”‚  โ”œโ”€โ”€ ๏…› metainfo.py.j2
โ”‚  โ””โ”€โ”€ ๏…œ noarg.txt
โ””โ”€โ”€ ๎—ฟ tests
โ””โ”€โ”€ ๎˜† test_utils.py
  1. Edit schema.py to convert a tree-sitter's tree to a json, which is the core function of XXX-langauge-server --convert
  2. Edit a misc/XXX.py to generate json schemas, which is the core function of XXX-languageserver --generate-schema
  3. Edit server.py to make sure the LSP features can work for specific tree-sitter parsers.
  4. Edit queries/XXX.scm to make sure the LSP features can work for specific tree-sitter parsers if you use them.
  5. Edit finders.py to add the language specific finders for XXX-languageserver --check and XXX-languageserver --format

Test if it can work

$ git init
$ pip install -e .
$ which zathura-language-server
~/.local/bin/zathura-language-server
  1. Refer docs/resources/configure.md to configure your language server for your editor.
  2. Refer README.md to see the LSP features provided by your language server.
vi /path/to/zathurarc

You can test the LSP features.

Refer https://docs.readthedocs.io to see how to publish the documents.

References

These following language servers can be a good example for beginners:

zathura-language-server

zathurarc's syntax only has 4 directives:

  • set option value
  • include /the/path
  • map key function
  • unmap key

Very few directives make creating tree-sitter-zathurarc and editing schema.py very easy. So I am highly recommended starting from it.

tmux-language-server

tmux.conf is more complex than zathurarc. It has not only set option = value and source /the/path, but also 170+ other directives.

mutt-language-server

muttrc or neomuttrc has the following directives:

  • set option = value
  • source /the/path
  • 80+ other directives

However, its set syntax is very flexible. The following syntaxes are legal:

  • set option2 = value1 option2 = value2 ...
  • set option: a shortcut for set option = yes
  • set nooption: a shortcut for set option = no
  • set invoption
  • set nooption1 invoption2 option3 ...
  • ...

So, in fact it is harder than tmux.conf, IMO.

termux-language-server

build.sh, PKGBUILD, *.ebuild use same syntax of bash. However, they use different json schemas. If the language where you want to create a language server, you can refer it to know how to handle this situation.

Other references

Some useful URLs for beginners who want to develop language servers:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lsp-tree-sitter-0.0.15.tar.gz (66.6 kB view hashes)

Uploaded Source

Built Distribution

lsp_tree_sitter-0.0.15-py3-none-any.whl (32.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page