lsp-tree-sitter

November 4, 2025 ยท View on GitHub

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_text_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

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: