{"id":13439915,"url":"https://github.com/neomutt/lsp-tree-sitter","last_synced_at":"2025-04-05T10:07:22.341Z","repository":{"id":178462400,"uuid":"655771560","full_name":"neomutt/lsp-tree-sitter","owner":"neomutt","description":"A library to create language servers.","archived":false,"fork":false,"pushed_at":"2024-12-31T07:47:02.000Z","size":213,"stargazers_count":75,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-29T09:08:25.969Z","etag":null,"topics":["json-schema","language-server-protocol","tree-sitter"],"latest_commit_sha":null,"homepage":"https://lsp-tree-sitter.readthedocs.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/neomutt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"flatcap","patreon":"neomutt","liberapay":"flatcap","custom":"paypal.me/russon"}},"created_at":"2023-06-19T15:04:15.000Z","updated_at":"2025-03-12T06:14:49.000Z","dependencies_parsed_at":"2023-09-26T05:16:54.161Z","dependency_job_id":"82aff04e-810c-40dd-9915-45fc0424d624","html_url":"https://github.com/neomutt/lsp-tree-sitter","commit_stats":null,"previous_names":["freed-wu/pkgbuild-language-server","freed-wu/tree-sitter-lsp","neomutt/tree-sitter-lsp","neomutt/lsp-tree-sitter"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neomutt%2Flsp-tree-sitter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neomutt%2Flsp-tree-sitter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neomutt%2Flsp-tree-sitter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neomutt%2Flsp-tree-sitter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neomutt","download_url":"https://codeload.github.com/neomutt/lsp-tree-sitter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318744,"owners_count":20919484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json-schema","language-server-protocol","tree-sitter"],"created_at":"2024-07-31T03:01:18.148Z","updated_at":"2025-04-05T10:07:22.319Z","avatar_url":"https://github.com/neomutt.png","language":"Python","funding_links":["https://github.com/sponsors/flatcap","https://patreon.com/neomutt","https://liberapay.com/flatcap","paypal.me/russon"],"categories":["HarmonyOS","Python"],"sub_categories":["Windows Manager"],"readme":"# lsp-tree-sitter\n\n[![readthedocs](https://shields.io/readthedocs/lsp-tree-sitter)](https://lsp-tree-sitter.readthedocs.io)\n[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/neomutt/lsp-tree-sitter/main.svg)](https://results.pre-commit.ci/latest/github/neomutt/lsp-tree-sitter/main)\n[![github/workflow](https://github.com/neomutt/lsp-tree-sitter/actions/workflows/main.yml/badge.svg)](https://github.com/neomutt/lsp-tree-sitter/actions)\n[![codecov](https://codecov.io/gh/neomutt/lsp-tree-sitter/branch/main/graph/badge.svg)](https://codecov.io/gh/neomutt/lsp-tree-sitter)\n[![DeepSource](https://deepsource.io/gh/neomutt/lsp-tree-sitter.svg/?show_trend=true)](https://deepsource.io/gh/neomutt/lsp-tree-sitter)\n\n[![github/downloads](https://shields.io/github/downloads/neomutt/lsp-tree-sitter/total)](https://github.com/neomutt/lsp-tree-sitter/releases)\n[![github/downloads/latest](https://shields.io/github/downloads/neomutt/lsp-tree-sitter/latest/total)](https://github.com/neomutt/lsp-tree-sitter/releases/latest)\n[![github/issues](https://shields.io/github/issues/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/issues)\n[![github/issues-closed](https://shields.io/github/issues-closed/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/issues?q=is%3Aissue+is%3Aclosed)\n[![github/issues-pr](https://shields.io/github/issues-pr/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/pulls)\n[![github/issues-pr-closed](https://shields.io/github/issues-pr-closed/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/pulls?q=is%3Apr+is%3Aclosed)\n[![github/discussions](https://shields.io/github/discussions/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/discussions)\n[![github/milestones](https://shields.io/github/milestones/all/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/milestones)\n[![github/forks](https://shields.io/github/forks/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/network/members)\n[![github/stars](https://shields.io/github/stars/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/stargazers)\n[![github/watchers](https://shields.io/github/watchers/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/watchers)\n[![github/contributors](https://shields.io/github/contributors/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/graphs/contributors)\n[![github/commit-activity](https://shields.io/github/commit-activity/w/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/graphs/commit-activity)\n[![github/last-commit](https://shields.io/github/last-commit/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/commits)\n[![github/release-date](https://shields.io/github/release-date/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/releases/latest)\n\n[![github/license](https://shields.io/github/license/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter/blob/main/LICENSE)\n[![github/languages](https://shields.io/github/languages/count/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n[![github/languages/top](https://shields.io/github/languages/top/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n[![github/directory-file-count](https://shields.io/github/directory-file-count/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n[![github/code-size](https://shields.io/github/languages/code-size/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n[![github/repo-size](https://shields.io/github/repo-size/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n[![github/v](https://shields.io/github/v/release/neomutt/lsp-tree-sitter)](https://github.com/neomutt/lsp-tree-sitter)\n\n[![pypi/status](https://shields.io/pypi/status/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#description)\n[![pypi/v](https://shields.io/pypi/v/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#history)\n[![pypi/downloads](https://shields.io/pypi/dd/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#files)\n[![pypi/format](https://shields.io/pypi/format/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#files)\n[![pypi/implementation](https://shields.io/pypi/implementation/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#files)\n[![pypi/pyversions](https://shields.io/pypi/pyversions/lsp-tree-sitter)](https://pypi.org/project/lsp-tree-sitter/#files)\n\nA core library to support language servers.\n\nI write many language servers and they share some same code so I extract the\nshared code to this library.\n\nI've had enough of writing many DSLs in my editor without any LSP support\n(completion, hover, ...). So I decide to sacrifice my time to do this work.\n\n## Language servers\n\n- [termux-language-server](https://github.com/termux/termux-language-server/):\n  for some specific bash scripts:\n  - [`build.sh`](https://github.com/termux/termux-packages/wiki/Creating-new-package)\n  - [`PKGBUILD`](https://wiki.archlinux.org/title/PKGBUILD)\n  - [`*.ebuild`](https://dev.gentoo.org/~zmedico/portage/doc/man/ebuild.5.html)\n  - ...\n- [mutt-language-server](https://github.com/neomutt/mutt-language-server):\n  for [(neo)mutt](https://github.com/neomutt/neomutt)'s (neo)muttrc\n- [More](https://github.com/Freed-Wu?tab=repositories\u0026q=lsp-server)\n\n## Usage\n\n### Schema\n\nA `Trie` to convert a file to a json, then you can use json schema to validate\nit to get diagnostics.\n\nTake\n[termux-language-server](https://github.com/termux/termux-language-server/) as\nan example.\n\n`PKGBUILD`:\n\n```sh\npkgname=hello\npkgver=0.0.1\npkgrel=1\npkgdesc=\"hello\"\narch=(wrong_arch)\nlicense=(GPL3)\n\nbuild() {\n    cat \u003c\u003cEOF \u003e hello\n#!/usr/bin/env sh\necho hello\nEOF\n}\n\npackage() {\n    install -D hello -t $pkgdir/usr/bin\n}\n```\n\n```sh\ntermux-language-server --convert PKGBUILD\n```\n\n```json\n{\n  \"pkgname\": \"hello\",\n  \"pkgver\": \"0.0.1\",\n  \"pkgrel\": \"1\",\n  \"pkgdesc\": \"hello\",\n  \"arch\": [\n    \"wrong_arch\"\n  ],\n  \"license\": [\n    \"GPL3\"\n  ],\n  \"build\": 0,\n  \"package\": 0\n}\n```\n\nSo, we can validate the json by [a json schema](https://github.com/termux/termux-language-server/tree/main/src/termux_language_server/assets/json):\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\n```sh\n$ termux-language-server --check PKGBUILD\nPKGBUILD: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']\n```\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n![PKGBUILD](https://github.com/neomutt/lsp-tree-sitter/assets/32936898/58614996-bd8a-4e27-b573-87346c82ea2a)\n\nSometimes it will be more complicated:\n\n`neomuttrc`:\n\n```neomuttrc\nset allow_ansi=yes sleep_time = no ispell = aspell\nset query_command = 'mutt_ldap_query.pl %s'\n```\n\n```sh\nmutt-language-server --convert neomuttrc\n```\n\n```json\n{\n  \"set\": {\n    \"allow_ansi\": \"yes\",\n    \"sleep_time\": \"no\",\n    \"ispell\": \"aspell\",\n    \"query_command\": \"mutt_ldap_query.pl %s\"\n  }\n}\n```\n\n```sh\n$ mutt-language-server --check neomuttrc\nneomuttrc:1:33-1:35:error: 'no' is not of type 'number'\n```\n\n![neomuttrc](https://github.com/neomutt/lsp-tree-sitter/assets/32936898/75ebf0c1-784a-43db-ae11-59783af57b4f)\n\nWe put the result to the json's `.set` not `.` just in order to reserve the\nother keys for other usages.\n\n### Finders\n\nSome finders to find the required node in tree-sitter's AST.\nSuch as, if you want to get the node under the cursor:\n\n```python\n@self.feature(TEXT_DOCUMENT_COMPLETION)\ndef completions(params: CompletionParams) -\u003e CompletionList:\n    document = self.workspace.get_document(params.text_document.uri)\n    uni = PositionFinder(params.position, right_equal=True).find(\n        document.uri, self.trees[document.uri]\n    )\n    # ...\n```\n\nUNI (Universal Node Identifier) is URI + node.\n\n### Utilities\n\nThis library also provides many utility functions. Such as converting man page to\nmarkdown and tokenizing it in order to generate the json schema.\n\n```sh\nmutt-language-server --generate-schema neomuttrc\n```\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\n````json\n{\n  \"$id\": \"https://github.com/neomutt/mutt-language-server/blob/main/src/termux_language_server/assets/json/neomuttrc.json\",\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"$comment\": \"Don't edit this file directly! It is generated by `mutt-language-server --generate-schema=neomuttrc`.\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"account-hook\": {\n      \"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.\"\n    },\n    \"$comment\": \"...\"\n  }\n}\n````\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n![hover](https://github.com/neomutt/lsp-tree-sitter/assets/32936898/22a0347e-3d4f-45c5-833b-e89225ce3b74)\n\n## Template\n\nThis project provides a template for\n[copier](https://github.com/copier-org/copier).\n\nFor example, you want to create a language server for a filetype named\n[`zathurarc`](https://pwmt.org/projects/zathura/documentation/). Please follow\nthe following steps:\n\n### Create a tree-sitter parser\n\n1. Create a tree-sitter-parser from [template](https://github.com/tree-sitter-grammars/template).\n2. Publish it to PYPI\n\nYou can see if\n[py-tree-sitter-languages](https://github.com/grantjenks/py-tree-sitter-languages)\nsupports the language where you want to create a language server.\n\n### Copy a template\n\n```sh\n$ copier copy -rHEAD gh:neomutt/lsp-tree-sitter /path/to/your/XXX-language-server\n🎤 What is your language name?\nzathurarc\n🎤 What is your file patterns? split by \" \"\n*.zathurarc zathurarc\n🎤 What is your project name?\nzathura-language-server\n🎤 What is your Python module name?\nzathura_language_server\n🎤 What is your Python class name?\nZathuraLanguageServer\n🎤 What is your tree-sitter parser name?\ntree-sitter-zathurarc\n🎤 What is your user name?\nwzy\n🎤 What is your email?\n32936898+Freed-Wu@users.noreply.github.com\n\nCopying from template version None\ncreate  .\n...\n$ cd /path/to/your/XXX-language-server\n$ tree .\n .\n├──  docs  # documents\n│  ├──  api\n│  │  └──  zathura-language-server.md\n│  ├──  conf.py\n│  ├──  index.md\n│  ├──  requirements.txt\n│  └──  resources\n│     ├──  configure.md\n│     ├──  install.md\n│     └──  requirements.md\n├──  LICENSE\n├──  pyproject.toml\n├──  README.md\n├──  requirements  # optional dependencies\n│  ├──  colorize.txt\n│  ├──  dev.txt\n│  └──  misc.txt\n├──  requirements.txt\n├──  src\n│  └──  zathura_language_server\n│     ├──  __init__.py\n│     ├──  __main__.py\n│     ├──  _shtab.py\n│     ├──  assets\n│     │  ├──  json  # json schemas generated by misc/XXX.py\n│     │  │  └──  zathurarc.json\n│     │  └──  queries  # tree-sitter queries\n│     │     └──  import.scm\n│     ├──  finders.py  # project specific finders\n│     ├──  misc\n│     │  ├──  __init__.py\n│     │  └──  zathurarc.py\n│     ├──  py.typed\n│     ├──  schema.py  # project specific schemas\n│     ├──  server.py  # main file for server\n│     └──  utils.py\n├──  templates\n│  ├──  class.txt\n│  ├──  def.txt\n│  ├──  metainfo.py.j2\n│  └──  noarg.txt\n└──  tests\n└──  test_utils.py\n```\n\n1. Edit `schema.py` to convert a tree-sitter's tree to a json, which is the\n   core function of `XXX-langauge-server --convert`\n2. Edit a `misc/XXX.py` to generate json schemas, which is the core function of\n   `XXX-languageserver --generate-schema`\n3. Edit `server.py` to make sure the LSP features can work for specific\n   tree-sitter parsers.\n4. Edit `queries/XXX.scm` to make sure the LSP features can work for specific\n   tree-sitter parsers if you use them.\n5. Edit `finders.py` to add the language specific finders for\n   `XXX-languageserver --check` and `XXX-languageserver --format`\n\n### Test if it can work\n\n```sh\n$ git init\n$ pip install -e .\n$ which zathura-language-server\n~/.local/bin/zathura-language-server\n```\n\n1. Refer `docs/resources/configure.md` to configure your language server for\n   your editor.\n2. Refer `README.md` to see the LSP features provided by your language server.\n\n```sh\nvi /path/to/zathurarc\n```\n\nYou can test the LSP features.\n\nRefer \u003chttps://docs.readthedocs.io\u003e to see how to publish the documents.\n\n## References\n\nThese following language servers can be a good example for beginners:\n\n### [zathura-language-server](https://github.com/Freed-Wu/zathura-language-server)\n\n`zathurarc`'s syntax only has 4 directives:\n\n- `set option value`\n- `include /the/path`\n- `map key function`\n- `unmap key`\n\nVery few directives make creating\n[tree-sitter-zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) and\nediting `schema.py` very easy. So I am highly recommended starting from it.\n\n### [tmux-language-server](https://github.com/Freed-Wu/tmux-language-server)\n\n`tmux.conf` is more complex than `zathurarc`. It has not only\n`set option = value` and `source /the/path`, but also 170+ other directives.\n\n### [mutt-language-server](https://github.com/neomutt/mutt-language-server)\n\n`muttrc` or `neomuttrc` has the following directives:\n\n- `set option = value`\n- `source /the/path`\n- 80+ other directives\n\nHowever, its `set` syntax is very flexible. The following syntaxes are legal:\n\n- `set option2 = value1 option2 = value2 ...`\n- `set option`: a shortcut for `set option = yes`\n- `set nooption`: a shortcut for `set option = no`\n- `set invoption`\n- `set nooption1 invoption2 option3 ...`\n- ...\n\nSo, in fact it is harder than `tmux.conf`, IMO.\n\n### [termux-language-server](https://github.com/termux/termux-language-server)\n\n`build.sh`, `PKGBUILD`, `*.ebuild` use same syntax of bash. However, they use\ndifferent json schemas. If the language where you want to create a language\nserver, you can refer it to know how to handle this situation.\n\n### Other references\n\nSome useful URLs for beginners who want to develop language servers:\n\n- some [Chinese blogs](https://freed-wu.github.io/tag/lsp/) about how I write\n  these language servers\n- [tree-sitter](https://tree-sitter.github.io/tree-sitter/)\n- [language server protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current)\n- [json schema](https://json-schema.org/specification)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneomutt%2Flsp-tree-sitter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneomutt%2Flsp-tree-sitter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneomutt%2Flsp-tree-sitter/lists"}