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.17.tar.gz (66.9 kB view details)

Uploaded Source

Built Distribution

lsp_tree_sitter-0.0.17-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file lsp_tree_sitter-0.0.17.tar.gz.

File metadata

  • Download URL: lsp_tree_sitter-0.0.17.tar.gz
  • Upload date:
  • Size: 66.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.8

File hashes

Hashes for lsp_tree_sitter-0.0.17.tar.gz
Algorithm Hash digest
SHA256 ba35219ee38a5c2e2d3e157c7e5dfd34a6edc351f47e5ee70bda9955b257c7fe
MD5 35d59033238a718a60ebbaacfb70e516
BLAKE2b-256 e8f77cee57b01c26878242c8cf1903e44b9a03ddf7e9e1ef61e09b33d300acb5

See more details on using hashes here.

File details

Details for the file lsp_tree_sitter-0.0.17-py3-none-any.whl.

File metadata

File hashes

Hashes for lsp_tree_sitter-0.0.17-py3-none-any.whl
Algorithm Hash digest
SHA256 958595c95accde7559fd40e48f0f123f2a6e004c83721305ef6b0eac318f161f
MD5 072715284056fce2e72986648b6a3ec0
BLAKE2b-256 b1a810ceff0f77f978f414871527303ddf1725349f834154cfe418751e7577ce

See more details on using hashes here.

Supported by

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