feat!: drop modules, general refactor and cleanup

This commit is contained in:
Christian Clason
2023-06-12 09:54:30 -06:00
parent 310f0925ec
commit 692b051b09
1247 changed files with 6096 additions and 9074 deletions

View File

@@ -3,7 +3,7 @@ name: Linting and style checking
on:
pull_request:
branches:
- "master"
- "main"
jobs:
luacheck:
@@ -37,7 +37,7 @@ jobs:
name: Lint queries
runs-on: ubuntu-latest
env:
NVIM_TAG: stable
NVIM_TAG: nightly
steps:
- uses: actions/checkout@v4
- name: Prepare

View File

@@ -85,4 +85,4 @@ jobs:
run: cp -r ~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/* parser
- name: Check query files
run: $NVIM -l scripts/check-queries.lua
run: $NVIM -l ./scripts/check-queries.lua

View File

@@ -1,6 +1,6 @@
column_width = 120
column_width = 100
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "None"
quote_style = "AutoPreferSingle"
call_parentheses = "Always"

View File

@@ -5,10 +5,7 @@ First of all, thank you very much for contributing to `nvim-treesitter`.
If you haven't already, you should really come and reach out to us on our
[Matrix channel], so we can help you with any question you might have!
As you know, `nvim-treesitter` is roughly split in two parts:
- Parser configurations : for various things like `locals`, `highlights`
- What we like to call _modules_ : tiny Lua modules that provide a given feature, based on parser configurations
The main goal of `nvim-treesitter` is to provide a framework to easily install parsers and queries.
Depending on which part of the plugin you want to contribute to, please read the appropriate section.
@@ -25,25 +22,9 @@ cargo install stylua
ln -s ../../scripts/pre-push .git/hooks/pre-push
```
## Adding new modules
If you want to see a new functionality added to `nvim-treesitter` feel free to first open an issue
to that we can track our solution!
Thus far, there is basically two types of modules:
- Little modules (like `incremental selection`) that are built in `nvim-treesitter`, we call them
`builtin modules`.
- Bigger modules (like `completion-treesitter`, or `nvim-tree-docs`), or modules that integrate
with other plugins, that we call `remote modules`.
In any case, you can build your own module! To help you started in the process, we have a template
repository designed to build new modules [here](https://github.com/nvim-treesitter/module-template).
Feel free to use it, and contact us over on our
on the "Neovim tree-sitter" [Matrix channel].
## Parser configurations
Contributing to parser configurations is basically modifying one of the `queries/*/*.scm`.
Contributing to parser configurations is basically modifying one of the `runtime/queries/*/*.scm`.
Each of these `scheme` files contains a _tree-sitter query_ for a given purpose.
Before going any further, we highly suggest that you [read more about tree-sitter queries](https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries).
@@ -59,14 +40,12 @@ For now these are the types of queries used by `nvim-treesitter`:
For these types there is a _norm_ you will have to follow so that features work fine.
Here are some global advices:
- If your language is listed [here](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages),
you can install the [playground plugin](https://github.com/nvim-treesitter/playground).
- If your language is listed [here](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages),
you can debug and experiment with your queries there.
- If not, you should consider installing the [tree-sitter CLI](https://github.com/tree-sitter/tree-sitter/tree/master/cli),
you should then be able to open a local playground using `tree-sitter build-wasm && tree-sitter web-ui` within the
parsers repo.
- Examples of queries can be found in [queries/](queries/)
- Examples of queries can be found in [runtime/queries/](runtime/queries/)
- Matches in the bottom will override queries that are above of them.
#### Inheriting languages

View File

@@ -1,7 +0,0 @@
# https://github.com/luarocks/luarocks/wiki/Creating-a-Makefile-that-plays-nice-with-LuaRocks
build:
echo "Do nothing"
install:
mkdir -p $(INST_LUADIR)
cp -r lua/* $(INST_LUADIR)

754
README.md
View File

@@ -1,5 +1,7 @@
<h1 align="center">
<img src="https://github.com/nvim-treesitter/nvim-treesitter/assets/2361214/0513b223-c902-4f12-92ee-8ac4d8d6f41f" alt="nvim-treesitter">
</h1>
<div align="center">
<h1>nvim-treesitter</h1>
<p>
<a href="https://matrix.to/#/#nvim-treesitter:matrix.org">
<img alt="Matrix Chat" src="https://img.shields.io/matrix/nvim-treesitter:matrix.org" />
@@ -13,608 +15,111 @@
</p>
</div>
<div align="center">
<p>
<img src="assets/logo.png" align="center" alt="Logo" />
</p>
<p>
<a href="https://github.com/tree-sitter/tree-sitter">Treesitter</a>
configurations and abstraction layer for
<a href="https://github.com/neovim/neovim/">Neovim</a>.
</p>
<p>
<i>
Logo by <a href="https://github.com/steelsojka">@steelsojka</a>
</i>
</p>
</div>
# WARNING
The goal of `nvim-treesitter` is both to provide a simple and easy way to use the interface for [tree-sitter](https://github.com/tree-sitter/tree-sitter) in Neovim and to provide some basic functionality such as highlighting based on it:
**This branch is a [full, incompatible, rewrite of `nvim-treesitter`](https://github.com/nvim-treesitter/nvim-treesitter/issues/4767) and [work in progress](TODO.md). This branch REQUIRES (the latest commit on) Neovim `master`.**
![example-cpp](https://user-images.githubusercontent.com/2361214/202753610-e923bf4e-e88f-494b-bb1e-d22a7688446f.png)
Traditional highlighting (left) vs Treesitter-based highlighting (right).
More examples can be found in [our gallery](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery).
**Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
We recommend using the nightly builds of Neovim if possible.
You can find the current roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/issues/4767).
The roadmap and all features of this plugin are open to change, and any suggestion will be highly appreciated!**
Nvim-treesitter is based on three interlocking features: [**language parsers**](#language-parsers), [**queries**](#adding-queries), and [**modules**](#available-modules), where _modules_ provide features e.g., highlighting based on _queries_ for syntax objects extracted from a given buffer by _language parsers_.
Users will generally only need to interact with parsers and modules as explained in the next section.
For more detailed information on setting these up, see ["Advanced setup"](#advanced-setup).
---
### Table of contents
- [Quickstart](#quickstart)
- [Supported languages](#supported-languages)
- [Available modules](#available-modules)
- [Advanced setup](#advanced-setup)
- [Extra features](#extra-features)
- [Troubleshooting](#troubleshooting)
---
The `nvim-treesitter` plugin provides
1. functions for installing, updating, and removing [**tree-sitter parsers**](SUPPORTED_LANGUAGES.md);
2. a collection of **queries** for enabling tree-sitter features built into Neovim for these languages.
# Quickstart
## Requirements
- **Neovim 0.10** or later ([nightly](https://github.com/neovim/neovim#install-from-source) recommended)
- Neovim 0.10.0 or later (nightly)
- `tar` and `curl` in your path (or alternatively `git`)
- A C compiler in your path and libstdc++ installed ([Windows users please read this!](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Windows-support)).
- a C compiler in your path and libstdc++ installed ([Windows users please read this!](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Windows-support))
- optional: `tree-sitter` CLI and `node`
## Installation
You can install `nvim-treesitter` with your favorite package manager (or using the native `package` feature of vim, see `:h packages`).
**NOTE: This plugin is only guaranteed to work with specific versions of language parsers** (as specified in the `lockfile.json`). **When upgrading the plugin, you must make sure that all installed parsers are updated to the latest version** via `:TSUpdate`.
It is strongly recommended to automate this; e.g., if you are using [vim-plug](https://github.com/junegunn/vim-plug), put this in your `init.vim` file:
```vim
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
```
For other plugin managers such as `packer.nvim`, see this [Installation page from the wiki](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Installation) (Note that this page is community maintained).
## Language parsers
Treesitter uses a different _parser_ for every language, which needs to be generated via `tree-sitter-cli` from a `grammar.js` file, then compiled to a `.so` library that needs to be placed in neovim's `runtimepath` (typically under `parser/{language}.so`).
To simplify this, `nvim-treesitter` provides commands to automate this process.
If the language is already [supported by `nvim-treesitter`](#supported-languages), you can install it with
```vim
:TSInstall <language_to_install>
```
This command supports tab expansion.
You can also get a list of all available languages and their installation status with `:TSInstallInfo`.
Parsers not on this list can be added manually by following the steps described under ["Adding parsers"](#adding-parsers) below.
To make sure a parser is at the latest compatible version (as specified in `nvim-treesitter`'s `lockfile.json`), use `:TSUpdate {language}`. To update all parsers unconditionally, use `:TSUpdate all` or just `:TSUpdate`.
## Modules
Each module provides a distinct tree-sitter-based feature such as [highlighting](#highlight), [indentation](#indentation), or [folding](#folding); see [`:h nvim-treesitter-modules`](doc/nvim-treesitter.txt) or ["Available modules"](#available-modules) below for a list of modules and their options.
Following examples assume that you are configuring neovim with lua. If you are using vimscript, see `:h lua-heredoc`.
All modules are disabled by default and need to be activated explicitly in your `init.lua`, e.g., via
It is strongly recommended to automate this; e.g., using [lazy.nvim](https://github.com/folke/lazy.nvim)
```lua
require'nvim-treesitter.configs'.setup {
-- A list of parser names, or "all" (the listed parsers MUST always be installed)
ensure_installed = { "c", "lua", "vim", "vimdoc", "query", "markdown", "markdown_inline" },
require('lazy').setup(
{ 'nvim-treesitter/nvim-treesitter', build = ':TSUpdate', lazy = false }
)
```
-- Install parsers synchronously (only applied to `ensure_installed`)
**NOTE: This plugin does not support lazy-loading.**
## Setup
`nvim-treesitter` can be configured by calling `setup`. The following snippet lists the available options and their default values. **You do not need to call `setup` for `nvim-treesitter` to work using default values.**
```lua
require'nvim-treesitter'.setup {
-- A list of parser names or tiers ('core', 'stable', 'community', 'unstable')
ensure_install = { },
-- List of parsers to ignore installing
ignore_install = { },
-- Install parsers synchronously (only applied to `ensure_install`)
sync_install = false,
-- Automatically install missing parsers when entering buffer
-- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
auto_install = true,
auto_install = false,
-- List of parsers to ignore installing (or "all")
ignore_install = { "javascript" },
---- If you need to change the installation directory of the parsers (see -> Advanced Setup)
-- parser_install_dir = "/some/path/to/store/parsers", -- Remember to run vim.opt.runtimepath:append("/some/path/to/store/parsers")!
highlight = {
enable = true,
-- NOTE: these are the names of the parsers and not the filetype. (for example if you want to
-- disable highlighting for the `tex` filetype, you need to include `latex` in this list as this is
-- the name of the parser)
-- list of language that will be disabled
disable = { "c", "rust" },
-- Or use a function for more flexibility, e.g. to disable slow treesitter highlight for large files
disable = function(lang, buf)
local max_filesize = 100 * 1024 -- 100 KB
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
if ok and stats and stats.size > max_filesize then
return true
end
end,
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
-- Directory to install parsers and queries to
install_dir = vim.fn.stdpath('data') .. '/site'
}
```
Each module can also be enabled or disabled interactively through the following commands:
```vim
:TSBufEnable {module} " enable module on current buffer
:TSBufDisable {module} " disable module on current buffer
:TSEnable {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype.
:TSDisable {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype.
:TSModuleInfo [{module}] " list information about modules state for each filetype
```
Check [`:h nvim-treesitter-commands`](doc/nvim-treesitter.txt) for a list of all available commands.
It may be necessary to reload the buffer (e.g., via `:e`) after enabling a module interactively.
# Supported languages
For `nvim-treesitter` to support a specific feature for a specific language requires both a parser for that language and an appropriate language-specific query file for that feature.
The following is a list of languages for which a parser can be installed through `:TSInstall`; a checked box means that `nvim-treesitter` also contains queries at least for the `highlight` module.
Experimental parsers are parsers that have a maintainer but are not stable enough for
daily use yet.
A list of the currently supported languages can be found [on this page](SUPPORTED_LANGUAGES.md).
We are looking for maintainers to add more parsers and to write query files for their languages. Check our [tracking issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/2282) for open language requests.
<!--This section of the README is automatically updated by a CI job-->
<!--parserinfo-->
- [x] [ada](https://github.com/briot/tree-sitter-ada) (maintained by @briot)
- [x] [agda](https://github.com/tree-sitter/tree-sitter-agda) (maintained by @Decodetalkers)
- [x] [angular](https://github.com/dlvandenberg/tree-sitter-angular) (experimental, maintained by @dlvandenberg)
- [x] [apex](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber, @xixiaofinland)
- [x] [arduino](https://github.com/ObserverOfTime/tree-sitter-arduino) (maintained by @ObserverOfTime)
- [x] [asm](https://github.com/RubixDev/tree-sitter-asm) (maintained by @RubixDev)
- [x] [astro](https://github.com/virchau13/tree-sitter-astro) (maintained by @virchau13)
- [x] [authzed](https://github.com/mleonidas/tree-sitter-authzed) (maintained by @mattpolzin)
- [ ] [awk](https://github.com/Beaglefoot/tree-sitter-awk)
- [x] [bash](https://github.com/tree-sitter/tree-sitter-bash) (maintained by @TravonteD)
- [x] [bass](https://github.com/vito/tree-sitter-bass) (maintained by @amaanq)
- [x] [beancount](https://github.com/polarmutex/tree-sitter-beancount) (maintained by @polarmutex)
- [x] [bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) (maintained by @theHamsta, @clason)
- [x] [bicep](https://github.com/amaanq/tree-sitter-bicep) (maintained by @amaanq)
- [x] [bitbake](https://github.com/amaanq/tree-sitter-bitbake) (maintained by @amaanq)
- [x] [blade](https://github.com/EmranMR/tree-sitter-blade) (maintained by @calebdw)
- [x] [blueprint](https://gitlab.com/gabmus/tree-sitter-blueprint.git) (experimental, maintained by @gabmus)
- [x] [bp](https://github.com/ambroisie/tree-sitter-bp) (maintained by @ambroisie)
- [x] [brightscript](https://github.com/ajdelcimmuto/tree-sitter-brightscript) (maintained by @ajdelcimmuto)
- [x] [c](https://github.com/tree-sitter/tree-sitter-c) (maintained by @amaanq)
- [x] [c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) (maintained by @amaanq)
- [x] [caddy](https://github.com/opa-oz/tree-sitter-caddy) (maintained by @opa-oz)
- [x] [cairo](https://github.com/amaanq/tree-sitter-cairo) (maintained by @amaanq)
- [x] [capnp](https://github.com/amaanq/tree-sitter-capnp) (maintained by @amaanq)
- [x] [chatito](https://github.com/ObserverOfTime/tree-sitter-chatito) (maintained by @ObserverOfTime)
- [x] [circom](https://github.com/Decurity/tree-sitter-circom) (maintained by @alexandr-martirosyan)
- [x] [clojure](https://github.com/sogaiu/tree-sitter-clojure) (maintained by @NoahTheDuke)
- [x] [cmake](https://github.com/uyha/tree-sitter-cmake) (maintained by @uyha)
- [x] [comment](https://github.com/stsewd/tree-sitter-comment) (maintained by @stsewd)
- [x] [commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) (maintained by @theHamsta)
- [x] [cooklang](https://github.com/addcninblue/tree-sitter-cooklang) (maintained by @addcninblue)
- [x] [corn](https://github.com/jakestanger/tree-sitter-corn) (maintained by @jakestanger)
- [x] [cpon](https://github.com/amaanq/tree-sitter-cpon) (maintained by @amaanq)
- [x] [cpp](https://github.com/tree-sitter/tree-sitter-cpp) (maintained by @theHamsta)
- [x] [css](https://github.com/tree-sitter/tree-sitter-css) (maintained by @TravonteD)
- [x] [csv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq)
- [x] [cuda](https://github.com/theHamsta/tree-sitter-cuda) (maintained by @theHamsta)
- [x] [cue](https://github.com/eonpatapon/tree-sitter-cue) (maintained by @amaanq)
- [x] [cylc](https://github.com/elliotfontaine/tree-sitter-cylc) (maintained by @elliotfontaine)
- [x] [d](https://github.com/gdamore/tree-sitter-d) (maintained by @amaanq)
- [x] [dart](https://github.com/UserNobody14/tree-sitter-dart) (maintained by @akinsho)
- [x] [desktop](https://github.com/ValdezFOmar/tree-sitter-desktop) (maintained by @ValdezFOmar)
- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar)
- [x] [dhall](https://github.com/jbellerb/tree-sitter-dhall) (maintained by @amaanq)
- [x] [diff](https://github.com/the-mikedavis/tree-sitter-diff) (maintained by @gbprod)
- [x] [disassembly](https://github.com/ColinKennedy/tree-sitter-disassembly) (maintained by @ColinKennedy)
- [x] [djot](https://github.com/treeman/tree-sitter-djot) (maintained by @NoahTheDuke)
- [x] [dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) (maintained by @camdencheek)
- [x] [dot](https://github.com/rydesun/tree-sitter-dot) (maintained by @rydesun)
- [x] [doxygen](https://github.com/amaanq/tree-sitter-doxygen) (maintained by @amaanq)
- [x] [dtd](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime)
- [x] [earthfile](https://github.com/glehmann/tree-sitter-earthfile) (maintained by @glehmann)
- [x] [ebnf](https://github.com/RubixDev/ebnf) (experimental, maintained by @RubixDev)
- [x] [editorconfig](https://github.com/ValdezFOmar/tree-sitter-editorconfig) (maintained by @ValdezFOmar)
- [x] [eds](https://github.com/uyha/tree-sitter-eds) (maintained by @uyha)
- [x] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay)
- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @connorlay)
- [x] [elm](https://github.com/elm-tooling/tree-sitter-elm) (maintained by @zweimach)
- [x] [elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) (maintained by @glapa-grossklag, @amaanq)
- [x] [elvish](https://github.com/elves/tree-sitter-elvish) (maintained by @elves)
- [ ] [embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template)
- [x] [enforce](https://github.com/simonvic/tree-sitter-enforce) (maintained by @simonvic)
- [x] [erlang](https://github.com/WhatsApp/tree-sitter-erlang) (maintained by @filmor)
- [x] [facility](https://github.com/FacilityApi/tree-sitter-facility) (maintained by @bryankenote)
- [x] [faust](https://github.com/khiner/tree-sitter-faust) (maintained by @khiner)
- [x] [fennel](https://github.com/alexmozaidze/tree-sitter-fennel) (maintained by @alexmozaidze)
- [x] [fidl](https://github.com/google/tree-sitter-fidl) (maintained by @chaopeng)
- [x] [firrtl](https://github.com/amaanq/tree-sitter-firrtl) (maintained by @amaanq)
- [x] [fish](https://github.com/ram02z/tree-sitter-fish) (maintained by @ram02z)
- [x] [foam](https://github.com/FoamScience/tree-sitter-foam) (experimental, maintained by @FoamScience)
- [x] [forth](https://github.com/AlexanderBrevig/tree-sitter-forth) (maintained by @amaanq)
- [x] [fortran](https://github.com/stadelmanma/tree-sitter-fortran) (maintained by @amaanq)
- [x] [fsh](https://github.com/mgramigna/tree-sitter-fsh) (maintained by @mgramigna)
- [x] [fsharp](https://github.com/ionide/tree-sitter-fsharp) (maintained by @nsidorenco)
- [x] [func](https://github.com/amaanq/tree-sitter-func) (maintained by @amaanq)
- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn)
- [x] [GAP system](https://github.com/gap-system/tree-sitter-gap) (maintained by @reiniscirpons)
- [x] [GAP system test files](https://github.com/gap-system/tree-sitter-gaptst) (maintained by @reiniscirpons)
- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @PrestonKnopp)
- [x] [gdshader](https://github.com/GodOfAvacyn/tree-sitter-gdshader) (maintained by @godofavacyn)
- [x] [git_config](https://github.com/the-mikedavis/tree-sitter-git-config) (maintained by @amaanq)
- [x] [git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) (maintained by @gbprod)
- [x] [gitattributes](https://github.com/ObserverOfTime/tree-sitter-gitattributes) (maintained by @ObserverOfTime)
- [x] [gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) (maintained by @gbprod)
- [x] [gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) (maintained by @theHamsta)
- [x] [gleam](https://github.com/gleam-lang/tree-sitter-gleam) (maintained by @amaanq)
- [x] [Glimmer and Ember](https://github.com/ember-tooling/tree-sitter-glimmer) (maintained by @NullVoxPopuli)
- [x] [glimmer_javascript](https://github.com/NullVoxPopuli/tree-sitter-glimmer-javascript) (maintained by @NullVoxPopuli)
- [x] [glimmer_typescript](https://github.com/NullVoxPopuli/tree-sitter-glimmer-typescript) (maintained by @NullVoxPopuli)
- [x] [glsl](https://github.com/theHamsta/tree-sitter-glsl) (maintained by @theHamsta)
- [x] [GN (Generate Ninja)](https://github.com/amaanq/tree-sitter-gn) (maintained by @amaanq)
- [x] [gnuplot](https://github.com/dpezto/tree-sitter-gnuplot) (maintained by @dpezto)
- [x] [go](https://github.com/tree-sitter/tree-sitter-go) (maintained by @theHamsta, @WinWisely268)
- [x] [goctl](https://github.com/chaozwn/tree-sitter-goctl) (maintained by @chaozwn)
- [x] [Godot Resources (gdresource)](https://github.com/PrestonKnopp/tree-sitter-godot-resource) (maintained by @pierpo)
- [x] [gomod](https://github.com/camdencheek/tree-sitter-go-mod) (maintained by @camdencheek)
- [x] [gosum](https://github.com/amaanq/tree-sitter-go-sum) (maintained by @amaanq)
- [x] [gotmpl](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin)
- [x] [gowork](https://github.com/omertuc/tree-sitter-go-work) (maintained by @omertuc)
- [x] [gpg](https://github.com/ObserverOfTime/tree-sitter-gpg-config) (maintained by @ObserverOfTime)
- [x] [graphql](https://github.com/bkegley/tree-sitter-graphql) (maintained by @bkegley)
- [x] [gren](https://github.com/MaeBrooks/tree-sitter-gren) (maintained by @MaeBrooks)
- [x] [groovy](https://github.com/murtaza64/tree-sitter-groovy) (maintained by @murtaza64)
- [x] [gstlaunch](https://github.com/theHamsta/tree-sitter-gstlaunch) (maintained by @theHamsta)
- [ ] [hack](https://github.com/slackhq/tree-sitter-hack)
- [x] [hare](https://github.com/amaanq/tree-sitter-hare) (maintained by @amaanq)
- [x] [haskell](https://github.com/tree-sitter/tree-sitter-haskell) (maintained by @mrcjkb)
- [x] [haskell_persistent](https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent) (maintained by @lykahb)
- [x] [hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann)
- [x] [heex](https://github.com/connorlay/tree-sitter-heex) (maintained by @connorlay)
- [x] [helm](https://github.com/ngalaiko/tree-sitter-go-template) (maintained by @qvalentin)
- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410)
- [x] [hlsl](https://github.com/theHamsta/tree-sitter-hlsl) (maintained by @theHamsta)
- [x] [hlsplaylist](https://github.com/Freed-Wu/tree-sitter-hlsplaylist) (maintained by @Freed-Wu)
- [x] [hocon](https://github.com/antosha417/tree-sitter-hocon) (maintained by @antosha417)
- [x] [hoon](https://github.com/urbit-pilled/tree-sitter-hoon) (experimental, maintained by @urbit-pilled)
- [x] [html](https://github.com/tree-sitter/tree-sitter-html) (maintained by @TravonteD)
- [x] [htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) (experimental, maintained by @ObserverOfTime)
- [x] [http](https://github.com/rest-nvim/tree-sitter-http) (maintained by @amaanq, @NTBBloodbath)
- [x] [hurl](https://github.com/pfeiferj/tree-sitter-hurl) (maintained by @pfeiferj)
- [x] [hyprlang](https://github.com/luckasRanarison/tree-sitter-hyprlang) (maintained by @luckasRanarison)
- [x] [idl](https://github.com/cathaysia/tree-sitter-idl) (maintained by @cathaysia)
- [x] [idris](https://github.com/kayhide/tree-sitter-idris) (maintained by @srghma)
- [x] [ini](https://github.com/justinmk/tree-sitter-ini) (experimental, maintained by @theHamsta)
- [x] [inko](https://github.com/inko-lang/tree-sitter-inko) (maintained by @yorickpeterse)
- [x] [ipkg](https://github.com/srghma/tree-sitter-ipkg) (maintained by @srghma)
- [x] [ispc](https://github.com/fab4100/tree-sitter-ispc) (maintained by @fab4100)
- [x] [janet_simple](https://github.com/sogaiu/tree-sitter-janet-simple) (maintained by @sogaiu)
- [x] [java](https://github.com/tree-sitter/tree-sitter-java) (maintained by @p00f)
- [x] [javadoc](https://github.com/rmuir/tree-sitter-javadoc) (maintained by @rmuir)
- [x] [javascript](https://github.com/tree-sitter/tree-sitter-javascript) (maintained by @steelsojka)
- [x] [jinja](https://github.com/cathaysia/tree-sitter-jinja) (maintained by @cathaysia)
- [x] [jinja_inline](https://github.com/cathaysia/tree-sitter-jinja) (maintained by @cathaysia)
- [x] [jq](https://github.com/flurie/tree-sitter-jq) (maintained by @ObserverOfTime)
- [x] [jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) (maintained by @steelsojka)
- [x] [json](https://github.com/tree-sitter/tree-sitter-json) (maintained by @steelsojka)
- [x] [json5](https://github.com/Joakker/tree-sitter-json5) (maintained by @Joakker)
- [x] [JSON with comments](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git) (maintained by @WhyNotHugo)
- [x] [jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) (maintained by @nawordar)
- [x] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @fredrikekre)
- [x] [just](https://github.com/IndianBoy42/tree-sitter-just) (maintained by @Hubro)
- [x] [kcl](https://github.com/kcl-lang/tree-sitter-kcl) (maintained by @bertbaron)
- [x] [kconfig](https://github.com/amaanq/tree-sitter-kconfig) (maintained by @amaanq)
- [x] [kdl](https://github.com/amaanq/tree-sitter-kdl) (maintained by @amaanq)
- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa)
- [x] [koto](https://github.com/koto-lang/tree-sitter-koto) (maintained by @irh)
- [x] [kusto](https://github.com/Willem-J-an/tree-sitter-kusto) (maintained by @Willem-J-an)
- [x] [lalrpop](https://github.com/traxys/tree-sitter-lalrpop) (maintained by @traxys)
- [x] [latex](https://github.com/latex-lsp/tree-sitter-latex) (maintained by @theHamsta, @clason)
- [x] [ledger](https://github.com/cbarrete/tree-sitter-ledger) (maintained by @cbarrete)
- [x] [leo](https://github.com/r001/tree-sitter-leo) (maintained by @r001)
- [x] [linkerscript](https://github.com/amaanq/tree-sitter-linkerscript) (maintained by @amaanq)
- [x] [liquid](https://github.com/hankthetank27/tree-sitter-liquid) (maintained by @hankthetank27)
- [x] [liquidsoap](https://github.com/savonet/tree-sitter-liquidsoap) (maintained by @toots)
- [x] [llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) (maintained by @benwilliamgraham)
- [x] [lua](https://github.com/MunifTanjim/tree-sitter-lua) (maintained by @muniftanjim)
- [x] [luadoc](https://github.com/amaanq/tree-sitter-luadoc) (maintained by @amaanq)
- [x] [lua patterns](https://github.com/amaanq/tree-sitter-luap) (maintained by @amaanq)
- [x] [luau](https://github.com/amaanq/tree-sitter-luau) (maintained by @amaanq)
- [x] [m68k](https://github.com/grahambates/tree-sitter-m68k) (maintained by @grahambates)
- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991)
- [x] [markdown (basic highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml)
- [x] [markdown_inline (needed for full highlighting)](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml)
- [x] [matlab](https://github.com/acristoffers/tree-sitter-matlab) (maintained by @acristoffers)
- [x] [menhir](https://github.com/Kerl13/tree-sitter-menhir) (maintained by @Kerl13)
- [ ] [mermaid](https://github.com/monaqa/tree-sitter-mermaid) (experimental)
- [x] [meson](https://github.com/Decodetalkers/tree-sitter-meson) (maintained by @Decodetalkers)
- [x] [mlir](https://github.com/artagnon/tree-sitter-mlir) (experimental, maintained by @artagnon)
- [x] [muttrc](https://github.com/neomutt/tree-sitter-muttrc) (maintained by @Freed-Wu)
- [x] [nasm](https://github.com/naclsn/tree-sitter-nasm) (maintained by @ObserverOfTime)
- [x] [nginx](https://github.com/opa-oz/tree-sitter-nginx) (maintained by @opa-oz)
- [ ] [nickel](https://github.com/nickel-lang/tree-sitter-nickel)
- [x] [nim](https://github.com/alaviss/tree-sitter-nim) (maintained by @aMOPel)
- [x] [nim_format_string](https://github.com/aMOPel/tree-sitter-nim-format-string) (maintained by @aMOPel)
- [x] [ninja](https://github.com/alemuller/tree-sitter-ninja) (maintained by @alemuller)
- [x] [nix](https://github.com/cstrahan/tree-sitter-nix) (maintained by @leo60228)
- [x] [norg](https://github.com/nvim-neorg/tree-sitter-norg) (maintained by @JoeyGrajciar, @vhyrro)
- [x] [nqc](https://github.com/amaanq/tree-sitter-nqc) (maintained by @amaanq)
- [x] [nu](https://github.com/nushell/tree-sitter-nu) (maintained by @abhisheksingh0x558)
- [x] [objc](https://github.com/amaanq/tree-sitter-objc) (maintained by @amaanq)
- [x] [objdump](https://github.com/ColinKennedy/tree-sitter-objdump) (maintained by @ColinKennedy)
- [x] [ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu)
- [x] [ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) (maintained by @undu)
- [x] [ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) (maintained by @undu)
- [x] [odin](https://github.com/amaanq/tree-sitter-odin) (maintained by @amaanq)
- [x] [pascal](https://github.com/Isopod/tree-sitter-pascal) (maintained by @Isopod)
- [x] [passwd](https://github.com/ath3/tree-sitter-passwd) (maintained by @amaanq)
- [x] [pem](https://github.com/ObserverOfTime/tree-sitter-pem) (maintained by @ObserverOfTime)
- [x] [perl](https://github.com/tree-sitter-perl/tree-sitter-perl) (maintained by @RabbiVeesh, @LeoNerd)
- [x] [php](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka, @calebdw)
- [x] [php_only](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka, @calebdw)
- [x] [phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) (experimental, maintained by @mikehaertl)
- [x] [pioasm](https://github.com/leo60228/tree-sitter-pioasm) (maintained by @leo60228)
- [x] [po](https://github.com/erasin/tree-sitter-po) (maintained by @amaanq)
- [x] [pod](https://github.com/tree-sitter-perl/tree-sitter-pod) (maintained by @RabbiVeesh, @LeoNerd)
- [x] [Path of Exile item filter](https://github.com/ObserverOfTime/tree-sitter-poe-filter) (experimental, maintained by @ObserverOfTime)
- [x] [pony](https://github.com/amaanq/tree-sitter-pony) (maintained by @amaanq, @mfelsche)
- [x] [powershell](https://github.com/airbus-cert/tree-sitter-powershell) (maintained by @L2jLiga)
- [x] [printf](https://github.com/ObserverOfTime/tree-sitter-printf) (maintained by @ObserverOfTime)
- [x] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva)
- [x] [problog](https://github.com/foxyseta/tree-sitter-prolog) (maintained by @foxyseta)
- [x] [prolog](https://github.com/foxyseta/tree-sitter-prolog) (maintained by @foxyseta)
- [x] [promql](https://github.com/MichaHoffmann/tree-sitter-promql) (maintained by @MichaHoffmann)
- [x] [properties](https://github.com/tree-sitter-grammars/tree-sitter-properties) (maintained by @ObserverOfTime)
- [x] [proto](https://github.com/treywood/tree-sitter-proto) (maintained by @treywood)
- [x] [prql](https://github.com/PRQL/tree-sitter-prql) (maintained by @matthias-Q)
- [x] [psv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq)
- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (experimental, maintained by @zealot128)
- [x] [puppet](https://github.com/amaanq/tree-sitter-puppet) (maintained by @amaanq)
- [x] [purescript](https://github.com/postsolar/tree-sitter-purescript) (maintained by @postsolar)
- [x] [PyPA manifest](https://github.com/ObserverOfTime/tree-sitter-pymanifest) (maintained by @ObserverOfTime)
- [x] [python](https://github.com/tree-sitter/tree-sitter-python) (maintained by @stsewd, @theHamsta)
- [x] [ql](https://github.com/tree-sitter/tree-sitter-ql) (maintained by @pwntester)
- [x] [qmldir](https://github.com/Decodetalkers/tree-sitter-qmldir) (maintained by @amaanq)
- [x] [qmljs](https://github.com/yuja/tree-sitter-qmljs) (maintained by @Decodetalkers)
- [x] [Tree-Sitter query language](https://github.com/nvim-treesitter/tree-sitter-query) (maintained by @steelsojka)
- [x] [r](https://github.com/r-lib/tree-sitter-r) (maintained by @ribru17)
- [ ] [racket](https://github.com/6cdh/tree-sitter-racket)
- [x] [ralph](https://github.com/alephium/tree-sitter-ralph) (maintained by @tdroxler)
- [x] [rasi](https://github.com/Fymyte/tree-sitter-rasi) (maintained by @Fymyte)
- [x] [razor](https://github.com/tris203/tree-sitter-razor) (maintained by @tris203)
- [x] [rbs](https://github.com/joker1007/tree-sitter-rbs) (maintained by @joker1007)
- [x] [re2c](https://github.com/amaanq/tree-sitter-re2c) (maintained by @amaanq)
- [x] [readline](https://github.com/ribru17/tree-sitter-readline) (maintained by @ribru17)
- [x] [regex](https://github.com/tree-sitter/tree-sitter-regex) (maintained by @theHamsta)
- [x] [rego](https://github.com/FallenAngel97/tree-sitter-rego) (maintained by @FallenAngel97)
- [x] [pip requirements](https://github.com/ObserverOfTime/tree-sitter-requirements) (maintained by @ObserverOfTime)
- [x] [rescript](https://github.com/rescript-lang/tree-sitter-rescript) (maintained by @ribru17)
- [x] [rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) (maintained by @bamonroe)
- [x] [robot](https://github.com/Hubro/tree-sitter-robot) (maintained by @Hubro)
- [x] [robots](https://github.com/opa-oz/tree-sitter-robots-txt) (maintained by @opa-oz)
- [x] [roc](https://github.com/faldor20/tree-sitter-roc) (maintained by @nat-418)
- [x] [ron](https://github.com/amaanq/tree-sitter-ron) (maintained by @amaanq)
- [x] [rst](https://github.com/stsewd/tree-sitter-rst) (maintained by @stsewd)
- [x] [ruby](https://github.com/tree-sitter/tree-sitter-ruby) (maintained by @TravonteD)
- [x] [runescript](https://github.com/2004Scape/tree-sitter-runescript) (maintained by @2004Scape)
- [x] [rust](https://github.com/tree-sitter/tree-sitter-rust) (maintained by @amaanq)
- [x] [scala](https://github.com/tree-sitter/tree-sitter-scala) (maintained by @stevanmilic)
- [x] [scfg](https://github.com/rockorager/tree-sitter-scfg) (maintained by @WhyNotHugo)
- [ ] [scheme](https://github.com/6cdh/tree-sitter-scheme)
- [x] [scss](https://github.com/serenadeai/tree-sitter-scss) (maintained by @elianiva)
- [x] [sflog](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber, @xixiaofinland)
- [x] [slang](https://github.com/theHamsta/tree-sitter-slang) (experimental, maintained by @theHamsta)
- [x] [slim](https://github.com/theoo/tree-sitter-slim) (maintained by @theoo)
- [x] [slint](https://github.com/slint-ui/tree-sitter-slint) (maintained by @hunger)
- [x] [smali](https://github.com/tree-sitter-grammars/tree-sitter-smali) (maintained by @amaanq)
- [x] [smithy](https://github.com/indoorvivants/tree-sitter-smithy) (maintained by @amaanq, @keynmol)
- [ ] [snakemake](https://github.com/osthomas/tree-sitter-snakemake) (experimental)
- [x] [solidity](https://github.com/JoranHonig/tree-sitter-solidity) (maintained by @amaanq)
- [x] [soql](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber, @xixiaofinland)
- [x] [sosl](https://github.com/aheber/tree-sitter-sfapex) (maintained by @aheber, @xixiaofinland)
- [x] [sourcepawn](https://github.com/nilshelmig/tree-sitter-sourcepawn) (maintained by @Sarrus1)
- [x] [sparql](https://github.com/GordianDziwis/tree-sitter-sparql) (maintained by @GordianDziwis)
- [x] [sql](https://github.com/derekstride/tree-sitter-sql) (maintained by @derekstride)
- [x] [squirrel](https://github.com/amaanq/tree-sitter-squirrel) (maintained by @amaanq)
- [x] [ssh_config](https://github.com/ObserverOfTime/tree-sitter-ssh-config) (maintained by @ObserverOfTime)
- [x] [starlark](https://github.com/amaanq/tree-sitter-starlark) (maintained by @amaanq)
- [x] [strace](https://github.com/sigmaSd/tree-sitter-strace) (maintained by @amaanq)
- [x] [styled](https://github.com/mskelton/tree-sitter-styled) (maintained by @mskelton)
- [x] [supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) (maintained by @madskjeldgaard)
- [x] [superhtml](https://github.com/kristoff-it/superhtml) (maintained by @rockorager)
- [x] [surface](https://github.com/connorlay/tree-sitter-surface) (maintained by @connorlay)
- [x] [svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) (maintained by @amaanq)
- [x] [sway](https://github.com/FuelLabs/tree-sitter-sway.git) (maintained by @ribru17)
- [x] [swift](https://github.com/alex-pinkus/tree-sitter-swift) (maintained by @alex-pinkus)
- [x] [sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) (maintained by @RaafatTurki)
- [x] [systemtap](https://github.com/ok-ryoko/tree-sitter-systemtap) (maintained by @ok-ryoko)
- [x] [t32](https://gitlab.com/xasc/tree-sitter-t32.git) (maintained by @xasc)
- [x] [tablegen](https://github.com/amaanq/tree-sitter-tablegen) (maintained by @amaanq)
- [x] [tact](https://github.com/tact-lang/tree-sitter-tact) (maintained by @novusnota)
- [x] [tcl](https://github.com/tree-sitter-grammars/tree-sitter-tcl) (maintained by @lewis6991)
- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce)
- [x] [templ](https://github.com/vrischmann/tree-sitter-templ) (maintained by @vrischmann)
- [x] [tera](https://github.com/uncenter/tree-sitter-tera) (maintained by @uncenter)
- [x] [terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann)
- [x] [textproto](https://github.com/PorterAtGoogle/tree-sitter-textproto) (maintained by @Porter)
- [x] [thrift](https://github.com/duskmoon314/tree-sitter-thrift) (maintained by @amaanq, @duskmoon314)
- [x] [tiger](https://github.com/ambroisie/tree-sitter-tiger) (maintained by @ambroisie)
- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer, @susliko)
- [x] [tmux](https://github.com/Freed-Wu/tree-sitter-tmux) (maintained by @Freed-Wu)
- [x] [todotxt](https://github.com/arnarg/tree-sitter-todotxt) (experimental, maintained by @arnarg)
- [x] [toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) (maintained by @tk-shirasaka)
- [x] [tsv](https://github.com/amaanq/tree-sitter-csv) (maintained by @amaanq)
- [x] [tsx](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka)
- [x] [turtle](https://github.com/GordianDziwis/tree-sitter-turtle) (maintained by @GordianDziwis)
- [x] [twig](https://github.com/gbprod/tree-sitter-twig) (maintained by @gbprod)
- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka)
- [x] [typespec](https://github.com/happenslol/tree-sitter-typespec) (maintained by @happenslol)
- [x] [typoscript](https://github.com/Teddytrombone/tree-sitter-typoscript) (maintained by @Teddytrombone)
- [x] [typst](https://github.com/uben0/tree-sitter-typst) (maintained by @uben0, @RaafatTurki)
- [x] [udev](https://github.com/ObserverOfTime/tree-sitter-udev) (maintained by @ObserverOfTime)
- [x] [ungrammar](https://github.com/Philipp-M/tree-sitter-ungrammar) (maintained by @Philipp-M, @amaanq)
- [x] [unison](https://github.com/kylegoetz/tree-sitter-unison) (maintained by @tapegram)
- [x] [usd](https://github.com/ColinKennedy/tree-sitter-usd) (maintained by @ColinKennedy)
- [x] [uxn tal](https://github.com/amaanq/tree-sitter-uxntal) (maintained by @amaanq)
- [x] [v](https://github.com/vlang/v-analyzer) (maintained by @kkharji, @amaanq)
- [x] [vala](https://github.com/vala-lang/tree-sitter-vala) (maintained by @Prince781)
- [x] [vento](https://github.com/ventojs/tree-sitter-vento) (maintained by @wrapperup, @oscarotero)
- [x] [verilog](https://github.com/gmlarumbe/tree-sitter-systemverilog) (maintained by @zhangwwpeng)
- [x] [vhdl](https://github.com/jpt13653903/tree-sitter-vhdl) (maintained by @jpt13653903)
- [x] [vhs](https://github.com/charmbracelet/tree-sitter-vhs) (maintained by @caarlos0)
- [x] [vim](https://github.com/neovim/tree-sitter-vim) (maintained by @clason)
- [x] [vimdoc](https://github.com/neovim/tree-sitter-vimdoc) (maintained by @clason)
- [x] [vrl](https://github.com/belltoy/tree-sitter-vrl) (maintained by @belltoy)
- [x] [vue](https://github.com/tree-sitter-grammars/tree-sitter-vue) (maintained by @WhyNotHugo, @lucario387)
- [x] [wgsl](https://github.com/szebniok/tree-sitter-wgsl) (maintained by @szebniok)
- [x] [wgsl_bevy](https://github.com/theHamsta/tree-sitter-wgsl-bevy) (maintained by @theHamsta)
- [x] [wing](https://github.com/winglang/tree-sitter-wing) (maintained by @gshpychka, @MarkMcCulloh)
- [x] [wit](https://github.com/liamwh/tree-sitter-wit) (maintained by @liamwh)
- [x] [xcompose](https://github.com/ObserverOfTime/tree-sitter-xcompose) (maintained by @ObserverOfTime)
- [x] [xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) (maintained by @ObserverOfTime)
- [x] [xresources](https://github.com/ValdezFOmar/tree-sitter-xresources) (maintained by @ValdezFOmar)
- [x] [yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml) (maintained by @amaanq)
- [x] [yang](https://github.com/Hubro/tree-sitter-yang) (maintained by @Hubro)
- [x] [yuck](https://github.com/Philipp-M/tree-sitter-yuck) (maintained by @Philipp-M, @amaanq)
- [x] [zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) (maintained by @Freed-Wu)
- [x] [zig](https://github.com/tree-sitter-grammars/tree-sitter-zig) (maintained by @amaanq)
- [x] [ziggy](https://github.com/kristoff-it/ziggy) (maintained by @rockorager)
- [x] [ziggy_schema](https://github.com/kristoff-it/ziggy) (maintained by @rockorager)
<!--parserinfo-->
For related information on the supported languages, including related plugins, see [this wiki page](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Supported-Languages-Information).
# Available modules
# Supported features
Modules provide the top-level features of `nvim-treesitter`.
The following is a list of modules included in `nvim-treesitter` and their configuration via `init.lua` (where multiple modules can be combined in a single call to `setup`).
Note that not all modules work for all languages (depending on the queries available for them).
Additional modules can be provided as [external plugins](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Extra-modules-and-plugins).
`nvim-treesitter` provides queries for the following features. **These are not automatically enabled.**
#### Highlight
## Highlighting
Consistent syntax highlighting.
Treesitter highlighting is provided by Neovim, see `:h treesitter-highlight`. To enable it for a filetype, put `vim.treesitter.start()` in a `ftplugin/<filetype>.lua` in your config directory, or place the following in your `init.lua`:
```lua
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
}
vim.api.nvim_create_autocmd('FileType', {
pattern = { '<filetype>' },
callback = function() vim.treesitter.start() end,
})
```
To customize the syntax highlighting of a capture, simply define or link a highlight group of the same name:
## Folds
Treesitter-based folding is provided by Neovim. To enable it, put the following in your `ftplugin` or `FileType` autocommand:
```lua
-- Highlight the @foo.bar capture group with the "Identifier" highlight group
vim.api.nvim_set_hl(0, "@foo.bar", { link = "Identifier" })
```
For a language-specific highlight, append the name of the language:
```lua
-- Highlight @foo.bar as "Identifier" only in Lua files
vim.api.nvim_set_hl(0, "@foo.bar.lua", { link = "Identifier" })
```
See `:h treesitter-highlight-groups` for details.
#### Incremental selection
Incremental selection based on the named nodes from the grammar.
```lua
require'nvim-treesitter.configs'.setup {
incremental_selection = {
enable = true,
keymaps = {
init_selection = "gnn", -- set to `false` to disable one of the mappings
node_incremental = "grn",
scope_incremental = "grc",
node_decremental = "grm",
},
},
}
```
#### Indentation
Indentation based on treesitter for the `=` operator.
**NOTE: This is an experimental feature**.
```lua
require'nvim-treesitter.configs'.setup {
indent = {
enable = true
}
}
```
#### Folding
Tree-sitter based folding (implemented in Neovim itself, see `:h vim.treesitter.foldexpr()`). To enable it for the current window, set
```lua
vim.wo.foldmethod = 'expr'
vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
```
This will respect your `foldminlines` and `foldnestmax` settings.
## Indentation
# Advanced setup
## Changing the parser install directory
If you want to install the parsers to a custom directory you can specify this
directory with `parser_install_dir` option in that is passed to `setup`.
`nvim-treesitter` will then install the parser files into this directory.
This directory must be writeable and must be explicitly prepended to the
`runtimepath`. For example:
Treesitter-based indentation is provided by this plugin but considered **experimental**. To enable it, put the following in your `ftplugin` or `FileType` autocommand:
```lua
-- It MUST be at the beginning of runtimepath. Otherwise the parsers from Neovim itself
-- is loaded that may not be compatible with the queries from the 'nvim-treesitter' plugin.
vim.opt.runtimepath:prepend("/some/path/to/store/parsers")
require'nvim-treesitter.configs'.setup {
parser_install_dir = "/some/path/to/store/parsers",
...
}
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
```
If this option is not included in the setup options, or is explicitly set to
`nil` then the default install directories will be used. If this value is set
the default directories will be ignored.
(Note the specific quotes used.)
Bear in mind that any parser installed into a parser folder on the runtime path
will still be considered installed. (For example if
"~/.local/share/nvim/site/parser/c.so" exists then the "c" parser will be
considered installed, even though it is not in `parser_install_dir`)
## Injections
The default paths are:
Injections are used for multi-language documents, see `:h treesitter-language-injections`. No setup is needed.
1. first the package folder. Where `nvim-treesitter` is installed.
2. second the site directory. This is the "site" subdirectory of `stdpath("data")`.
# Advanced setup
## Adding parsers
@@ -625,139 +130,56 @@ If you have a parser that is not on the list of supported languages (either as a
3. Add the following snippet to your `init.lua`:
```lua
local parser_config = require "nvim-treesitter.parsers".get_parser_configs()
local parser_config = require "nvim-treesitter.parsers".configs
parser_config.zimbu = {
install_info = {
url = "~/projects/tree-sitter-zimbu", -- local path or git repo
files = {"src/parser.c"}, -- note that some parsers also require src/scanner.c or src/scanner.cc
-- optional entries:
branch = "main", -- default branch in case of git repo if different from master
branch = "develop", -- only needed if different from default branch
generate_requires_npm = false, -- if stand-alone parser without npm dependencies
requires_generate_from_grammar = false, -- if folder contains pre-generated src/parser.c
},
filetype = "zu", -- if filetype does not match the parser name
}
```
If you use a git repository for your parser and want to use a specific version, you can set the `revision` key
in the `install_info` table for you parser config.
If you wish to set a specific parser for a filetype, you should use `vim.treesitter.language.register()`:
4. If the parser name differs from the filetype(s) used by Neovim, you need to register the parser via
```lua
vim.treesitter.language.register('python', 'someft') -- the someft filetype will use the python parser and queries.
vim.treesitter.language.register('zimbu', { 'zu' })
```
Note this requires Nvim v0.9.
If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`](<https://neovim.io/doc/user/lua.html#vim.filetype.add()>) to add a custom detection rule.
4. Start `nvim` and `:TSInstall zimbu`.
5. Start `nvim` and `:TSInstall zimbu`.
You can also skip step 2 and use `:TSInstallFromGrammar zimbu` to install directly from a `grammar.js` in the top-level directory specified by `url`.
Once the parser is installed, you can update it (from the latest revision of the `main` branch if `url` is a Github repository) with `:TSUpdate zimbu`.
Note that neither `:TSInstall` nor `:TSInstallFromGrammar` copy query files from the grammar repository.
If you want your installed grammar to be useful, you must manually [add query files](#adding-queries) to your local nvim-treesitter installation.
Note also that module functionality is only triggered if your language's filetype is correctly identified.
If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`](<https://neovim.io/doc/user/lua.html#vim.filetype.add()>) to add a custom detection rule.
If you use a git repository for your parser and want to use a specific version, you can set the `revision` key
in the `install_info` table for you parser config.
## Adding queries
Queries are what `nvim-treesitter` uses to extract information from the syntax tree;
they are located in the `queries/{language}/*` runtime directories (see `:h rtp`),
like the `queries` folder of this plugin, e.g. `queries/{language}/{locals,highlights,textobjects}.scm`.
Other modules may require additional queries such as `folding.scm`. You can find a
list of all supported capture names in [CONTRIBUTING.md](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations).
The first query file on `runtimepath` will be used (see `:h treesitter-query`).
If you want to make a query on the user config extend other queries instead of
replacing them, see `:h treesitter-query-modeline-extends`.
If you want to completely override a query, you can use `:h vim.treesitter.query.set()`.
For example, to override the `injections` queries from `c` with your own:
Queries can be placed anywhere in your `runtimepath` under `queries/<language>`, with earlier directories taking precedence unless the queries are marked with `; extends`; see `:h treesitter-query`.
E.g., to add queries for `zimbu`, put `highlights.scm` etc. under
```lua
vim.treesitter.query.set("c", "injections", "(comment) @comment")
vim.fn.stdpath('data') .. 'site/queries/zimbu'
```
Note: when using `query.set()`, all queries in the runtime directories will be ignored.
## Adding modules
If you wish you write your own module, you need to support
- tree-sitter language detection support;
- attaching and detaching to buffers;
- all nvim-treesitter commands.
At the top level, you can use the `define_modules` function to define one or more modules or module groups:
```lua
require'nvim-treesitter'.define_modules {
my_cool_plugin = {
attach = function(bufnr, lang)
-- Do cool stuff here
end,
detach = function(bufnr)
-- Undo cool stuff here
end,
is_supported = function(lang)
-- Check if the language is supported
end
}
}
```
with the following properties:
- `module_path` specifies a require path (string) that exports a module with an `attach` and `detach` function. This is not required if the functions are on this definition.
- `enable` determines if the module is enabled by default. This is usually overridden by the user.
- `disable` takes a list of languages that this module is disabled for. This is usually overridden by the user.
- `is_supported` takes a function that takes a language and determines if this module supports that language.
- `attach` takes a function that attaches to a buffer. This is required if `module_path` is not provided.
- `detach` takes a function that detaches from a buffer. This is required if `module_path` is not provided.
# Extra features
### Statusline indicator
```vim
echo nvim_treesitter#statusline(90) " 90 can be any length
module->expression_statement->call->identifier
```
### Utilities
You can get some utility functions with
```lua
local ts_utils = require 'nvim-treesitter.ts_utils'
```
Check [`:h nvim-treesitter-utils`](doc/nvim-treesitter.txt) for more information.
# Troubleshooting
Before doing anything, make sure you have the latest version of this plugin and run `:checkhealth nvim-treesitter`.
It can also help to update the parsers via `:TSUpdate`.
#### Feature `X` does not work for `{language}`...
#### Feature `{X}` does not work for `{language}`...
First, check the `health#nvim_treesitter#check` and the `health#treesitter#check` sections of `:checkhealth` for any warning.
If there is one, it's highly likely that this is the cause of the problem.
1. Check the `nvim-treesitter` section of `:checkhealth` for any warning, and make sure that the query for `{X}` is listed for `{language}`.
Next check the `## Parser/Features` subsection of the `health#nvim_treesitter#check` section of `:checkhealth` to ensure the desired module is enabled for your language.
If not, you might be missing query files; see [Adding queries](#adding-queries).
2. Ensure that the feature is enabled as explained above.
Finally, ensure Neovim is correctly identifying your language's filetype using the `:echo &filetype` command while one of your language's files is open in Neovim.
If not, add a short Vimscript file to nvim-treesitter's `ftdetect` runtime directory following [Neovim's documentation](https://neovim.io/doc/user/filetype.html#new-filetype) on filetype detection.
You can also quickly & temporarily set the filetype for a single buffer with the `:set filetype=langname` command to test whether it fixes the problem.
If everything is okay, then it might be an actual error.
In that case, feel free to [open an issue here](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose).
#### I get `module 'vim.treesitter.query' not found`
Make sure you have the latest version of Neovim.
3. Ensure Neovim is correctly identifying your language's filetype using the `:echo &filetype` command while one of your language's files is open in Neovim.
#### I get `Error detected while processing .../plugin/nvim-treesitter.vim` every time I open Neovim
@@ -773,32 +195,10 @@ or due to an outdated parser.
- Make sure you have the parsers up to date with `:TSUpdate`
- Make sure you don't have more than one `parser` runtime directory.
You can execute this command `:echo nvim_get_runtime_file('parser', v:true)` to find all runtime directories.
You can execute this command `:= vim.api.nvim_get_runtime_file('parser', true)` to find all runtime directories.
If you get more than one path, remove the ones that are outside this plugin (`nvim-treesitter` directory),
so the correct version of the parser is used.
#### I experience weird highlighting issues similar to [#78](https://github.com/nvim-treesitter/nvim-treesitter/issues/78)
This is a well known issue, which arises when the tree and the buffer have gotten out of sync.
As this is an upstream issue, we don't have any definite fix.
To get around this, you can force reparsing the buffer with
```vim
:write | edit | TSBufEnable highlight
```
This will save, restore and enable highlighting for the current buffer.
#### I experience bugs when using `nvim-treesitter`'s `foldexpr` similar to [#194](https://github.com/nvim-treesitter/nvim-treesitter/issues/194)
This might happen, and is known to happen, with `vim-clap`.
To avoid these kind of errors, please use `setlocal` instead of `set` for the respective filetypes.
#### I run into errors like `module 'nvim-treesitter.configs' not found` at startup
This is because of `rtp` management in `nvim`, adding `packadd
nvim-treesitter` should fix the issue.
#### I want to use Git instead of curl for downloading the parsers
In your Lua config:
@@ -828,24 +228,12 @@ require("nvim-treesitter.install").prefer_git = true
In your Lua config:
```lua
for _, config in pairs(require("nvim-treesitter.parsers").get_parser_configs()) do
for _, config in pairs(require("nvim-treesitter.parsers").configs) do
config.install_info.url = config.install_info.url:gsub("https://github.com/", "something else")
end
require'nvim-treesitter.configs'.setup {
require'nvim-treesitter'.setup {
--
--
}
```
#### Using an existing parser for another filetype
For example, to use the `bash` tree-sitter to highlight file with
`filetype=apkbuild`, use:
```lua
vim.treesitter.language.register("bash", "apkbuild")
```
The `bash` tree-sitter must be installed following the usual procedure [as
described above](#language-parsers).

305
SUPPORTED_LANGUAGES.md Normal file
View File

@@ -0,0 +1,305 @@
# Supported languages
The following is a list of languages for which a parser can be installed through `:TSInstall`.
Legend:
- **Tier:** _core_, _stable_, _community_, or _unstable_
- **CLI:** `:TSInstall` requires `tree-sitter` CLI installed
- **NPM:** `:TSInstallFromGrammar` requires `node` installed
- **Queries** available for **H**ighlights, **I**ndents, **F**olds, In**J**ections
<!--This section of the README is automatically updated by a CI job-->
<!--parserinfo-->
Language | Tier | Queries | CLI | NPM | Maintainer
-------- |:----:|:-------:|:---:|:---:| ----------
[ada](https://github.com/briot/tree-sitter-ada) | | `HF  ` | | | @briot
[agda](https://github.com/tree-sitter/tree-sitter-agda) | | `HF  ` | | | @Decodetalkers
[angular](https://github.com/dlvandenberg/tree-sitter-angular) | unstable | `HFIJ` | | ✓ | @dlvandenberg
[apex](https://github.com/aheber/tree-sitter-sfapex) | community | `HF  ` | | | @aheber
[arduino](https://github.com/ObserverOfTime/tree-sitter-arduino) | | `HFIJ` | | | @ObserverOfTime
[asm](https://github.com/RubixDev/tree-sitter-asm) | community | `H  J` | | | @RubixDev
[astro](https://github.com/virchau13/tree-sitter-astro) | | `HFIJ` | | | @virchau13
[authzed](https://github.com/mleonidas/tree-sitter-authzed) | community | `H  J` | | | @mattpolzin
[awk](https://github.com/Beaglefoot/tree-sitter-awk) | | `H  J` | | |
[bash](https://github.com/tree-sitter/tree-sitter-bash) | | `HF J` | | | @TravonteD
[bass](https://github.com/vito/tree-sitter-bass) | | `HFIJ` | | | @amaanq
[beancount](https://github.com/polarmutex/tree-sitter-beancount) | | `HF J` | | | @polarmutex
[bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) | community | `HFI ` | | | @theHamsta, @clason
[bicep](https://github.com/amaanq/tree-sitter-bicep) | | `HFIJ` | | | @amaanq
[bitbake](https://github.com/amaanq/tree-sitter-bitbake) | stable | `HFIJ` | | | @amaanq
[blueprint](https://gitlab.com/gabmus/tree-sitter-blueprint.git) | unstable | `H   ` | | | @gabmus
[c](https://github.com/tree-sitter/tree-sitter-c) | core | `HFIJ` | | | @amaanq
[c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) | | `HF J` | | | @Luxed
[cairo](https://github.com/amaanq/tree-sitter-cairo) | stable | `HFIJ` | | | @amaanq
[capnp](https://github.com/amaanq/tree-sitter-capnp) | | `HFIJ` | | | @amaanq
[chatito](https://github.com/ObserverOfTime/tree-sitter-chatito) | | `HFIJ` | | | @ObserverOfTime
[clojure](https://github.com/sogaiu/tree-sitter-clojure) | | `HF J` | | | @NoahTheDuke
[cmake](https://github.com/uyha/tree-sitter-cmake) | | `HFI ` | | | @uyha
[comment](https://github.com/stsewd/tree-sitter-comment) | stable | `H   ` | | | @stsewd
[commonlisp](https://github.com/theHamsta/tree-sitter-commonlisp) | | `HF  ` | | ✓ | @theHamsta
[cooklang](https://github.com/addcninblue/tree-sitter-cooklang) | community | `H   ` | | | @addcninblue
[corn](https://github.com/jakestanger/tree-sitter-corn) | community | `HFI ` | | | @jakestanger
[cpon](https://github.com/amaanq/tree-sitter-cpon) | | `HFIJ` | | | @amaanq
[cpp](https://github.com/tree-sitter/tree-sitter-cpp) | | `HFIJ` | | ✓ | @theHamsta
[css](https://github.com/tree-sitter/tree-sitter-css) | | `HFIJ` | | | @TravonteD
[csv](https://github.com/amaanq/tree-sitter-csv) | stable | `H   ` | | | @amaanq
[cuda](https://github.com/theHamsta/tree-sitter-cuda) | | `HFIJ` | | ✓ | @theHamsta
[cue](https://github.com/eonpatapon/tree-sitter-cue) | | `HFIJ` | | | @amaanq
[d](https://github.com/gdamore/tree-sitter-d) | stable | `HFIJ` | | | @amaanq
[dart](https://github.com/UserNobody14/tree-sitter-dart) | | `HFIJ` | | | @akinsho
[devicetree](https://github.com/joelspadin/tree-sitter-devicetree) | | `HFIJ` | | | @jedrzejboczar
[dhall](https://github.com/jbellerb/tree-sitter-dhall) | | `HF J` | | | @amaanq
[diff](https://github.com/the-mikedavis/tree-sitter-diff) | | `H   ` | | | @gbprod
[disassembly](https://github.com/ColinKennedy/tree-sitter-disassembly) | community | `H  J` | | | @ColinKennedy
[djot](https://github.com/treeman/tree-sitter-djot) | community | `HFIJ` | | | @NoahTheDuke
[dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) | | `H  J` | | | @camdencheek
[dot](https://github.com/rydesun/tree-sitter-dot) | | `H IJ` | | | @rydesun
[doxygen](https://github.com/amaanq/tree-sitter-doxygen) | stable | `H IJ` | | | @amaanq
[dtd](https://github.com/tree-sitter-grammars/tree-sitter-xml) | stable | `HF J` | | | @ObserverOfTime
[earthfile](https://github.com/glehmann/tree-sitter-earthfile) | community | `H  J` | | | @glehmann
[ebnf](https://github.com/RubixDev/ebnf) | unstable | `H   ` | | | @RubixDev
[eds](https://github.com/uyha/tree-sitter-eds) | community | `HF  ` | | | @uyha
[eex](https://github.com/connorlay/tree-sitter-eex) | | `H  J` | | | @connorlay
[elixir](https://github.com/elixir-lang/tree-sitter-elixir) | | `HFIJ` | | | @connorlay
[elm](https://github.com/elm-tooling/tree-sitter-elm) | | `H  J` | | | @zweimach
[elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) | | `HFIJ` | | | @glapa-grossklag, @amaanq
[elvish](https://github.com/elves/tree-sitter-elvish) | | `H  J` | | | @elves
[embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template) | | `H  J` | | |
[erlang](https://github.com/WhatsApp/tree-sitter-erlang) | | `HF  ` | | | @filmor
[facility](https://github.com/FacilityApi/tree-sitter-facility) | community | `HFIJ` | | | @bryankenote
[faust](https://github.com/khiner/tree-sitter-faust) | community | `H  J` | | | @khiner
[fennel](https://github.com/alexmozaidze/tree-sitter-fennel) | | `HF J` | | ✓ | @alexmozaidze
[fidl](https://github.com/google/tree-sitter-fidl) | community | `HF J` | | | @chaopeng
[firrtl](https://github.com/amaanq/tree-sitter-firrtl) | | `HFIJ` | | | @amaanq
[fish](https://github.com/ram02z/tree-sitter-fish) | | `HFIJ` | | | @ram02z
[foam](https://github.com/FoamScience/tree-sitter-foam) | unstable | `HFIJ` | | | @FoamScience
[forth](https://github.com/AlexanderBrevig/tree-sitter-forth) | stable | `HFIJ` | | | @amaanq
[fortran](https://github.com/stadelmanma/tree-sitter-fortran) | | `HFI ` | | | @amaanq
[fsh](https://github.com/mgramigna/tree-sitter-fsh) | | `H   ` | | | @mgramigna
[func](https://github.com/amaanq/tree-sitter-func) | | `H   ` | | | @amaanq
[fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) | | `HFI ` | | | @jirgn
[gdscript](https://github.com/PrestonKnopp/tree-sitter-gdscript)[^gdscript] | | `HFIJ` | | | @PrestonKnopp
[gdshader](https://github.com/GodOfAvacyn/tree-sitter-gdshader) | community | `H  J` | | | @godofavacyn
[git_config](https://github.com/the-mikedavis/tree-sitter-git-config)[^git_config] | | `HF J` | | | @amaanq
[git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) | | `H  J` | | | @gbprod
[gitattributes](https://github.com/ObserverOfTime/tree-sitter-gitattributes) | | `H  J` | | | @ObserverOfTime
[gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) | | `H  J` | | | @gbprod
[gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) | | `H   ` | | | @theHamsta
[gleam](https://github.com/gleam-lang/tree-sitter-gleam) | | `HFIJ` | | | @amaanq
[glimmer](https://github.com/alexlafroscia/tree-sitter-glimmer)[^glimmer] | | `HFI ` | | | @NullVoxPopuli
[glsl](https://github.com/theHamsta/tree-sitter-glsl) | | `HFIJ` | | ✓ | @theHamsta
[gn](https://github.com/amaanq/tree-sitter-gn) | stable | `HFIJ` | | | @amaanq
[gnuplot](https://github.com/dpezto/tree-sitter-gnuplot) | community | `H  J` | | | @dpezto
[go](https://github.com/tree-sitter/tree-sitter-go) | | `HFIJ` | | | @theHamsta, @WinWisely268
[godot_resource](https://github.com/PrestonKnopp/tree-sitter-godot-resource)[^godot_resource] | | `HF J` | | | @pierpo
[gomod](https://github.com/camdencheek/tree-sitter-go-mod) | | `H  J` | | | @camdencheek
[gosum](https://github.com/amaanq/tree-sitter-go-sum) | | `H   ` | | | @amaanq
[gotmpl](https://github.com/ngalaiko/tree-sitter-go-template) | community | `H  J` | | | @qvalentin
[gowork](https://github.com/omertuc/tree-sitter-go-work) | | `H  J` | | | @omertuc
[gpg](https://github.com/ObserverOfTime/tree-sitter-gpg-config) | stable | `H  J` | | | @ObserverOfTime
[graphql](https://github.com/bkegley/tree-sitter-graphql) | | `H IJ` | | | @bkegley
[groovy](https://github.com/murtaza64/tree-sitter-groovy) | community | `HFIJ` | | | @murtaza64
[gstlaunch](https://github.com/theHamsta/tree-sitter-gstlaunch) | stable | `H   ` | | | @theHamsta
[hack](https://github.com/slackhq/tree-sitter-hack) | | `H   ` | | |
[hare](https://github.com/amaanq/tree-sitter-hare) | | `HFIJ` | | | @amaanq
[haskell](https://github.com/tree-sitter/tree-sitter-haskell) | | `HF J` | | | @mrcjkb
[haskell_persistent](https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent) | | `HF  ` | | | @lykahb
[hcl](https://github.com/MichaHoffmann/tree-sitter-hcl) | | `HFIJ` | | | @MichaHoffmann
[heex](https://github.com/connorlay/tree-sitter-heex) | | `HFIJ` | | | @connorlay
[helm](https://github.com/ngalaiko/tree-sitter-go-template) | community | `H  J` | | | @qvalentin
[hjson](https://github.com/winston0410/tree-sitter-hjson) | | `HFIJ` | | ✓ | @winston0410
[hlsl](https://github.com/theHamsta/tree-sitter-hlsl) | | `HFIJ` | | ✓ | @theHamsta
[hlsplaylist](https://github.com/Freed-Wu/tree-sitter-hlsplaylist) | community | `H  J` | | | @Freed-Wu
[hocon](https://github.com/antosha417/tree-sitter-hocon) | | `HF J` | | ✓ | @antosha417
[hoon](https://github.com/urbit-pilled/tree-sitter-hoon) | unstable | `HF  ` | | | @urbit-pilled
[html](https://github.com/tree-sitter/tree-sitter-html) | | `HFIJ` | | | @TravonteD
[htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) | unstable | `HFIJ` | | | @ObserverOfTime
[http](https://github.com/rest-nvim/tree-sitter-http) | | `H  J` | | | @amaanq, @NTBBloodbath
[hurl](https://github.com/pfeiferj/tree-sitter-hurl) | community | `HFIJ` | | | @pfeiferj
[hyprlang](https://github.com/luckasRanarison/tree-sitter-hyprlang) | community | `HFIJ` | | | @luckasRanarison
[idl](https://github.com/cathaysia/tree-sitter-idl) | community | `H  J` | | | @cathaysa
[ini](https://github.com/justinmk/tree-sitter-ini) | unstable | `HF  ` | | | @theHamsta
[inko](https://github.com/inko-lang/tree-sitter-inko) | community | `HFIJ` | | | @yorickpeterse
[ispc](https://github.com/fab4100/tree-sitter-ispc) | | `HFIJ` | | ✓ | @fab4100
[janet_simple](https://github.com/sogaiu/tree-sitter-janet-simple) | | `HF J` | | | @sogaiu
[java](https://github.com/tree-sitter/tree-sitter-java) | | `HFIJ` | | | @p00f
[javascript](https://github.com/tree-sitter/tree-sitter-javascript) | | `HFIJ` | | | @steelsojka
[jq](https://github.com/flurie/tree-sitter-jq) | | `H  J` | | | @ObserverOfTime
[jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) | | `H   ` | | | @steelsojka
[json](https://github.com/tree-sitter/tree-sitter-json) | | `HFI ` | | | @steelsojka
[json5](https://github.com/Joakker/tree-sitter-json5) | | `H  J` | | | @Joakker
[jsonc](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git)[^jsonc] | | `HFIJ` | | ✓ | @WhyNotHugo
[jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) | | `HF  ` | | | @nawordar
[julia](https://github.com/tree-sitter/tree-sitter-julia) | community | `HFIJ` | | | @theHamsta
[just](https://github.com/IndianBoy42/tree-sitter-just) | community | `HFIJ` | | | @Hubro
[kconfig](https://github.com/amaanq/tree-sitter-kconfig) | stable | `HFIJ` | | | @amaanq
[kdl](https://github.com/amaanq/tree-sitter-kdl) | | `HFIJ` | | | @amaanq
[kotlin](https://github.com/fwcd/tree-sitter-kotlin) | | `HF J` | | | @SalBakraa
[koto](https://github.com/koto-lang/tree-sitter-koto) | community | `HF J` | | | @irh
[kusto](https://github.com/Willem-J-an/tree-sitter-kusto) | community | `H  J` | | | @Willem-J-an
[lalrpop](https://github.com/traxys/tree-sitter-lalrpop) | | `H  J` | | | @traxys
[latex](https://github.com/latex-lsp/tree-sitter-latex) | community | `HF J` | ✓ | | @theHamsta, @clason
[ledger](https://github.com/cbarrete/tree-sitter-ledger) | | `HFIJ` | | | @cbarrete
[leo](https://github.com/r001/tree-sitter-leo) | community | `H IJ` | | | @r001
[linkerscript](https://github.com/amaanq/tree-sitter-linkerscript) | stable | `HFIJ` | | | @amaanq
[liquid](https://github.com/hankthetank27/tree-sitter-liquid) | community | `H  J` | | | @hankthetank27
[liquidsoap](https://github.com/savonet/tree-sitter-liquidsoap) | community | `HFI ` | | | @toots
[llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) | | `H   ` | | | @benwilliamgraham
[lua](https://github.com/MunifTanjim/tree-sitter-lua) | core | `HFIJ` | | | @muniftanjim
[luadoc](https://github.com/amaanq/tree-sitter-luadoc) | | `H   ` | | | @amaanq
[luap](https://github.com/amaanq/tree-sitter-luap)[^luap] | | `H   ` | | | @amaanq
[luau](https://github.com/amaanq/tree-sitter-luau) | | `HFIJ` | | | @amaanq
[m68k](https://github.com/grahambates/tree-sitter-m68k) | | `HF J` | | | @grahambates
[make](https://github.com/alemuller/tree-sitter-make) | | `HF J` | | | @lewis6991
[markdown](https://github.com/MDeiml/tree-sitter-markdown)[^markdown] | core | `HFIJ` | | | @MDeiml
[markdown_inline](https://github.com/MDeiml/tree-sitter-markdown)[^markdown_inline] | core | `H  J` | | | @MDeiml
[matlab](https://github.com/acristoffers/tree-sitter-matlab) | | `HFIJ` | | | @acristoffers
[menhir](https://github.com/Kerl13/tree-sitter-menhir) | | `H  J` | | | @Kerl13
[mermaid](https://github.com/monaqa/tree-sitter-mermaid) | unstable | `H   ` | | |
[meson](https://github.com/Decodetalkers/tree-sitter-meson) | | `HFIJ` | | | @Decodetalkers
[mlir](https://github.com/artagnon/tree-sitter-mlir) | unstable | `H   ` | ✓ | | @artagnon
[muttrc](https://github.com/neomutt/tree-sitter-muttrc) | community | `H  J` | | | @Freed-Wu
[nasm](https://github.com/naclsn/tree-sitter-nasm) | stable | `H  J` | | | @ObserverOfTime
[nickel](https://github.com/nickel-lang/tree-sitter-nickel) | | `H I ` | | |
[nim](https://github.com/alaviss/tree-sitter-nim) | community | `HF J` | | | @aMOPel
[nim_format_string](https://github.com/aMOPel/tree-sitter-nim-format-string) | community | `H  J` | | | @aMOPel
[ninja](https://github.com/alemuller/tree-sitter-ninja) | | `HFI ` | | | @alemuller
[nix](https://github.com/cstrahan/tree-sitter-nix) | | `HF J` | | | @leo60228
[norg](https://github.com/nvim-neorg/tree-sitter-norg) | unstable | `    ` | | | @JoeyGrajciar, @vhyrro
[nqc](https://github.com/amaanq/tree-sitter-nqc) | stable | `HFIJ` | | | @amaanq
[objc](https://github.com/amaanq/tree-sitter-objc) | | `HFIJ` | | | @amaanq
[objdump](https://github.com/ColinKennedy/tree-sitter-objdump) | community | `H  J` | | | @ColinKennedy
[ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) | | `HFIJ` | | | @undu
[ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) | | `HFIJ` | | | @undu
[ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) | | `H  J` | ✓ | | @undu
[odin](https://github.com/amaanq/tree-sitter-odin) | | `HFIJ` | | | @amaanq
[org](https://github.com/milisims/tree-sitter-org) | | `    ` | | |
[pascal](https://github.com/Isopod/tree-sitter-pascal.git) | | `HFIJ` | | | @Isopod
[passwd](https://github.com/ath3/tree-sitter-passwd) | | `H   ` | | | @amaanq
[pem](https://github.com/ObserverOfTime/tree-sitter-pem) | stable | `HF J` | | | @ObserverOfTime
[perl](https://github.com/tree-sitter-perl/tree-sitter-perl) | | `HF J` | | | @RabbiVeesh, @LeoNerd
[php](https://github.com/tree-sitter/tree-sitter-php)[^php] | | `HFIJ` | | | @tk-shirasaka
[php_only](https://github.com/tree-sitter/tree-sitter-php)[^php_only] | | `HFIJ` | | | @tk-shirasaka
[phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) | unstable | `H   ` | | ✓ | @mikehaertl
[pioasm](https://github.com/leo60228/tree-sitter-pioasm) | | `H  J` | | | @leo60228
[po](https://github.com/erasin/tree-sitter-po) | | `HF J` | | | @amaanq
[pod](https://github.com/tree-sitter-perl/tree-sitter-pod) | community | `H   ` | | | @RabbiVeesh, @LeoNerd
[poe_filter](https://github.com/ObserverOfTime/tree-sitter-poe-filter)[^poe_filter] | unstable | `HFIJ` | | | @ObserverOfTime
[pony](https://github.com/amaanq/tree-sitter-pony) | | `HFIJ` | | | @amaanq, @mfelsche
[printf](https://github.com/ObserverOfTime/tree-sitter-printf) | stable | `H   ` | | | @ObserverOfTime
[prisma](https://github.com/victorhqc/tree-sitter-prisma) | | `HF  ` | | | @elianiva
[promql](https://github.com/MichaHoffmann/tree-sitter-promql) | unstable | `H  J` | | | @MichaHoffmann
[properties](https://github.com/ObserverOfTime/tree-sitter-properties)[^properties] | stable | `H  J` | | | @ObserverOfTime
[proto](https://github.com/treywood/tree-sitter-proto) | | `HF  ` | | | @treywood
[prql](https://github.com/PRQL/tree-sitter-prql) | | `H  J` | | | @matthias-Q
[psv](https://github.com/amaanq/tree-sitter-csv) | stable | `H   ` | | | @amaanq
[pug](https://github.com/zealot128/tree-sitter-pug) | unstable | `H  J` | | | @zealot128
[puppet](https://github.com/amaanq/tree-sitter-puppet) | | `HFIJ` | | | @amaanq
[purescript](https://github.com/postsolar/tree-sitter-purescript) | community | `H  J` | | | @postsolar
[pymanifest](https://github.com/ObserverOfTime/tree-sitter-pymanifest) | stable | `H  J` | | | @ObserverOfTime
[python](https://github.com/tree-sitter/tree-sitter-python) | | `HFIJ` | | | @stsewd, @theHamsta
[ql](https://github.com/tree-sitter/tree-sitter-ql) | | `HFIJ` | | | @pwntester
[qmldir](https://github.com/Decodetalkers/tree-sitter-qmldir) | | `H  J` | | | @amaanq
[qmljs](https://github.com/yuja/tree-sitter-qmljs) | | `HF J` | | | @Decodetalkers
[query](https://github.com/nvim-treesitter/tree-sitter-query)[^query] | core | `HFIJ` | | | @steelsojka
[r](https://github.com/r-lib/tree-sitter-r) | | `H IJ` | | | @echasnovski
[racket](https://github.com/6cdh/tree-sitter-racket) | unstable | `HF J` | | |
[rasi](https://github.com/Fymyte/tree-sitter-rasi) | | `HFIJ` | | | @Fymyte
[rbs](https://github.com/joker1007/tree-sitter-rbs) | community | `HFIJ` | | | @joker1007
[re2c](https://github.com/amaanq/tree-sitter-re2c) | stable | `HFIJ` | | | @amaanq
[readline](https://github.com/ribru17/tree-sitter-readline) | community | `HFIJ` | | | @ribru17
[regex](https://github.com/tree-sitter/tree-sitter-regex) | | `H   ` | | | @theHamsta
[rego](https://github.com/FallenAngel97/tree-sitter-rego) | | `H  J` | | | @FallenAngel97
[requirements](https://github.com/ObserverOfTime/tree-sitter-requirements) | stable | `H  J` | | | @ObserverOfTime
[rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) | | `HF J` | | | @bamonroe
[robot](https://github.com/Hubro/tree-sitter-robot) | community | `HFI ` | | | @Hubro
[roc](https://github.com/nat-418/tree-sitter-roc) | community | `H  J` | | | @nat-418
[ron](https://github.com/amaanq/tree-sitter-ron) | | `HFIJ` | | | @amaanq
[rst](https://github.com/stsewd/tree-sitter-rst) | | `H  J` | | | @stsewd
[ruby](https://github.com/tree-sitter/tree-sitter-ruby) | | `HFIJ` | | | @TravonteD
[rust](https://github.com/tree-sitter/tree-sitter-rust) | | `HFIJ` | | | @amaanq
[scala](https://github.com/tree-sitter/tree-sitter-scala) | | `HF J` | | | @stevanmilic
[scfg](https://git.sr.ht/~rockorager/tree-sitter-scfg) | community | `H  J` | ✓ | | @WhyNotHugo
[scheme](https://github.com/6cdh/tree-sitter-scheme) | unstable | `HF J` | | |
[scss](https://github.com/serenadeai/tree-sitter-scss) | | `HFI ` | | | @elianiva
[slang](https://github.com/theHamsta/tree-sitter-slang)[^slang] | unstable | `HFIJ` | | ✓ | @theHamsta
[slint](https://github.com/slint-ui/tree-sitter-slint) | community | `HFIJ` | | | @hunger
[smali](https://github.com/tree-sitter-grammars/tree-sitter-smali) | community | `HFIJ` | | | @amaanq
[smithy](https://github.com/indoorvivants/tree-sitter-smithy) | | `H   ` | | | @amaanq, @keynmol
[snakemake](https://github.com/osthomas/tree-sitter-snakemake) | unstable | `HFIJ` | | |
[solidity](https://github.com/JoranHonig/tree-sitter-solidity) | | `HF  ` | | | @amaanq
[soql](https://github.com/aheber/tree-sitter-sfapex) | community | `H   ` | | | @aheber
[sosl](https://github.com/aheber/tree-sitter-sfapex) | | `H   ` | | | @aheber
[sourcepawn](https://github.com/nilshelmig/tree-sitter-sourcepawn) | community | `H  J` | | | @Sarrus1
[sparql](https://github.com/BonaBeavis/tree-sitter-sparql) | | `HFIJ` | | | @BonaBeavis
[sql](https://github.com/derekstride/tree-sitter-sql) | | `H IJ` | | | @derekstride
[squirrel](https://github.com/amaanq/tree-sitter-squirrel) | | `HFIJ` | | | @amaanq
[ssh_config](https://github.com/ObserverOfTime/tree-sitter-ssh-config) | stable | `HFIJ` | | | @ObserverOfTime
[starlark](https://github.com/amaanq/tree-sitter-starlark) | | `HFIJ` | | | @amaanq
[strace](https://github.com/sigmaSd/tree-sitter-strace) | stable | `H  J` | | | @amaanq
[styled](https://github.com/mskelton/tree-sitter-styled) | community | `HFIJ` | | | @mskelton
[supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) | | `HFIJ` | | | @madskjeldgaard
[surface](https://github.com/connorlay/tree-sitter-surface) | | `HFIJ` | | | @connorlay
[svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) | stable | `HFIJ` | | | @amaanq
[swift](https://github.com/alex-pinkus/tree-sitter-swift) | | `H I ` | ✓ | | @alex-pinkus
[sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) | | `HF J` | | | @RaafatTurki
[systemtap](https://github.com/ok-ryoko/tree-sitter-systemtap) | community | `HF J` | | | @ok-ryoko
[t32](https://gitlab.com/xasc/tree-sitter-t32.git) | community | `HFIJ` | | | @xasc
[tablegen](https://github.com/amaanq/tree-sitter-tablegen) | | `HFIJ` | | | @amaanq
[tcl](https://github.com/tree-sitter-grammars/tree-sitter-tcl) | stable | `HFI ` | | | @lewis6991
[teal](https://github.com/euclidianAce/tree-sitter-teal) | | `HFIJ` | ✓ | | @euclidianAce
[templ](https://github.com/vrischmann/tree-sitter-templ) | community | `H  J` | | | @vrischmann
[terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) | | `HFIJ` | | | @MichaHoffmann
[textproto](https://github.com/PorterAtGoogle/tree-sitter-textproto) | community | `HFI ` | | | @Porter
[thrift](https://github.com/duskmoon314/tree-sitter-thrift) | | `HFIJ` | | | @amaanq, @duskmoon314
[tiger](https://github.com/ambroisie/tree-sitter-tiger) | | `HFIJ` | | | @ambroisie
[tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) | | `HF J` | | | @ahelwer, @susliko
[tmux](https://github.com/Freed-Wu/tree-sitter-tmux) | community | `H  J` | | | @Freed-Wu
[todotxt](https://github.com/arnarg/tree-sitter-todotxt.git) | unstable | `H   ` | | | @arnarg
[toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) | | `HFIJ` | | ✓ | @tk-shirasaka
[tsv](https://github.com/amaanq/tree-sitter-csv) | stable | `H   ` | | | @amaanq
[tsx](https://github.com/tree-sitter/tree-sitter-typescript) | | `HFIJ` | | ✓ | @steelsojka
[turtle](https://github.com/BonaBeavis/tree-sitter-turtle) | | `HFIJ` | | | @BonaBeavis
[twig](https://github.com/gbprod/tree-sitter-twig) | | `H  J` | | | @gbprod
[typescript](https://github.com/tree-sitter/tree-sitter-typescript) | | `HFIJ` | | ✓ | @steelsojka
[typoscript](https://github.com/Teddytrombone/tree-sitter-typoscript) | community | `HFIJ` | | | @Teddytrombone
[typst](https://github.com/uben0/tree-sitter-typst) | community | `HFIJ` | | | @uben0, @RaafatTurki
[udev](https://github.com/ObserverOfTime/tree-sitter-udev) | stable | `H  J` | | | @ObserverOfTime
[ungrammar](https://github.com/Philipp-M/tree-sitter-ungrammar) | | `HFIJ` | | | @Philipp-M, @amaanq
[unison](https://github.com/kylegoetz/tree-sitter-unison) | unstable | `H  J` | ✓ | | @tapegram
[usd](https://github.com/ColinKennedy/tree-sitter-usd) | | `HFI ` | | | @ColinKennedy
[uxntal](https://github.com/amaanq/tree-sitter-uxntal) | | `HFIJ` | | | @amaanq
[v](https://github.com/vlang/v-analyzer) | | `HFIJ` | | | @kkharji, @amaanq
[vala](https://github.com/vala-lang/tree-sitter-vala) | | `HF  ` | | | @Prince781
[vento](https://github.com/ventojs/tree-sitter-vento) | community | `H  J` | | | @wrapperup, @oscarotero
[verilog](https://github.com/tree-sitter/tree-sitter-verilog) | | `HF J` | | | @zegervdv
[vhs](https://github.com/charmbracelet/tree-sitter-vhs) | | `H   ` | | | @caarlos0
[vim](https://github.com/neovim/tree-sitter-vim) | core | `HF J` | | | @clason
[vimdoc](https://github.com/neovim/tree-sitter-vimdoc) | core | `H  J` | | | @clason
[vue](https://github.com/tree-sitter-grammars/tree-sitter-vue) | stable | `HFIJ` | | | @WhyNotHugo, @lucario387
[wgsl](https://github.com/szebniok/tree-sitter-wgsl) | | `HFI ` | | | @szebniok
[wgsl_bevy](https://github.com/theHamsta/tree-sitter-wgsl-bevy) | | `HFI ` | | ✓ | @theHamsta
[wing](https://github.com/winglang/tree-sitter-wing) | community | `HF  ` | | | @gshpychka, @MarkMcCulloh
[wit](https://github.com/liamwh/tree-sitter-wit) | community | `H  J` | | | @liamwh
[xcompose](https://github.com/ObserverOfTime/tree-sitter-xcompose) | stable | `H  J` | | | @ObserverOfTime
[xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) | stable | `HFIJ` | | | @ObserverOfTime
[yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml) | stable | `HFIJ` | | | @amaanq
[yang](https://github.com/Hubro/tree-sitter-yang) | | `HFIJ` | | | @Hubro
[yuck](https://github.com/Philipp-M/tree-sitter-yuck) | | `HFIJ` | | | @Philipp-M, @amaanq
[zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) | community | `H  J` | | | @Freed-Wu
[zig](https://github.com/maxxnino/tree-sitter-zig) | | `HFIJ` | | | @maxxnino
[^gdscript]: Godot
[^git_config]: git_config
[^glimmer]: Glimmer and Ember
[^godot_resource]: Godot Resources
[^jsonc]: JSON with comments
[^luap]: Lua patterns
[^markdown]: basic highlighting
[^markdown_inline]: needed for full highlighting
[^php]: PHP with embedded HTML
[^php_only]: PHP without embedded HTML
[^poe_filter]: Path of Exile item filter
[^properties]: Java properties files
[^query]: Tree-sitter query language
[^slang]: Shader Slang
<!--parserinfo-->

34
TODO.md Normal file
View File

@@ -0,0 +1,34 @@
# Roadmap
This document lists the planned and finished changes in this rewrite towards [Nvim-treesitter 1.0](https://github.com/nvim-treesitter/nvim-treesitter/issues/4767).
## TODO
- [ ] **`query_predicates.lua`:** upstream/remove
- [ ] **`parsers.lua`:** modularize?
- [ ] **`parsers.lua`:** assign tiers
- [ ] **`install.lua`:** fix messages, add sync support (@lewis6991)
- [ ] **`install.lua`:** simplify compilation:
- hardcode one compiler + args per platform
- provide `install.compile_command` for overriding (function that takes files, ...?)
- allow using repo makefile (norg!)?
- ...or switch to makefile completely?
- [ ] **`locals.lua`:** refactor, move to `nvim-treesitter-refactor`
- [ ] **update-lockfile:** allow specifying version in addition to commit hash (for Tier 1)
- [ ] **update-lockfile:** one commit per parser/tier?
- [ ] **documentation:** consolidate, autogenerate?
- [ ] **documentation:** migration guide
- [ ] **textobjects:** include simple(!) function, queries? (check Helix)
- [ ] **downstream:** adapt to breaking changes (`nvim-treesitter-textobjects`, `nvim-treesitter-refactor`)
## DONE
- [X] remove module framework
- [X] remove extra utilities
- [X] refactor `indent.lua` into standalone
- [X] refactor commands, predicates, filetypes registration to plugin/
- [X] support installing tiers of parsers
- [X] install parsers to standard directory by default
- [X] remove bundled queries from runtimepath; copy on parser install
- [X] general refactor and cleanup
- [X] remove locals from highlighting (cf. https://github.com/nvim-treesitter/nvim-treesitter/issues/3944#issuecomment-1458782497)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -1,27 +0,0 @@
function! nvim_treesitter#statusline(...) abort
return luaeval("require'nvim-treesitter.statusline'.statusline(_A)", get(a:, 1, {}))
endfunction
function! nvim_treesitter#foldexpr() abort
return luaeval(printf('require"nvim-treesitter.fold".get_fold_indic(%d)', v:lnum))
endfunction
function! nvim_treesitter#installable_parsers(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.parsers'.available_parsers()") + ['all'], "\n")
endfunction
function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all'], "\n")
endfunction
function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n")
endfunction
function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n")
endfunction
function! nvim_treesitter#indent() abort
return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum))
endfunction

View File

@@ -1,6 +1,4 @@
*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim.
Minimum version of neovim: nightly
*nvim-treesitter.txt* Treesitter parser and query installer for Neovim
Authors:
Kiyan Yazdani <yazdani.kiyan@protonmail.com>
@@ -15,38 +13,37 @@ Authors:
==============================================================================
INTRODUCTION *nvim-treesitter-intro*
nvim-treesitter wraps the Neovim treesitter API to provide functionalities
such as highlighting and incremental selection, and a command to easily
install parsers.
Nvim-treesitter provides functionalities for managing treesitter parsers and
compatible queries for core features (highlighting, injections, fold, indent).
WARNING: This is work in progress and requires the latest commit on Neovim
`master`.
==============================================================================
QUICK START *nvim-treesitter-quickstart*
Install the parser for your language
>
>vim
:TSInstall {language}
<
To get a list of supported languages
>
>vim
:TSInstallInfo
<
By default, everything is disabled.
To enable supported features, put this in your `init.lua` file:
To install supported parsers and queries, put this in your `init.lua` file:
>
>lua
require'nvim-treesitter.configs'.setup {
-- A directory to install the parsers into.
-- If this is excluded or nil parsers are installed
-- to either the package dir, or the "site" dir.
-- If a custom path is used (not nil) it must be added to the runtimepath.
parser_install_dir = "/some/path/to/store/parsers",
-- Defaults to the `stdpath('data')/site` dir.
install_dir = "/some/path/to/store/parsers",
-- A list of parser names, or "all"
ensure_installed = { "c", "lua", "rust" },
-- A list of parser names, or "core", "stable", "community", "unstable"
ensure_install = { "core", "rust" },
-- Install parsers synchronously (only applied to `ensure_installed`)
sync_install = false,
@@ -54,174 +51,66 @@ To enable supported features, put this in your `init.lua` file:
-- Automatically install missing parsers when entering buffer
auto_install = false,
-- List of parsers to ignore installing (for "all")
-- List of parsers to ignore installing (for "core" etc.)
ignore_install = { "javascript" },
highlight = {
-- `false` will disable the whole extension
enable = true,
-- list of language that will be disabled
disable = { "c", "rust" },
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false,
},
}
vim.opt.runtimepath:append("/some/path/to/store/parsers")
<
See |nvim-treesitter-modules| for a list of all available modules and its options.
See |nvim-treesitter-commands| for a list of all available commands.
==============================================================================
MODULES *nvim-treesitter-modules*
COMMANDS *nvim-treesitter-commands*
|nvim-treesitter| provides several functionalities via modules (and submodules),
each module makes use of the query files defined for each language,
*:TSInstall*
:TSInstall {language} ... ~
All modules are disabled by default, and some provide default keymaps.
Each module corresponds to an entry in the dictionary passed to the
`nvim-treesitter.configs.setup` function, this should be in your `init.lua` file.
Install one or more treesitter parsers.
You can use |:TSInstall| `all` to install all parsers. Use |:TSInstall!| to
force the reinstallation of already installed parsers.
*:TSInstallSync*
:TSInstallSync {language} ... ~
>
require'nvim-treesitter.configs'.setup {
-- Modules and its options go here
highlight = { enable = true },
incremental_selection = { enable = true },
textobjects = { enable = true },
}
<
Perform the |:TSInstall| operation synchronously.
All modules share some common options, like `enable` and `disable`.
When `enable` is `true` this will enable the module for all supported languages,
if you want to disable the module for some languages you can pass a list to the `disable` option.
*:TSInstallInfo*
:TSInstallInfo ~
>
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
disable = { "cpp", "lua" },
},
}
<
List information about currently installed parsers
For more fine-grained control, `disable` can also take a function and
whenever it returns `true`, the module is disabled for that buffer.
The function is called once when a module starts in a buffer and receives the
language and buffer number as arguments:
*:TSUpdate*
:TSUpdate {language} ... ~
>
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
disable = function(lang, bufnr) -- Disable in large C++ buffers
return lang == "cpp" and vim.api.nvim_buf_line_count(bufnr) > 50000
end,
},
}
<
Update the installed parser for one more {language} or all installed parsers
if {language} is omitted. The specified parser is installed if it is not already
installed.
Options that define or accept a keymap use the same format you use to define
keymaps in Neovim, so you can write keymaps as `gd`, `<space>a`, `<leader>a`
`<C-a>` (control + a), `<A-n>` (alt + n), `<CR>` (enter), etc.
*:TSUpdateSync*
:TSUpdateSync {language} ... ~
External plugins can provide their own modules with their own options,
those can also be configured using the `nvim-treesitter.configs.setup`
function.
Perform the |:TSUpdate| operation synchronously.
------------------------------------------------------------------------------
HIGHLIGHT *nvim-treesitter-highlight-mod*
*:TSUninstall*
:TSUninstall {language} ... ~
Consistent syntax highlighting.
Deletes the parser for one or more {language}. You can use 'all' for language
to uninstall all parsers.
Query files: `highlights.scm`.
Supported options:
- enable: `true` or `false`.
- disable: list of languages.
- additional_vim_regex_highlighting: `true` or `false`, or a list of languages.
Set this to `true` if you depend on 'syntax' being enabled
(like for indentation). Using this option may slow down your editor,
and you may see some duplicate highlights.
Defaults to `false`.
>
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
custom_captures = {
-- Highlight the @foo.bar capture group with the "Identifier" highlight group.
["foo.bar"] = "Identifier",
},
-- Setting this to true or a list of languages will run `:h syntax` and tree-sitter at the same time.
additional_vim_regex_highlighting = false,
},
}
<
You can also set custom highlight captures
>
lua <<EOF
require"nvim-treesitter.highlight".set_custom_captures {
-- Highlight the @foo.bar capture group with the "Identifier" highlight group.
["foo.bar"] = "Identifier",
}
EOF
<
Note: The api is not stable yet.
------------------------------------------------------------------------------
INCREMENTAL SELECTION *nvim-treesitter-incremental-selection-mod*
Incremental selection based on the named nodes from the grammar.
Query files: `locals.scm`.
Supported options:
- enable: `true` or `false`.
- disable: list of languages.
- keymaps:
- init_selection: in normal mode, start incremental selection.
Defaults to `gnn`.
- node_incremental: in visual mode, increment to the upper named parent.
Defaults to `grn`.
- scope_incremental: in visual mode, increment to the upper scope
(as defined in `locals.scm`). Defaults to `grc`.
- node_decremental: in visual mode, decrement to the previous named node.
Defaults to `grm`.
>
require'nvim-treesitter.configs'.setup {
incremental_selection = {
enable = true,
keymaps = {
init_selection = "gnn",
node_incremental = "grn",
scope_incremental = "grc",
node_decremental = "grm",
},
},
}
<
------------------------------------------------------------------------------
INDENTATION *nvim-treesitter-indentation-mod*
==============================================================================
INDENTATION *nvim-treesitter-indentation*
Indentation based on treesitter for the |=| operator.
NOTE: this is an experimental feature.
NOTE: this is an experimental feature and will be upstreamed to Neovim when
stable.
To enable it for a supported parser, add the following to a corresponding
`FileType` autocommand or `ftplugin/<lang>.lua`: >lua
vim.bo.indentexpr = 'v.lua:require'nvim-treesitter'.indentexpr()'
<
Indentation for a language is controlled by `indents.scm` queries. The
following captures are supported:
Query files: `indents.scm`.
Supported options:
- enable: `true` or `false`.
- disable: list of languages.
>
require'nvim-treesitter.configs'.setup {
indent = {
enable = true
},
}
`@indent` *nvim-treesitter-indentation-queries*
Queries can use the following captures: `@indent.begin` and `@indent.dedent`,
@@ -233,7 +122,7 @@ the indentation to 0.
The `@indent.begin` specifies that the next line should be indented. Multiple
indents on the same line get collapsed. Eg.
>
>query
(
(if_statement)
(ERROR "else") @indent.begin
@@ -244,13 +133,13 @@ permits the next line to indent even when the block intended to be indented
has no content yet, improving interactive typing.
eg for python:
>
>query
((if_statement) @indent.begin
(#set! indent.immediate 1))
<
Will allow:
>
>python
if True:<CR>
# Auto indent to here
@@ -291,7 +180,7 @@ and finally
<
To specify the delimiters to use `indent.open_delimiter` and
`indent.close_delimiter` should be used. Eg.
>
>query
((argument_list) @indent.align
(#set! indent.open_delimiter "(")
(#set! indent.close_delimiter ")"))
@@ -302,13 +191,13 @@ the same indent as the natural next line.
For example in python:
>
>python
if (a > b and
c < d):
pass
Is not correct, whereas
>
>python
if (a > b and
c < d):
pass
@@ -316,7 +205,7 @@ Is not correct, whereas
Would be correctly indented. This behavior may be chosen using
`indent.avoid_last_matching_next`. Eg.
>
>query
(if_statement
condition: (parenthesized_expression) @indent.align
(#set! indent.open_delimiter "(")
@@ -328,263 +217,4 @@ Could be used to specify that the last line of an `@indent.align` capture
should be additionally indented to avoid clashing with the indent of the first
line of the block inside an if.
==============================================================================
COMMANDS *nvim-treesitter-commands*
*:TSInstall*
:TSInstall {language} ...~
Install one or more treesitter parsers.
You can use |:TSInstall| `all` to install all parsers. Use |:TSInstall!| to
force the reinstallation of already installed parsers.
*:TSInstallSync*
:TSInstallSync {language} ...~
Perform the |:TSInstall| operation synchronously.
*:TSInstallInfo*
:TSInstallInfo~
List information about currently installed parsers
*:TSUpdate*
:TSUpdate {language} ...~
Update the installed parser for one more {language} or all installed parsers
if {language} is omitted. The specified parser is installed if it is not already
installed.
*:TSUpdateSync*
:TSUpdateSync {language} ...~
Perform the |:TSUpdate| operation synchronously.
*:TSUninstall*
:TSUninstall {language} ...~
Deletes the parser for one or more {language}. You can use 'all' for language
to uninstall all parsers.
*:TSBufEnable*
:TSBufEnable {module}~
Enable {module} on the current buffer.
A list of modules can be found at |:TSModuleInfo|
*:TSBufDisable*
:TSBufDisable {module}~
Disable {module} on the current buffer.
A list of modules can be found at |:TSModuleInfo|
*:TSBufToggle*
:TSBufToggle {module}~
Toggle (enable if disabled, disable if enabled) {module} on the current
buffer.
A list of modules can be found at |:TSModuleInfo|
*:TSEnable*
:TSEnable {module} [{language}]~
Enable {module} for the session.
If {language} is specified, enable module for the session only for this
particular language.
A list of modules can be found at |:TSModuleInfo|
A list of languages can be found at |:TSInstallInfo|
*:TSDisable*
:TSDisable {module} [{language}]~
Disable {module} for the session.
If {language} is specified, disable module for the session only for this
particular language.
A list of modules can be found at |:TSModuleInfo|
A list of languages can be found at |:TSInstallInfo|
*:TSToggle*
:TSToggle {module} [{language}]~
Toggle (enable if disabled, disable if enabled) {module} for the session.
If {language} is specified, toggle module for the session only for this
particular language.
A list of modules can be found at |:TSModuleInfo|
A list of languages can be found at |:TSInstallInfo|
*:TSModuleInfo*
:TSModuleInfo [{module}]~
List the state for the given module or all modules for the current session in
a new buffer.
These highlight groups are used by default:
>
highlight default TSModuleInfoGood guifg=LightGreen gui=bold
highlight default TSModuleInfoBad guifg=Crimson
highlight default link TSModuleInfoHeader Type
highlight default link TSModuleInfoNamespace Statement
highlight default link TSModuleInfoParser Identifier
<
*:TSEditQuery*
:TSEditQuery {query-group} [{lang}]~
Edit the query file for a {query-group} (e.g. highlights, locals) for given
{lang}. If there are multiple files, the user is prompted to select one of them.
If no such file exists, a buffer for a new file in the user's config directory
is created. If {lang} is not specified, the language of the current buffer
is used.
*:TSEditQueryUserAfter*
:TSEditQueryUserAfter {query-group} [{lang}]~
Same as |:TSEditQuery| but edits a file in the `after` directory of the
user's config directory. Useful to add custom extensions for the queries
provided by a plugin.
==============================================================================
UTILS *nvim-treesitter-utils*
Nvim treesitter has some wrapper functions that you can retrieve with:
>
local ts_utils = require 'nvim-treesitter.ts_utils'
<
Methods
*ts_utils.get_node_at_cursor*
get_node_at_cursor(winnr)~
`winnr` will be 0 if nil.
Returns the node under the cursor.
*ts_utils.is_parent*
is_parent(dest, source)~
Determines whether `dest` is a parent of `source`.
Returns a boolean.
*ts_utils.get_named_children*
get_named_children(node)~
Returns a table of named children of `node`.
*ts_utils.get_next_node*
get_next_node(node, allow_switch_parent, allow_next_parent)~
Returns the next node within the same parent.
If no node is found, returns `nil`.
If `allow_switch_parent` is true, it will allow switching parent
when the node is the last node.
If `allow_next_parent` is true, it will allow next parent if
the node is the last node and the next parent doesn't have children.
*ts_utils.get_previous_node*
get_previous_node(node, allow_switch_parents, allow_prev_parent)~
Returns the previous node within the same parent.
`allow_switch_parent` and `allow_prev_parent` follow the same rule
as |ts_utils.get_next_node| but if the node is the first node.
*ts_utils.goto_node*
goto_node(node, goto_end, avoid_set_jump)~
Sets cursor to the position of `node` in the current windows.
If `goto_end` is truthy, the cursor is set to the end the node range.
Setting `avoid_set_jump` to `true`, avoids setting the current cursor position
to the jump list.
*ts_utils.swap_nodes*
swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)~
Swaps the nodes or ranges.
set `cursor_to_second` to true to move the cursor to the second node
*ts_utils.memoize_by_buf_tick*
memoize_by_buf_tick(fn, options)~
Caches the return value for a function and returns the cache value if the tick
of the buffer has not changed from the previous.
`fn`: a function that takes any arguments
and returns a value to store.
`options?`: <table>
- `bufnr`: a function/value that extracts the bufnr from the given arguments.
- `key`: a function/value that extracts the cache key from the given arguments.
`returns`: a function to call with bufnr as argument to
retrieve the value from the cache
*ts_utils.node_to_lsp_range*
node_to_lsp_range(node)~
Get an lsp formatted range from a node range
*ts_utils.node_length*
node_length(node)~
Get the byte length of node range
*ts_utils.update_selection*
update_selection(buf, node)~
Set the selection to the node range
*ts_utils.highlight_range*
highlight_range(range, buf, hl_namespace, hl_group)~
Set a highlight that spans the given range
*ts_utils.highlight_node*
highlight_node(node, buf, hl_namespace, hl_group)~
Set a highlight that spans the given node's range
==============================================================================
FUNCTIONS *nvim-treesitter-functions*
*nvim_treesitter#statusline()*
nvim_treesitter#statusline(opts)~
Returns a string describing the current position in the file. This
could be used as a statusline indicator.
Default options (lua syntax):
>
{
indicator_size = 100,
type_patterns = {'class', 'function', 'method'},
transform_fn = function(line, _node) return line:gsub('%s*[%[%(%{]*%s*$', '') end,
separator = ' -> ',
allow_duplicates = false
}
<
- `indicator_size` - How long should the string be. If longer, it is cut from
the beginning.
- `type_patterns` - Which node type patterns to match.
- `transform_fn` - Function used to transform the single item in line. By
default it removes opening brackets and spaces from end. Takes two arguments:
the text of the line in question, and the corresponding treesitter node.
- `separator` - Separator between nodes.
- `allow_duplicates` - Whether or not to remove duplicate components.
*nvim_treesitter#foldexpr()*
nvim_treesitter#foldexpr()~
Functions to be used to determine the fold level at a given line number.
To use it: >
set foldmethod=expr
set foldexpr=nvim_treesitter#foldexpr()
<
This will respect your 'foldminlines' and 'foldnestmax' settings.
Note: This is highly experimental, and folding can break on some types of
edits. If you encounter such breakage, hitting `zx` should fix folding.
In any case, feel free to open an issue with the reproducing steps.
==============================================================================
PERFORMANCE *nvim-treesitter-performance*
`nvim-treesitter` checks the 'runtimepath' on startup in order to discover
available parsers and queries and index them. As a consequence, a very long
'runtimepath' might result in delayed startup times.
vim:tw=78:ts=8:expandtab:noet:ft=help:norl:

View File

@@ -1,22 +0,0 @@
local install = require "nvim-treesitter.install"
local utils = require "nvim-treesitter.utils"
local info = require "nvim-treesitter.info"
local configs = require "nvim-treesitter.configs"
local statusline = require "nvim-treesitter.statusline"
-- Registers all query predicates
require "nvim-treesitter.query_predicates"
local M = {}
function M.setup()
utils.setup_commands("install", install.commands)
utils.setup_commands("info", info.commands)
utils.setup_commands("configs", configs.commands)
configs.init()
end
M.define_modules = configs.define_modules
M.statusline = statusline.statusline
return M

View File

@@ -1,71 +0,0 @@
local api = vim.api
local M = {}
-- Creates a cache table for buffers keyed by a type name.
-- Cache entries attach to the buffer and cleanup entries
-- as buffers are detached.
function M.create_buffer_cache()
local cache = {}
---@type table<integer, table<string, any>>
local items = setmetatable({}, {
__index = function(tbl, key)
rawset(tbl, key, {})
return rawget(tbl, key)
end,
})
---@type table<integer, boolean>
local loaded_buffers = {}
---@param type_name string
---@param bufnr integer
---@param value any
function cache.set(type_name, bufnr, value)
if not loaded_buffers[bufnr] then
loaded_buffers[bufnr] = true
-- Clean up the cache if the buffer is detached
-- to avoid memory leaks
api.nvim_buf_attach(bufnr, false, {
on_detach = function()
cache.clear_buffer(bufnr)
loaded_buffers[bufnr] = nil
return true
end,
on_reload = function() end, -- this is needed to prevent on_detach being called on buffer reload
})
end
items[bufnr][type_name] = value
end
---@param type_name string
---@param bufnr integer
---@return any
function cache.get(type_name, bufnr)
return items[bufnr][type_name]
end
---@param type_name string
---@param bufnr integer
---@return boolean
function cache.has(type_name, bufnr)
return cache.get(type_name, bufnr) ~= nil
end
---@param type_name string
---@param bufnr integer
function cache.remove(type_name, bufnr)
items[bufnr][type_name] = nil
end
---@param bufnr integer
function cache.clear_buffer(bufnr)
items[bufnr] = nil
end
return cache
end
return M

View File

@@ -1,39 +0,0 @@
-- Shim module to address deprecations across nvim versions
local ts = vim.treesitter
local tsq = ts.query
local M = {}
function M.get_query_files(lang, query_group, is_included)
return (tsq.get_files or tsq.get_query_files)(lang, query_group, is_included)
end
function M.get_query(lang, query_name)
return (tsq.get or tsq.get_query)(lang, query_name)
end
function M.parse_query(lang, query)
return (tsq.parse or tsq.parse_query)(lang, query)
end
function M.get_range(node, source, metadata)
return (ts.get_range or tsq.get_range)(node, source, metadata)
end
function M.get_node_text(node, bufnr)
return (ts.get_node_text or tsq.get_node_text)(node, bufnr)
end
function M.require_language(lang, opts)
return (ts.language.add or ts.language.require_language)(lang, opts)
end
function M.flatten(t)
if vim.fn.has "nvim-0.11" == 1 then
return vim.iter(t):flatten():totable()
else
return vim.tbl_flatten(t)
end
end
return M

View File

@@ -0,0 +1,144 @@
local utils = require('nvim-treesitter.utils')
local M = {}
---@class TSConfig
---@field sync_install boolean
---@field auto_install boolean
---@field ensure_install string[]
---@field ignore_install string[]
---@field install_dir string
---@type TSConfig
local config = {
sync_install = false,
auto_install = false,
ensure_install = {},
ignore_install = {},
install_dir = utils.join_path(vim.fn.stdpath('data'), 'site'),
}
---Setup call for users to override configuration configurations.
---@param user_data TSConfig|nil user configuration table
function M.setup(user_data)
if user_data then
if user_data.install_dir then
user_data.install_dir = vim.fs.normalize(user_data.install_dir)
--TODO(clason): insert after/before site, or leave to user?
vim.opt.runtimepath:append(user_data.install_dir)
end
config = vim.tbl_deep_extend('force', config, user_data)
end
if config.auto_install then
vim.api.nvim_create_autocmd('FileType', {
callback = function(args)
local ft = vim.bo[args.buf].filetype
local lang = vim.treesitter.language.get_lang(ft) or ft
if
require('nvim-treesitter.parsers').configs[lang]
and not vim.list_contains(M.installed_parsers(), lang)
and not vim.list_contains(config.ignore_install, lang)
then
require('nvim-treesitter.install').install(lang)
end
end,
})
end
if #config.ensure_install > 0 then
local to_install = M.norm_languages(config.ensure_install, { ignored = true, installed = true })
if #to_install > 0 then
require('nvim-treesitter.install').install(to_install, {
with_sync = config.sync_install,
})
end
end
end
-- Returns the install path for parsers, parser info, and queries.
-- If the specified directory does not exist, it is created.
---@param dir_name string
---@return string
function M.get_install_dir(dir_name)
local dir = utils.join_path(config.install_dir, dir_name)
if not vim.loop.fs_stat(dir) then
local ok, error = pcall(vim.fn.mkdir, dir, 'p', '0755')
if not ok then
vim.notify(error, vim.log.levels.ERROR)
end
end
return dir
end
---@return string[]
function M.installed_parsers()
local install_dir = M.get_install_dir('parser')
local installed = {} --- @type string[]
for f in vim.fs.dir(install_dir) do
local lang = assert(f:match('(.*)%..*'))
installed[#installed + 1] = lang
end
return installed
end
---Normalize languages
---@param languages? string[]|string
---@param skip? table
---@return string[]
function M.norm_languages(languages, skip)
if not languages then
return {}
end
local parsers = require('nvim-treesitter.parsers')
-- Turn into table
if type(languages) == 'string' then
languages = { languages }
end
if vim.list_contains(languages, 'all') then
if skip and skip.missing then
return M.installed_parsers()
end
languages = parsers.get_available()
end
for i, tier in ipairs(parsers.tiers) do
if vim.list_contains(languages, tier) then
languages = vim.iter.filter(function(l)
return l ~= tier
end, languages)
vim.list_extend(languages, parsers.get_available(i))
end
end
if skip and skip.ignored then
local ignored = config.ignore_install
languages = vim.iter.filter(function(v)
return not vim.list_contains(ignored, v)
end, languages)
end
if skip and skip.installed then
local installed = M.installed_parsers()
languages = vim.iter.filter(function(v)
return not vim.list_contains(installed, v)
end, languages)
end
if skip and skip.missing then
local installed = M.installed_parsers()
languages = vim.iter.filter(function(v)
return vim.list_contains(installed, v)
end, languages)
end
return languages
end
return M

View File

@@ -1,616 +0,0 @@
local api = vim.api
local queries = require "nvim-treesitter.query"
local ts = require "nvim-treesitter.compat"
local parsers = require "nvim-treesitter.parsers"
local utils = require "nvim-treesitter.utils"
local caching = require "nvim-treesitter.caching"
local M = {}
---@class TSConfig
---@field modules {[string]:TSModule}
---@field sync_install boolean
---@field ensure_installed string[]|string
---@field ignore_install string[]
---@field auto_install boolean
---@field parser_install_dir string|nil
---@type TSConfig
local config = {
modules = {},
sync_install = false,
ensure_installed = {},
auto_install = false,
ignore_install = {},
parser_install_dir = nil,
}
-- List of modules that need to be setup on initialization.
---@type TSModule[][]
local queued_modules_defs = {}
-- Whether we've initialized the plugin yet.
local is_initialized = false
---@class TSModule
---@field module_path string
---@field enable boolean|string[]|function(string): boolean
---@field disable boolean|string[]|function(string): boolean
---@field keymaps table<string, string>
---@field is_supported function(string): boolean
---@field attach function(string)
---@field detach function(string)
---@field enabled_buffers table<integer, boolean>
---@field additional_vim_regex_highlighting boolean|string[]
---@type {[string]: TSModule}
local builtin_modules = {
highlight = {
module_path = "nvim-treesitter.highlight",
-- @deprecated: use `highlight.set_custom_captures` instead
custom_captures = {},
enable = false,
is_supported = function(lang)
return queries.has_highlights(lang)
end,
additional_vim_regex_highlighting = false,
},
incremental_selection = {
module_path = "nvim-treesitter.incremental_selection",
enable = false,
keymaps = {
init_selection = "gnn", -- set to `false` to disable one of the mappings
node_incremental = "grn",
scope_incremental = "grc",
node_decremental = "grm",
},
is_supported = function()
return true
end,
},
indent = {
module_path = "nvim-treesitter.indent",
enable = false,
is_supported = queries.has_indents,
},
}
local attached_buffers_by_module = caching.create_buffer_cache()
---Resolves a module by requiring the `module_path` or using the module definition.
---@param mod_name string
---@return TSModule|nil
local function resolve_module(mod_name)
local config_mod = M.get_module(mod_name)
if not config_mod then
return
end
if type(config_mod.attach) == "function" and type(config_mod.detach) == "function" then
return config_mod
elseif type(config_mod.module_path) == "string" then
return require(config_mod.module_path)
end
end
---Enables and attaches the module to a buffer for lang.
---@param mod string path to module
---@param bufnr integer|nil buffer number, defaults to current buffer
---@param lang string|nil language, defaults to current language
local function enable_module(mod, bufnr, lang)
local module = M.get_module(mod)
if not module then
return
end
bufnr = bufnr or api.nvim_get_current_buf()
lang = lang or parsers.get_buf_lang(bufnr)
if not module.enable then
if module.enabled_buffers then
module.enabled_buffers[bufnr] = true
else
module.enabled_buffers = { [bufnr] = true }
end
end
M.attach_module(mod, bufnr, lang)
end
---Enables autocomands for the module.
---After the module is loaded `loaded` will be set to true for the module.
---@param mod string path to module
local function enable_mod_conf_autocmd(mod)
local config_mod = M.get_module(mod)
if not config_mod or config_mod.loaded then
return
end
api.nvim_create_autocmd("FileType", {
group = api.nvim_create_augroup("NvimTreesitter-" .. mod, {}),
callback = function(args)
require("nvim-treesitter.configs").reattach_module(mod, args.buf)
end,
desc = "Reattach module",
})
config_mod.loaded = true
end
---Enables the module globally and for all current buffers.
---After enabled, `enable` will be set to true for the module.
---@param mod string path to module
local function enable_all(mod)
local config_mod = M.get_module(mod)
if not config_mod then
return
end
enable_mod_conf_autocmd(mod)
config_mod.enable = true
config_mod.enabled_buffers = nil
for _, bufnr in pairs(api.nvim_list_bufs()) do
enable_module(mod, bufnr)
end
end
---Disables and detaches the module for a buffer.
---@param mod string path to module
---@param bufnr integer buffer number, defaults to current buffer
local function disable_module(mod, bufnr)
local module = M.get_module(mod)
if not module then
return
end
bufnr = bufnr or api.nvim_get_current_buf()
if module.enabled_buffers then
module.enabled_buffers[bufnr] = false
end
M.detach_module(mod, bufnr)
end
---Disables autocomands for the module.
---After the module is unloaded `loaded` will be set to false for the module.
---@param mod string path to module
local function disable_mod_conf_autocmd(mod)
local config_mod = M.get_module(mod)
if not config_mod or not config_mod.loaded then
return
end
api.nvim_clear_autocmds { event = "FileType", group = "NvimTreesitter-" .. mod }
config_mod.loaded = false
end
---Disables the module globally and for all current buffers.
---After disabled, `enable` will be set to false for the module.
---@param mod string path to module
local function disable_all(mod)
local config_mod = M.get_module(mod)
if not config_mod then
return
end
config_mod.enabled_buffers = nil
disable_mod_conf_autocmd(mod)
config_mod.enable = false
for _, bufnr in pairs(api.nvim_list_bufs()) do
disable_module(mod, bufnr)
end
end
---Toggles a module for a buffer
---@param mod string path to module
---@param bufnr integer buffer number, defaults to current buffer
---@param lang string language, defaults to current language
local function toggle_module(mod, bufnr, lang)
bufnr = bufnr or api.nvim_get_current_buf()
lang = lang or parsers.get_buf_lang(bufnr)
if attached_buffers_by_module.has(mod, bufnr) then
disable_module(mod, bufnr)
else
enable_module(mod, bufnr, lang)
end
end
-- Toggles the module globally and for all current buffers.
-- @param mod path to module
local function toggle_all(mod)
local config_mod = M.get_module(mod)
if not config_mod then
return
end
if config_mod.enable then
disable_all(mod)
else
enable_all(mod)
end
end
---Recurses through all modules including submodules
---@param accumulator function called for each module
---@param root {[string]: TSModule}|nil root configuration table to start at
---@param path string|nil prefix path
local function recurse_modules(accumulator, root, path)
root = root or config.modules
for name, module in pairs(root) do
local new_path = path and (path .. "." .. name) or name
if M.is_module(module) then
accumulator(name, module, new_path, root)
elseif type(module) == "table" then
recurse_modules(accumulator, module, new_path)
end
end
end
-- Shows current configuration of all nvim-treesitter modules
---@param process_function function used as the `process` parameter
--- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess)
local function config_info(process_function)
process_function = process_function
or function(item, path)
if path[#path] == vim.inspect.METATABLE then
return
end
if path[#path] == "is_supported" then
return
end
return item
end
print(vim.inspect(config, { process = process_function }))
end
---@param query_group string
---@param lang string
function M.edit_query_file(query_group, lang)
lang = lang or parsers.get_buf_lang()
local files = ts.get_query_files(lang, query_group, true)
if #files == 0 then
utils.notify "No query file found! Creating a new one!"
M.edit_query_file_user_after(query_group, lang)
elseif #files == 1 then
vim.cmd(":edit " .. files[1])
else
vim.ui.select(files, { prompt = "Select a file:" }, function(file)
if file then
vim.cmd(":edit " .. file)
end
end)
end
end
---@param query_group string
---@param lang string
function M.edit_query_file_user_after(query_group, lang)
lang = lang or parsers.get_buf_lang()
local folder = utils.join_path(vim.fn.stdpath "config", "after", "queries", lang)
local file = utils.join_path(folder, query_group .. ".scm")
if vim.fn.isdirectory(folder) ~= 1 then
vim.ui.select({ "Yes", "No" }, { prompt = '"' .. folder .. '" does not exist. Create it?' }, function(choice)
if choice == "Yes" then
vim.fn.mkdir(folder, "p", "0755")
vim.cmd(":edit " .. file)
end
end)
else
vim.cmd(":edit " .. file)
end
end
M.commands = {
TSBufEnable = {
run = enable_module,
args = {
"-nargs=1",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSBufDisable = {
run = disable_module,
args = {
"-nargs=1",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSBufToggle = {
run = toggle_module,
args = {
"-nargs=1",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSEnable = {
run = enable_all,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSDisable = {
run = disable_all,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSToggle = {
run = toggle_all,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#available_modules",
},
},
TSConfigInfo = {
run = config_info,
args = {
"-nargs=0",
},
},
TSEditQuery = {
run = M.edit_query_file,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#available_query_groups",
},
},
TSEditQueryUserAfter = {
run = M.edit_query_file_user_after,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#available_query_groups",
},
},
}
---@param mod string module
---@param lang string the language of the buffer
---@param bufnr integer the buffer
function M.is_enabled(mod, lang, bufnr)
if not parsers.has_parser(lang) then
return false
end
local module_config = M.get_module(mod)
if not module_config then
return false
end
local buffer_enabled = module_config.enabled_buffers and module_config.enabled_buffers[bufnr]
local config_enabled = module_config.enable or buffer_enabled
if not config_enabled or not module_config.is_supported(lang) then
return false
end
local disable = module_config.disable
if type(disable) == "function" then
if disable(lang, bufnr) then
return false
end
elseif type(disable) == "table" then
-- Otherwise it's a list of languages
for _, parser in pairs(disable) do
if lang == parser then
return false
end
end
end
return true
end
---Setup call for users to override module configurations.
---@param user_data TSConfig module overrides
function M.setup(user_data)
config.modules = vim.tbl_deep_extend("force", config.modules, user_data)
config.ignore_install = user_data.ignore_install or {}
config.parser_install_dir = user_data.parser_install_dir or nil
if config.parser_install_dir then
config.parser_install_dir = vim.fn.expand(config.parser_install_dir, ":p")
end
config.auto_install = user_data.auto_install or false
if config.auto_install then
require("nvim-treesitter.install").setup_auto_install()
end
local ensure_installed = user_data.ensure_installed or {}
if #ensure_installed > 0 then
if user_data.sync_install then
require("nvim-treesitter.install").ensure_installed_sync(ensure_installed)
else
require("nvim-treesitter.install").ensure_installed(ensure_installed)
end
end
config.modules.ensure_installed = nil
config.ensure_installed = ensure_installed
recurse_modules(function(_, _, new_path)
local data = utils.get_at_path(config.modules, new_path)
if data.enable then
enable_all(new_path)
end
end, config.modules)
end
-- Defines a table of modules that can be attached/detached to buffers
-- based on language support. A module consist of the following properties:
---* @enable Whether the modules is enabled. Can be true or false.
---* @disable A list of languages to disable the module for. Only relevant if enable is true.
---* @keymaps A list of user mappings for a given module if relevant.
---* @is_supported A function which, given a ft, will return true if the ft works on the module.
---* @module_path A string path to a module file using `require`. The exported module must contain
--- an `attach` and `detach` function. This path is not required if `attach` and `detach`
--- functions are provided directly on the module definition.
---* @attach An attach function that is called for each buffer that the module is enabled for. This is required
--- if a `module_path` is not specified.
---* @detach A detach function that is called for each buffer that the module is enabled for. This is required
--- if a `module_path` is not specified.
--
-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order
-- and can be loaded lazily.
--
---* @example
---require"nvim-treesitter".define_modules {
--- my_cool_module = {
--- attach = function()
--- do_some_cool_setup()
--- end,
--- detach = function()
--- do_some_cool_teardown()
--- end
--- }
---}
---@param mod_defs TSModule[]
function M.define_modules(mod_defs)
if not is_initialized then
table.insert(queued_modules_defs, mod_defs)
return
end
recurse_modules(function(key, mod, _, group)
group[key] = vim.tbl_extend("keep", mod, {
enable = false,
disable = {},
is_supported = function()
return true
end,
})
end, mod_defs)
config.modules = vim.tbl_deep_extend("keep", config.modules, mod_defs)
for _, mod in ipairs(M.available_modules(mod_defs)) do
local module_config = M.get_module(mod)
if module_config and module_config.enable then
enable_mod_conf_autocmd(mod)
end
end
end
---Attaches a module to a buffer
---@param mod_name string the module name
---@param bufnr integer the buffer
---@param lang string the language of the buffer
function M.attach_module(mod_name, bufnr, lang)
bufnr = bufnr or api.nvim_get_current_buf()
lang = lang or parsers.get_buf_lang(bufnr)
local resolved_mod = resolve_module(mod_name)
if resolved_mod and not attached_buffers_by_module.has(mod_name, bufnr) and M.is_enabled(mod_name, lang, bufnr) then
attached_buffers_by_module.set(mod_name, bufnr, true)
resolved_mod.attach(bufnr, lang)
end
end
-- Detaches a module to a buffer
---@param mod_name string the module name
---@param bufnr integer the buffer
function M.detach_module(mod_name, bufnr)
local resolved_mod = resolve_module(mod_name)
bufnr = bufnr or api.nvim_get_current_buf()
if resolved_mod and attached_buffers_by_module.has(mod_name, bufnr) then
attached_buffers_by_module.remove(mod_name, bufnr)
resolved_mod.detach(bufnr)
end
end
-- Same as attach_module, but if the module is already attached, detach it first.
---@param mod_name string the module name
---@param bufnr integer the buffer
---@param lang string the language of the buffer
function M.reattach_module(mod_name, bufnr, lang)
M.detach_module(mod_name, bufnr)
M.attach_module(mod_name, bufnr, lang)
end
-- Gets available modules
---@param root {[string]:TSModule}|nil table to find modules
---@return string[] modules list of module paths
function M.available_modules(root)
local modules = {}
recurse_modules(function(_, _, path)
table.insert(modules, path)
end, root)
return modules
end
---Gets a module config by path
---@param mod_path string path to the module
---@return TSModule|nil: the module or nil
function M.get_module(mod_path)
local mod = utils.get_at_path(config.modules, mod_path)
return M.is_module(mod) and mod or nil
end
-- Determines whether the provided table is a module.
-- A module should contain an attach and detach function.
---@param mod table|nil the module table
---@return boolean
function M.is_module(mod)
return type(mod) == "table"
and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string")
end
-- Initializes built-in modules and any queued modules
-- registered by plugins or the user.
function M.init()
is_initialized = true
M.define_modules(builtin_modules)
for _, mod_def in ipairs(queued_modules_defs) do
M.define_modules(mod_def)
end
end
-- If parser_install_dir is not nil is used or created.
-- If parser_install_dir is nil try the package dir of the nvim-treesitter
-- plugin first, followed by the "site" dir from "runtimepath". "site" dir will
-- be created if it doesn't exist. Using only the package dir won't work when
-- the plugin is installed with Nix, since the "/nix/store" is read-only.
---@param folder_name string|nil
---@return string|nil, string|nil
function M.get_parser_install_dir(folder_name)
folder_name = folder_name or "parser"
local install_dir = config.parser_install_dir or utils.get_package_path()
local parser_dir = utils.join_path(install_dir, folder_name)
return utils.create_or_reuse_writable_dir(
parser_dir,
utils.join_space("Could not create parser dir '", parser_dir, "': "),
utils.join_space(
"Parser dir '",
parser_dir,
"' should be read/write (see README on how to configure an alternative install location)"
)
)
end
function M.get_parser_info_dir()
return M.get_parser_install_dir "parser-info"
end
function M.get_ignored_parser_installs()
return config.ignore_install or {}
end
function M.get_ensure_installed_parsers()
if type(config.ensure_installed) == "string" then
return { config.ensure_installed }
end
return config.ensure_installed or {}
end
return M

View File

@@ -1,123 +0,0 @@
local api = vim.api
local tsutils = require "nvim-treesitter.ts_utils"
local query = require "nvim-treesitter.query"
local parsers = require "nvim-treesitter.parsers"
local M = {}
-- This is cached on buf tick to avoid computing that multiple times
-- Especially not for every line in the file when `zx` is hit
local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local max_fold_level = api.nvim_win_get_option(0, "foldnestmax")
local trim_level = function(level)
if level > max_fold_level then
return max_fold_level
end
return level
end
local parser = parsers.get_parser(bufnr)
if not parser then
return {}
end
local matches = query.get_capture_matches_recursively(bufnr, function(lang)
if query.has_folds(lang) then
return "@fold", "folds"
elseif query.has_locals(lang) then
return "@scope", "locals"
end
end)
-- start..stop is an inclusive range
---@type table<number, number>
local start_counts = {}
---@type table<number, number>
local stop_counts = {}
local prev_start = -1
local prev_stop = -1
local min_fold_lines = api.nvim_win_get_option(0, "foldminlines")
for _, match in ipairs(matches) do
local start, stop, stop_col ---@type integer, integer, integer
if match.metadata and match.metadata.range then
start, _, stop, stop_col = unpack(match.metadata.range) ---@type integer, integer, integer, integer
else
start, _, stop, stop_col = match.node:range() ---@type integer, integer, integer, integer
end
if stop_col == 0 then
stop = stop - 1
end
local fold_length = stop - start + 1
local should_fold = fold_length > min_fold_lines
-- Fold only multiline nodes that are not exactly the same as previously met folds
-- Checking against just the previously found fold is sufficient if nodes
-- are returned in preorder or postorder when traversing tree
if should_fold and not (start == prev_start and stop == prev_stop) then
start_counts[start] = (start_counts[start] or 0) + 1
stop_counts[stop] = (stop_counts[stop] or 0) + 1
prev_start = start
prev_stop = stop
end
end
---@type string[]
local levels = {}
local current_level = 0
-- We now have the list of fold opening and closing, fill the gaps and mark where fold start
for lnum = 0, api.nvim_buf_line_count(bufnr) do
local prefix = ""
local last_trimmed_level = trim_level(current_level)
current_level = current_level + (start_counts[lnum] or 0)
local trimmed_level = trim_level(current_level)
current_level = current_level - (stop_counts[lnum] or 0)
local next_trimmed_level = trim_level(current_level)
-- Determine if it's the start/end of a fold
-- NB: vim's fold-expr interface does not have a mechanism to indicate that
-- two (or more) folds start at this line, so it cannot distinguish between
-- ( \n ( \n )) \n (( \n ) \n )
-- versus
-- ( \n ( \n ) \n ( \n ) \n )
-- If it did have such a mechanism, (trimmed_level - last_trimmed_level)
-- would be the correct number of starts to pass on.
if trimmed_level - last_trimmed_level > 0 then
prefix = ">"
elseif trimmed_level - next_trimmed_level > 0 then
-- Ending marks tend to confuse vim more than it helps, particularly when
-- the fold level changes by at least 2; we can uncomment this if
-- vim's behavior gets fixed.
-- prefix = "<"
prefix = ""
end
levels[lnum + 1] = prefix .. tostring(trimmed_level)
end
return levels
end)
---@param lnum integer
---@return string
function M.get_fold_indic(lnum)
if not parsers.has_parser() or not lnum then
return "0"
end
local buf = api.nvim_get_current_buf()
local levels = folds_levels(buf) or {}
return levels[lnum] or "0"
end
return M

View File

@@ -1,116 +1,119 @@
local api = vim.api
local fn = vim.fn
local queries = require "nvim-treesitter.query"
local info = require "nvim-treesitter.info"
local shell = require "nvim-treesitter.shell_command_selectors"
local install = require "nvim-treesitter.install"
local utils = require "nvim-treesitter.utils"
local ts = require "nvim-treesitter.compat"
local health = vim.health or require "health"
-- "report_" prefix has been deprecated, use the recommended replacements if they exist.
local _start = health.start or health.report_start
local _ok = health.ok or health.report_ok
local _warn = health.warn or health.report_warn
local _error = health.error or health.report_error
local shell = require('nvim-treesitter.shell_cmds')
local install = require('nvim-treesitter.install')
local config = require('nvim-treesitter.config')
local tsq = vim.treesitter.query
local M = {}
local NVIM_TREESITTER_MINIMUM_ABI = 13
local function install_health()
_start "Installation"
if fn.has "nvim-0.10" ~= 1 then
_error "Nvim-treesitter requires Nvim 0.10 or newer"
end
if fn.executable "tree-sitter" == 0 then
_warn(
"`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar,"
.. " not required for :TSInstall)"
)
else
_ok(
"`tree-sitter` found "
.. (utils.ts_cli_version() or "(unknown version)")
.. " (parser generator, only needed for :TSInstallFromGrammar)"
)
end
if fn.executable "node" == 0 then
_warn("`node` executable not found (only needed for :TSInstallFromGrammar," .. " not required for :TSInstall)")
else
local handle = io.popen "node --version"
local result = handle:read "*a"
---@return string|nil
local function ts_cli_version()
if vim.fn.executable('tree-sitter') == 1 then
local handle = io.popen('tree-sitter -V')
if not handle then
return
end
local result = handle:read('*a')
handle:close()
local version = vim.split(result, "\n")[1]
_ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)")
return vim.split(result, '\n')[1]:match('[^tree%psitter ].*')
end
end
local function install_health()
vim.health.start('Installation')
if vim.fn.has('nvim-0.10') ~= 1 then
vim.health.error('Nvim-treesitter requires Neovim Nightly')
end
if fn.executable "git" == 0 then
_error("`git` executable not found.", {
"Install it with your package manager.",
"Check that your `$PATH` is set correctly.",
if vim.fn.executable('tree-sitter') == 0 then
vim.health.warn(
'`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar,'
.. ' not required for :TSInstall)'
)
else
vim.health.ok(
'`tree-sitter` found '
.. (ts_cli_version() or '(unknown version)')
.. ' (parser generator, only needed for :TSInstallFromGrammar)'
)
end
if vim.fn.executable('node') == 0 then
vim.health.warn(
'`node` executable not found (only needed for :TSInstallFromGrammar,'
.. ' not required for :TSInstall)'
)
else
local handle = assert(io.popen('node --version'))
local result = handle:read('*a')
handle:close()
local version = vim.split(result, '\n')[1]
vim.health.ok('`node` found ' .. version .. ' (only needed for :TSInstallFromGrammar)')
end
if vim.fn.executable('git') == 0 then
vim.health.error('`git` executable not found.', {
'Install it with your package manager.',
'Check that your `$PATH` is set correctly.',
})
else
_ok "`git` executable found."
vim.health.ok('`git` executable found.')
end
local cc = shell.select_executable(install.compilers)
if not cc then
_error("`cc` executable not found.", {
"Check that any of "
vim.health.error('`cc` executable not found.', {
'Check that any of '
.. vim.inspect(install.compilers)
.. " is in your $PATH"
.. ' is in your $PATH'
.. ' or set the environment variable CC or `require"nvim-treesitter.install".compilers` explicitly!',
})
else
local version = vim.fn.systemlist(cc .. (cc == "cl" and "" or " --version"))[1]
_ok(
"`"
local version = vim.fn.systemlist(cc .. (cc == 'cl' and '' or ' --version'))[1]
vim.health.ok(
'`'
.. cc
.. "` executable found. Selected from "
.. '` executable found. Selected from '
.. vim.inspect(install.compilers)
.. (version and ("\nVersion: " .. version) or "")
.. (version and ('\nVersion: ' .. version) or '')
)
end
if vim.treesitter.language_version then
if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then
_ok(
"Neovim was compiled with tree-sitter runtime ABI version "
vim.health.ok(
'Neovim was compiled with tree-sitter runtime ABI version '
.. vim.treesitter.language_version
.. " (required >="
.. ' (required >='
.. NVIM_TREESITTER_MINIMUM_ABI
.. "). Parsers must be compatible with runtime ABI."
.. '). Parsers must be compatible with runtime ABI.'
)
else
_error(
"Neovim was compiled with tree-sitter runtime ABI version "
vim.health.error(
'Neovim was compiled with tree-sitter runtime ABI version '
.. vim.treesitter.language_version
.. ".\n"
.. "nvim-treesitter expects at least ABI version "
.. '.\n'
.. 'nvim-treesitter expects at least ABI version '
.. NVIM_TREESITTER_MINIMUM_ABI
.. "\n"
.. "Please make sure that Neovim is linked against are recent tree-sitter runtime when building"
.. " or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI."
.. '\n'
.. 'Please make sure that Neovim is linked against are recent tree-sitter runtime when building'
.. ' or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI.'
)
end
end
_start("OS Info:\n" .. vim.inspect(vim.loop.os_uname()))
vim.health.start('OS Info:\n' .. vim.inspect(vim.loop.os_uname()))
end
local function query_status(lang, query_group)
local ok, err = pcall(queries.get_query, lang, query_group)
local ok, err = pcall(tsq.get, lang, query_group)
if not ok then
return "x", err
return 'x', err
elseif not err then
return "."
return '.'
else
return ""
return ''
end
end
@@ -118,59 +121,53 @@ function M.check()
local error_collection = {}
-- Installation dependency checks
install_health()
queries.invalidate_query_cache()
-- Parser installation checks
local parser_installation = { "Parser/Features" .. string.rep(" ", 9) .. "H L F I J" }
for _, parser_name in pairs(info.installed_parsers()) do
local installed = #api.nvim_get_runtime_file("parser/" .. parser_name .. ".so", false)
-- Only append information about installed parsers
if installed >= 1 then
local multiple_parsers = installed > 1 and "+" or ""
local out = " - " .. parser_name .. multiple_parsers .. string.rep(" ", 20 - (#parser_name + #multiple_parsers))
for _, query_group in pairs(queries.built_in_query_groups) do
local status, err = query_status(parser_name, query_group)
out = out .. status .. " "
if err then
table.insert(error_collection, { parser_name, query_group, err })
end
local parser_installation = { 'Parser/Features' .. string.rep(' ', 9) .. 'H L F I J' }
for _, parser_name in pairs(config.installed_parsers()) do
local out = ' - ' .. parser_name .. string.rep(' ', 20 - #parser_name)
for _, query_group in pairs(M.bundled_queries) do
local status, err = query_status(parser_name, query_group)
out = out .. status .. ' '
if err then
table.insert(error_collection, { parser_name, query_group, err })
end
table.insert(parser_installation, vim.fn.trim(out, " ", 2))
end
table.insert(parser_installation, vim.fn.trim(out, ' ', 2))
end
local legend = [[
Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[j]ections
+) multiple parsers found, only one will be used
x) errors found in the query, try to run :TSUpdate {lang}]]
Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[J]ections
x) errors found in the query, try to run :TSUpdate {lang}]]
table.insert(parser_installation, legend)
-- Finally call the report function
_start(table.concat(parser_installation, "\n"))
vim.health.start(table.concat(parser_installation, '\n'))
if #error_collection > 0 then
_start "The following errors have been detected:"
vim.health.start('The following errors have been detected:')
for _, p in ipairs(error_collection) do
local lang, type, err = unpack(p)
local lines = {}
table.insert(lines, lang .. "(" .. type .. "): " .. err)
local files = ts.get_query_files(lang, type)
table.insert(lines, lang .. '(' .. type .. '): ' .. err)
local files = tsq.get_files(lang, type)
if #files > 0 then
table.insert(lines, lang .. "(" .. type .. ") is concatenated from the following files:")
table.insert(lines, lang .. '(' .. type .. ') is concatenated from the following files:')
for _, file in ipairs(files) do
local fd = io.open(file, "r")
local fd = io.open(file, 'r')
if fd then
local ok, file_err = pcall(ts.parse_query, lang, fd:read "*a")
local ok, file_err = pcall(tsq.parse, lang, fd:read('*a'))
if ok then
table.insert(lines, '| [OK]:"' .. file .. '"')
table.insert(lines, '| [OK]:"' .. file .. '"')
else
table.insert(lines, '| [ERROR]:"' .. file .. '", failed to load: ' .. file_err)
table.insert(lines, '| [ERR]:"' .. file .. '", failed to load: ' .. file_err)
end
fd:close()
end
end
end
_error(table.concat(lines, "\n"))
vim.health.error(table.concat(lines, '\n'))
end
end
end
M.bundled_queries = { 'highlights', 'locals', 'folds', 'indents', 'injections' }
return M

View File

@@ -1,49 +0,0 @@
local configs = require "nvim-treesitter.configs"
local M = {}
---@param config TSModule
---@param lang string
---@return boolean
local function should_enable_vim_regex(config, lang)
local additional_hl = config.additional_vim_regex_highlighting
local is_table = type(additional_hl) == "table"
---@diagnostic disable-next-line: param-type-mismatch
return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang))
end
---@param bufnr integer
---@param lang string
function M.attach(bufnr, lang)
local config = configs.get_module "highlight"
vim.treesitter.start(bufnr, lang)
if config and should_enable_vim_regex(config, lang) then
vim.bo[bufnr].syntax = "ON"
end
end
---@param bufnr integer
function M.detach(bufnr)
vim.treesitter.stop(bufnr)
end
---@deprecated
function M.start(...)
vim.notify(
"`nvim-treesitter.highlight.start` is deprecated: use `nvim-treesitter.highlight.attach` or `vim.treesitter.start`",
vim.log.levels.WARN
)
M.attach(...)
end
---@deprecated
function M.stop(...)
vim.notify(
"`nvim-treesitter.highlight.stop` is deprecated: use `nvim-treesitter.highlight.detach` or `vim.treesitter.stop`",
vim.log.levels.WARN
)
M.detach(...)
end
return M

View File

@@ -1,176 +0,0 @@
local api = vim.api
local configs = require "nvim-treesitter.configs"
local ts_utils = require "nvim-treesitter.ts_utils"
local locals = require "nvim-treesitter.locals"
local parsers = require "nvim-treesitter.parsers"
local queries = require "nvim-treesitter.query"
local utils = require "nvim-treesitter.utils"
local M = {}
---@type table<integer, table<TSNode|nil>>
local selections = {}
function M.init_selection()
local buf = api.nvim_get_current_buf()
parsers.get_parser():parse { vim.fn.line "w0" - 1, vim.fn.line "w$" }
local node = ts_utils.get_node_at_cursor()
selections[buf] = { [1] = node }
ts_utils.update_selection(buf, node)
end
-- Get the range of the current visual selection.
--
-- The range starts with 1 and the ending is inclusive.
---@return integer, integer, integer, integer
local function visual_selection_range()
local _, csrow, cscol, _ = unpack(vim.fn.getpos "v") ---@type integer, integer, integer, integer
local _, cerow, cecol, _ = unpack(vim.fn.getpos ".") ---@type integer, integer, integer, integer
local start_row, start_col, end_row, end_col ---@type integer, integer, integer, integer
if csrow < cerow or (csrow == cerow and cscol <= cecol) then
start_row = csrow
start_col = cscol
end_row = cerow
end_col = cecol
else
start_row = cerow
start_col = cecol
end_row = csrow
end_col = cscol
end
return start_row, start_col, end_row, end_col
end
---@param node TSNode
---@return boolean
local function range_matches(node)
local csrow, cscol, cerow, cecol = visual_selection_range()
local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() }
return srow == csrow and scol == cscol and erow == cerow and ecol == cecol
end
---@param get_parent fun(node: TSNode): TSNode|nil
---@return fun():nil
local function select_incremental(get_parent)
return function()
local buf = api.nvim_get_current_buf()
local nodes = selections[buf]
local csrow, cscol, cerow, cecol = visual_selection_range()
-- Initialize incremental selection with current selection
if not nodes or #nodes == 0 or not range_matches(nodes[#nodes]) then
local parser = parsers.get_parser()
parser:parse { vim.fn.line "w0" - 1, vim.fn.line "w$" }
local node = parser:named_node_for_range(
{ csrow - 1, cscol - 1, cerow - 1, cecol },
{ ignore_injections = false }
)
ts_utils.update_selection(buf, node)
if nodes and #nodes > 0 then
table.insert(selections[buf], node)
else
selections[buf] = { [1] = node }
end
return
end
-- Find a node that changes the current selection.
local node = nodes[#nodes] ---@type TSNode
while true do
local parent = get_parent(node)
if not parent or parent == node then
-- Keep searching in the parent tree
local root_parser = parsers.get_parser()
root_parser:parse { vim.fn.line "w0" - 1, vim.fn.line "w$" }
local current_parser = root_parser:language_for_range { csrow - 1, cscol - 1, cerow - 1, cecol }
if root_parser == current_parser then
node = root_parser:named_node_for_range { csrow - 1, cscol - 1, cerow - 1, cecol }
ts_utils.update_selection(buf, node)
return
end
-- NOTE: parent() method is private
local parent_parser = current_parser:parent()
parent = parent_parser:named_node_for_range { csrow - 1, cscol - 1, cerow - 1, cecol }
end
node = parent
local srow, scol, erow, ecol = ts_utils.get_vim_range { node:range() }
local same_range = (srow == csrow and scol == cscol and erow == cerow and ecol == cecol)
if not same_range then
table.insert(selections[buf], node)
if node ~= nodes[#nodes] then
table.insert(nodes, node)
end
ts_utils.update_selection(buf, node)
return
end
end
end
end
M.node_incremental = select_incremental(function(node)
return node:parent() or node
end)
M.scope_incremental = select_incremental(function(node)
local lang = parsers.get_buf_lang()
if queries.has_locals(lang) then
return locals.containing_scope(node:parent() or node)
else
return node
end
end)
function M.node_decremental()
local buf = api.nvim_get_current_buf()
local nodes = selections[buf]
if not nodes or #nodes < 2 then
return
end
table.remove(selections[buf])
local node = nodes[#nodes] ---@type TSNode
ts_utils.update_selection(buf, node)
end
local FUNCTION_DESCRIPTIONS = {
init_selection = "Start selecting nodes with nvim-treesitter",
node_incremental = "Increment selection to named node",
scope_incremental = "Increment selection to surrounding scope",
node_decremental = "Shrink selection to previous named node",
}
---@param bufnr integer
function M.attach(bufnr)
local config = configs.get_module "incremental_selection"
for funcname, mapping in pairs(config.keymaps) do
if mapping then
local mode = funcname == "init_selection" and "n" or "x"
local rhs = M[funcname] ---@type function
if not rhs then
utils.notify("Unknown keybinding: " .. funcname .. debug.traceback(), vim.log.levels.ERROR)
else
vim.keymap.set(mode, mapping, rhs, { buffer = bufnr, silent = true, desc = FUNCTION_DESCRIPTIONS[funcname] })
end
end
end
end
function M.detach(bufnr)
local config = configs.get_module "incremental_selection"
for f, mapping in pairs(config.keymaps) do
if mapping then
local mode = f == "init_selection" and "n" or "x"
local ok, err = pcall(vim.keymap.del, mode, mapping, { buffer = bufnr })
if not ok then
utils.notify(string.format('%s "%s" for mode %s', err, mapping, mode), vim.log.levels.ERROR)
end
end
end
end
return M

View File

@@ -1,5 +1,4 @@
local ts = vim.treesitter
local parsers = require "nvim-treesitter.parsers"
local M = {}
@@ -14,13 +13,13 @@ M.comment_parsers = {
}
local function getline(lnum)
return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or ""
return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or ''
end
---@param lnum integer
---@return integer
local function get_indentcols_at_line(lnum)
local _, indentcols = getline(lnum):find "^%s*"
local _, indentcols = getline(lnum):find('^%s*')
return indentcols or 0
end
@@ -62,8 +61,8 @@ local function find_delimiter(bufnr, node, delimiter)
local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, false)[1]
local end_char = { child:end_() }
local trimmed_after_delim
local escaped_delimiter = delimiter:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1")
trimmed_after_delim, _ = line:sub(end_char[2] + 1):gsub("[%s" .. escaped_delimiter .. "]*", "")
local escaped_delimiter = delimiter:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]', '%%%1')
trimmed_after_delim = line:sub(end_char[2] + 1):gsub('[%s' .. escaped_delimiter .. ']*', '')
return child, #trimmed_after_delim == 0
end
end
@@ -75,7 +74,7 @@ end
---@param hash_fn fun(...): any
---@return F
local function memoize(fn, hash_fn)
local cache = setmetatable({}, { __mode = "kv" }) ---@type table<any,any>
local cache = setmetatable({}, { __mode = 'kv' }) ---@type table<any,any>
return function(...)
local key = hash_fn(...)
@@ -91,47 +90,47 @@ end
local get_indents = memoize(function(bufnr, root, lang)
local map = {
["indent.auto"] = {},
["indent.begin"] = {},
["indent.end"] = {},
["indent.dedent"] = {},
["indent.branch"] = {},
["indent.ignore"] = {},
["indent.align"] = {},
["indent.zero"] = {},
['indent.auto'] = {},
['indent.begin'] = {},
['indent.end'] = {},
['indent.dedent'] = {},
['indent.branch'] = {},
['indent.ignore'] = {},
['indent.align'] = {},
['indent.zero'] = {},
}
--TODO(clason): remove when dropping Nvim 0.8 compat
local query = (ts.query.get or ts.get_query)(lang, "indents")
local query = ts.query.get(lang, 'indents')
if not query then
return map
end
for id, node, metadata in query:iter_captures(root, bufnr) do
if query.captures[id]:sub(1, 1) ~= "_" then
if query.captures[id]:sub(1, 1) ~= '_' then
map[query.captures[id]][node:id()] = metadata or {}
end
end
return map
end, function(bufnr, root, lang)
return tostring(bufnr) .. root:id() .. "_" .. lang
return tostring(bufnr) .. root:id() .. '_' .. lang
end)
---@param lnum number (1-indexed)
---@return integer
function M.get_indent(lnum)
local bufnr = vim.api.nvim_get_current_buf()
local parser = parsers.get_parser(bufnr)
local parser = ts.get_parser(bufnr)
if not parser or not lnum then
return -1
end
--TODO(clason): replace when dropping Nvim 0.8 compat
local root_lang = parsers.get_buf_lang(bufnr)
local ft = vim.bo[bufnr].filetype
local root_lang = vim.treesitter.language.get_lang(ft) or ft
-- some languages like Python will actually have worse results when re-parsing at opened new line
if not M.avoid_force_reparsing[root_lang] then
-- Reparse in case we got triggered by ":h indentkeys"
parser:parse { vim.fn.line "w0" - 1, vim.fn.line "w$" }
parser:parse({ vim.fn.line('w0') - 1, vim.fn.line('w$') })
end
-- Get language tree with smallest range around node that's not a comment parser
@@ -155,15 +154,14 @@ function M.get_indent(lnum)
end
local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang())
local is_empty_line = string.match(getline(lnum), "^%s*$") ~= nil
local node ---@type TSNode
if is_empty_line then
if getline(lnum):find('^%s*$') then
local prevlnum = vim.fn.prevnonblank(lnum)
local indentcols = get_indentcols_at_line(prevlnum)
local prevline = vim.trim(getline(prevlnum))
-- The final position can be trailing spaces, which should not affect indentation
node = get_last_node_at_line(root, prevlnum, indentcols + #prevline - 1)
if node:type():match "comment" then
if node:type():match('comment') then
-- The final node we capture of the previous line can be a comment node, which should also be ignored
-- Unless the last line is an entire line of comment, ignore the comment range and find the last node again
local first_node = get_first_node_at_line(root, prevlnum, indentcols)
@@ -176,7 +174,7 @@ function M.get_indent(lnum)
node = get_last_node_at_line(root, prevlnum, col)
end
end
if q["indent.end"][node:id()] then
if q['indent.end'][node:id()] then
node = get_first_node_at_line(root, lnum)
end
else
@@ -192,18 +190,18 @@ function M.get_indent(lnum)
end
-- tracks to ensure multiple indent levels are not applied for same line
local is_processed_by_row = {}
local is_processed_by_row = {} --- @type table<integer,boolean>
if q["indent.zero"][node:id()] then
if q['indent.zero'][node:id()] then
return 0
end
while node do
-- do 'autoindent' if not marked as @indent
if
not q["indent.begin"][node:id()]
and not q["indent.align"][node:id()]
and q["indent.auto"][node:id()]
not q['indent.begin'][node:id()]
and not q['indent.align'][node:id()]
and q['indent.auto'][node:id()]
and node:start() < lnum - 1
and lnum - 1 <= node:end_()
then
@@ -214,8 +212,8 @@ function M.get_indent(lnum)
-- If a node spans from L1,C1 to L2,C2, we know that lines where L1 < line <= L2 would
-- have their indentations contained by the node.
if
not q["indent.begin"][node:id()]
and q["indent.ignore"][node:id()]
not q['indent.begin'][node:id()]
and q['indent.ignore'][node:id()]
and node:start() < lnum - 1
and lnum - 1 <= node:end_()
then
@@ -228,7 +226,10 @@ function M.get_indent(lnum)
if
not is_processed_by_row[srow]
and ((q["indent.branch"][node:id()] and srow == lnum - 1) or (q["indent.dedent"][node:id()] and srow ~= lnum - 1))
and (
(q['indent.branch'][node:id()] and srow == lnum - 1)
or (q['indent.dedent'][node:id()] and srow ~= lnum - 1)
)
then
indent = indent - indent_size
is_processed = true
@@ -244,16 +245,16 @@ function M.get_indent(lnum)
if
should_process
and (
q["indent.begin"][node:id()]
and (srow ~= erow or is_in_err or q["indent.begin"][node:id()]["indent.immediate"])
and (srow ~= lnum - 1 or q["indent.begin"][node:id()]["indent.start_at_same_line"])
q['indent.begin'][node:id()]
and (srow ~= erow or is_in_err or q['indent.begin'][node:id()]['indent.immediate'])
and (srow ~= lnum - 1 or q['indent.begin'][node:id()]['indent.start_at_same_line'])
)
then
indent = indent + indent_size
is_processed = true
end
if is_in_err and not q["indent.align"][node:id()] then
if is_in_err and not q['indent.align'][node:id()] then
-- only when the node is in error, promote the
-- first child's aligned indent to the error node
-- to work around ((ERROR "X" . (_)) @aligned_indent (#set! "delimiter" "AB"))
@@ -261,34 +262,41 @@ function M.get_indent(lnum)
-- (ERROR "X" @aligned_indent (#set! "delimiter" "AB") . (_))
-- and we will fish it out here.
for c in node:iter_children() do
if q["indent.align"][c:id()] then
q["indent.align"][node:id()] = q["indent.align"][c:id()]
if q['indent.align'][c:id()] then
q['indent.align'][node:id()] = q['indent.align'][c:id()]
break
end
end
end
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
if should_process and q["indent.align"][node:id()] and (srow ~= erow or is_in_err) and (srow ~= lnum - 1) then
local metadata = q["indent.align"][node:id()]
if
should_process
and q['indent.align'][node:id()]
and (srow ~= erow or is_in_err)
and (srow ~= lnum - 1)
then
local metadata = q['indent.align'][node:id()]
local o_delim_node, o_is_last_in_line ---@type TSNode|nil, boolean|nil
local c_delim_node, c_is_last_in_line ---@type TSNode|nil, boolean|nil, boolean|nil
local indent_is_absolute = false
if metadata["indent.open_delimiter"] then
o_delim_node, o_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.open_delimiter"])
if metadata['indent.open_delimiter'] then
o_delim_node, o_is_last_in_line =
find_delimiter(bufnr, node, metadata['indent.open_delimiter'])
else
o_delim_node = node
end
if metadata["indent.close_delimiter"] then
c_delim_node, c_is_last_in_line = find_delimiter(bufnr, node, metadata["indent.close_delimiter"])
if metadata['indent.close_delimiter'] then
c_delim_node, c_is_last_in_line =
find_delimiter(bufnr, node, metadata['indent.close_delimiter'])
else
c_delim_node = node
end
if o_delim_node then
local o_srow, o_scol = o_delim_node:start()
local c_srow = nil
local c_srow = nil --- @type integer?
if c_delim_node then
c_srow, _ = c_delim_node:start()
c_srow = c_delim_node:start()
end
if o_is_last_in_line then
-- hanging indent (previous line ended with starting delimiter)
@@ -310,7 +318,7 @@ function M.get_indent(lnum)
-- Then its indent level shouldn't be affected by `@aligned_indent` node
indent = math.max(indent - indent_size, 0)
else
indent = o_scol + (metadata["indent.increment"] or 1)
indent = o_scol + (metadata['indent.increment'] or 1)
indent_is_absolute = true
end
end
@@ -321,7 +329,7 @@ function M.get_indent(lnum)
-- then this last line may need additional indent to avoid clashes
-- with the next. `indent.avoid_last_matching_next` controls this behavior,
-- for example this is needed for function parameters.
avoid_last_matching_next = metadata["indent.avoid_last_matching_next"] or false
avoid_last_matching_next = metadata['indent.avoid_last_matching_next'] or false
end
if avoid_last_matching_next then
-- last line must be indented more in cases where
@@ -350,17 +358,4 @@ function M.get_indent(lnum)
return indent
end
---@type table<integer, string>
local indent_funcs = {}
---@param bufnr integer
function M.attach(bufnr)
indent_funcs[bufnr] = vim.bo.indentexpr
vim.bo.indentexpr = "nvim_treesitter#indent()"
end
function M.detach(bufnr)
vim.bo.indentexpr = indent_funcs[bufnr]
end
return M

View File

@@ -1,190 +0,0 @@
local api = vim.api
local configs = require "nvim-treesitter.configs"
local parsers = require "nvim-treesitter.parsers"
local M = {}
local function install_info()
local max_len = 0
for _, ft in pairs(parsers.available_parsers()) do
if #ft > max_len then
max_len = #ft
end
end
local parser_list = parsers.available_parsers()
table.sort(parser_list)
for _, lang in pairs(parser_list) do
local is_installed = #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0
api.nvim_out_write(lang .. string.rep(" ", max_len - #lang + 1))
if is_installed then
api.nvim_out_write "[✓] installed\n"
elseif pcall(vim.treesitter.inspect_lang, lang) then
api.nvim_out_write "[✗] not installed (but still loaded. Restart Neovim!)\n"
else
api.nvim_out_write "[✗] not installed\n"
end
end
end
-- Sort a list of modules into namespaces.
-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'}
-- ->
-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}}
---@param modulelist string[]
---@return table
local function namespace_modules(modulelist)
local modules = {}
for _, module in ipairs(modulelist) do
if module:find "%." then
local namespace, submodule = module:match "^(.*)%.(.*)$"
if not modules[namespace] then
modules[namespace] = {}
end
table.insert(modules[namespace], submodule)
else
if not modules.default then
modules.default = {}
end
table.insert(modules.default, module)
end
end
return modules
end
---@param list string[]
---@return integer length
local function longest_string_length(list)
local length = 0
for _, value in ipairs(list) do
if #value > length then
length = #value
end
end
return length
end
---@param curbuf integer
---@param origbuf integer
---@param parserlist string[]
---@param namespace string
---@param modulelist string[]
local function append_module_table(curbuf, origbuf, parserlist, namespace, modulelist)
local maxlen_parser = longest_string_length(parserlist)
table.sort(modulelist)
-- header
local header = ">> " .. namespace .. string.rep(" ", maxlen_parser - #namespace - 1)
for _, module in pairs(modulelist) do
header = header .. module .. " "
end
api.nvim_buf_set_lines(curbuf, -1, -1, true, { header })
-- actual table
for _, parser in ipairs(parserlist) do
local padding = string.rep(" ", maxlen_parser - #parser + 2)
local line = parser .. padding
local namespace_prefix = (namespace == "default") and "" or namespace .. "."
for _, module in pairs(modulelist) do
local modlen = #module
module = namespace_prefix .. module
if configs.is_enabled(module, parser, origbuf) then
line = line .. ""
else
line = line .. ""
end
line = line .. string.rep(" ", modlen + 1)
end
api.nvim_buf_set_lines(curbuf, -1, -1, true, { line })
end
api.nvim_buf_set_lines(curbuf, -1, -1, true, { "" })
end
local function print_info_modules(parserlist, module)
local origbuf = api.nvim_get_current_buf()
api.nvim_command "enew"
local curbuf = api.nvim_get_current_buf()
local modules
if module then
modules = namespace_modules { module }
else
modules = namespace_modules(configs.available_modules())
end
---@type string[]
local namespaces = {}
for k, _ in pairs(modules) do
table.insert(namespaces, k)
end
table.sort(namespaces)
table.sort(parserlist)
for _, namespace in ipairs(namespaces) do
append_module_table(curbuf, origbuf, parserlist, namespace, modules[namespace])
end
api.nvim_buf_set_option(curbuf, "modified", false)
api.nvim_buf_set_option(curbuf, "buftype", "nofile")
vim.cmd [[
syntax match TSModuleInfoGood /✓/
syntax match TSModuleInfoBad /✗/
syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace
syntax match TSModuleInfoNamespace /^>> \w*/ contained
syntax match TSModuleInfoParser /^[^> ]*\ze /
]]
local highlights = {
TSModuleInfoGood = { fg = "LightGreen", bold = true, default = true },
TSModuleInfoBad = { fg = "Crimson", default = true },
TSModuleInfoHeader = { link = "Type", default = true },
TSModuleInfoNamespace = { link = "Statement", default = true },
TSModuleInfoParser = { link = "Identifier", default = true },
}
for k, v in pairs(highlights) do
api.nvim_set_hl(0, k, v)
end
end
local function module_info(module)
if module and not configs.get_module(module) then
return
end
local parserlist = parsers.available_parsers()
if module then
print_info_modules(parserlist, module)
else
print_info_modules(parserlist)
end
end
---@return string[]
function M.installed_parsers()
local installed = {}
for _, p in pairs(parsers.available_parsers()) do
if parsers.has_parser(p) then
table.insert(installed, p)
end
end
return installed
end
M.commands = {
TSInstallInfo = {
run = install_info,
args = {
"-nargs=0",
},
},
TSModuleInfo = {
run = module_info,
args = {
"-nargs=?",
"-complete=custom,nvim_treesitter#available_modules",
},
},
}
return M

View File

@@ -0,0 +1,11 @@
local M = {}
function M.setup(...)
require('nvim-treesitter.config').setup(...)
end
function M.indentexpr()
return require('nvim-treesitter.indent').get_indent(vim.v.lnum)
end
return M

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,46 @@
-- Functions to handle locals
-- Locals are a generalization of definition and scopes
-- its the way nvim-treesitter uses to "understand" the code
-- it's the way nvim-treesitter uses to "understand" the code
local queries = require "nvim-treesitter.query"
local ts_utils = require "nvim-treesitter.ts_utils"
local ts = vim.treesitter
local query = require('nvim-treesitter.query')
local api = vim.api
local ts = vim.treesitter
local M = {}
function M.collect_locals(bufnr)
return queries.collect_group_results(bufnr, "locals")
local function get_named_children(node)
local nodes = {} ---@type TSNode[]
for i = 0, node:named_child_count() - 1, 1 do
nodes[i + 1] = node:named_child(i)
end
return nodes
end
---@param node TSNode
---@return TSNode result
local function get_root_for_node(node)
local parent = node
local result = node
while parent ~= nil do
result = parent
parent = result:parent()
end
return result
end
-- Iterates matches from a locals query file.
-- @param bufnr the buffer
-- @param root the root node
function M.iter_locals(bufnr, root)
return queries.iter_group_results(bufnr, "locals", root)
return query.iter_group_results(bufnr, 'locals', root)
end
---@param bufnr integer
---@return any
function M.get_locals(bufnr)
return queries.get_matches(bufnr, "locals")
function M.collect_locals(bufnr)
return query.collect_group_results(bufnr, 'locals')
end
-- Creates unique id for a node based on text and range
@@ -32,17 +49,17 @@ end
---@return string: a string id
function M.get_definition_id(scope, node_text)
-- Add a valid starting character in case node text doesn't start with a valid one.
return table.concat({ "k", node_text or "", scope:range() }, "_")
return table.concat({ 'k', node_text or '', scope:range() }, '_')
end
function M.get_definitions(bufnr)
local locals = M.get_locals(bufnr)
local locals = M.collect_locals(bufnr)
local defs = {}
for _, loc in ipairs(locals) do
if loc["local"]["definition"] then
table.insert(defs, loc["local"]["definition"])
if loc['local.definition'] then
table.insert(defs, loc['local.definition'])
end
end
@@ -50,13 +67,13 @@ function M.get_definitions(bufnr)
end
function M.get_scopes(bufnr)
local locals = M.get_locals(bufnr)
local locals = M.collect_locals(bufnr)
local scopes = {}
for _, loc in ipairs(locals) do
if loc["local"]["scope"] and loc["local"]["scope"].node then
table.insert(scopes, loc["local"]["scope"].node)
if loc['local.scope'] and loc['local.scope'].node then
table.insert(scopes, loc['local.scope'].node)
end
end
@@ -64,13 +81,13 @@ function M.get_scopes(bufnr)
end
function M.get_references(bufnr)
local locals = M.get_locals(bufnr)
local locals = M.collect_locals(bufnr)
local refs = {}
for _, loc in ipairs(locals) do
if loc["local"]["reference"] and loc["local"]["reference"].node then
table.insert(refs, loc["local"]["reference"].node)
if loc['local.reference'] and loc['local.reference'].node then
table.insert(refs, loc['local.reference'].node)
end
end
@@ -103,7 +120,7 @@ function M.iter_scope_tree(node, bufnr)
return
end
local scope = M.containing_scope(last_node, bufnr, false) or ts_utils.get_root_for_node(node)
local scope = M.containing_scope(last_node, bufnr, false) or get_root_for_node(node)
last_node = scope:parent()
@@ -117,8 +134,8 @@ end
function M.get_local_nodes(local_def)
local result = {}
M.recurse_local_nodes(local_def, function(def, _node, kind)
table.insert(result, vim.tbl_extend("keep", { kind = kind }, def))
M.recurse_local_nodes(local_def, function(def, _, kind)
table.insert(result, vim.tbl_extend('keep', { kind = kind }, def))
end)
return result
@@ -135,7 +152,7 @@ end
---@param full_match? string The full match path to append to
---@param last_match? string The last match
function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
if type(local_def) ~= "table" then
if type(local_def) ~= 'table' then
return
end
@@ -143,11 +160,36 @@ function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
accumulator(local_def, local_def.node, full_match, last_match)
else
for match_key, def in pairs(local_def) do
M.recurse_local_nodes(def, accumulator, full_match and (full_match .. "." .. match_key) or match_key, match_key)
M.recurse_local_nodes(
def,
accumulator,
full_match and (full_match .. '.' .. match_key) or match_key,
match_key
)
end
end
end
---Memoize a function using hash_fn to hash the arguments.
---@generic F: function
---@param fn F
---@param hash_fn fun(...): any
---@return F
local function memoize(fn, hash_fn)
local cache = setmetatable({}, { __mode = 'kv' }) ---@type table<any,any>
return function(...)
local key = hash_fn(...)
if cache[key] == nil then
local v = fn(...) ---@type any
cache[key] = v ~= nil and v or vim.NIL
end
local v = cache[key]
return v ~= vim.NIL and v or nil
end
end
-- Get a single dimension table to look definition nodes.
-- Keys are generated by using the range of the containing scope and the text of the definition node.
-- This makes looking up a definition for a given scope a simple key lookup.
@@ -161,7 +203,7 @@ end
--
---@param bufnr integer: the buffer
---@return table result: a table for looking up definitions
M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr)
M.get_definitions_lookup_table = memoize(function(bufnr)
local definitions = M.get_definitions(bufnr)
local result = {}
@@ -178,6 +220,8 @@ M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr)
end
return result
end, function(bufnr)
return tostring(bufnr)
end)
-- Gets all the scopes of a definition based on the scope type
@@ -196,10 +240,10 @@ function M.get_definition_scopes(node, bufnr, scope_type)
-- Definition is valid for the containing scope
-- and the containing scope of that scope
if scope_type == "parent" then
if scope_type == 'parent' then
scope_count = 2
-- Definition is valid in all parent scopes
elseif scope_type == "global" then
elseif scope_type == 'global' then
scope_count = nil
end
@@ -235,7 +279,7 @@ function M.find_definition(node, bufnr)
end
end
return node, ts_utils.get_root_for_node(node), nil
return node, get_root_for_node(node), nil
end
-- Finds usages of a node in a given scope.
@@ -250,12 +294,15 @@ function M.find_usages(node, scope_node, bufnr)
return {}
end
local scope_node = scope_node or ts_utils.get_root_for_node(node)
scope_node = scope_node or get_root_for_node(node)
local usages = {}
for match in M.iter_locals(bufnr, scope_node) do
match = match["local"]
if match.reference and match.reference.node and ts.get_node_text(match.reference.node, bufnr) == node_text then
if
match.reference
and match.reference.node
and ts.get_node_text(match.reference.node, bufnr) == node_text
then
local def_node, _, kind = M.find_definition(match.reference.node, bufnr)
if kind == nil or def_node == node then
@@ -272,8 +319,8 @@ end
---@param allow_scope? boolean
---@return TSNode|nil
function M.containing_scope(node, bufnr, allow_scope)
local bufnr = bufnr or api.nvim_get_current_buf()
local allow_scope = allow_scope == nil or allow_scope == true
bufnr = bufnr or api.nvim_get_current_buf()
allow_scope = allow_scope == nil or allow_scope == true
local scopes = M.get_scopes(bufnr)
if not node or not scopes then
@@ -301,7 +348,7 @@ function M.nested_scope(node, cursor_pos)
local col = cursor_pos.col ---@type integer
local scope = M.containing_scope(node)
for _, child in ipairs(ts_utils.get_named_children(scope)) do
for _, child in ipairs(get_named_children(scope)) do
local row_, col_ = child:start()
if vim.tbl_contains(scopes, child) and ((row_ + 1 == row and col_ > col) or row_ + 1 > row) then
return child
@@ -318,6 +365,9 @@ function M.next_scope(node)
end
local scope = M.containing_scope(node)
if not scope then
return
end
local parent = scope:parent()
if not parent then
@@ -325,7 +375,7 @@ function M.next_scope(node)
end
local is_prev = true
for _, child in ipairs(ts_utils.get_named_children(parent)) do
for _, child in ipairs(get_named_children(parent)) do
if child == scope then
is_prev = false
elseif not is_prev and vim.tbl_contains(scopes, child) then
@@ -345,6 +395,9 @@ function M.previous_scope(node)
end
local scope = M.containing_scope(node)
if not scope then
return
end
local parent = scope:parent()
if not parent then
@@ -352,7 +405,7 @@ function M.previous_scope(node)
end
local is_prev = true
local children = ts_utils.get_named_children(parent)
local children = get_named_children(parent)
for i = #children, 1, -1 do
if children[i] == scope then
is_prev = false

File diff suppressed because it is too large Load Diff

View File

@@ -1,154 +1,7 @@
local api = vim.api
local ts = require "nvim-treesitter.compat"
local tsrange = require "nvim-treesitter.tsrange"
local utils = require "nvim-treesitter.utils"
local parsers = require "nvim-treesitter.parsers"
local caching = require "nvim-treesitter.caching"
local M = {}
local EMPTY_ITER = function() end
M.built_in_query_groups = { "highlights", "locals", "folds", "indents", "injections" }
-- Creates a function that checks whether a given query exists
-- for a specific language.
---@param query string
---@return fun(string): boolean
local function get_query_guard(query)
return function(lang)
return M.has_query_files(lang, query)
end
end
for _, query in ipairs(M.built_in_query_groups) do
M["has_" .. query] = get_query_guard(query)
end
---@return string[]
function M.available_query_groups()
local query_files = api.nvim_get_runtime_file("queries/*/*.scm", true)
local groups = {}
for _, f in ipairs(query_files) do
groups[vim.fn.fnamemodify(f, ":t:r")] = true
end
local list = {}
for k, _ in pairs(groups) do
table.insert(list, k)
end
return list
end
do
local query_cache = caching.create_buffer_cache()
local function update_cached_matches(bufnr, changed_tick, query_group)
query_cache.set(query_group, bufnr, {
tick = changed_tick,
cache = M.collect_group_results(bufnr, query_group) or {},
})
end
---@param bufnr integer
---@param query_group string
---@return any
function M.get_matches(bufnr, query_group)
bufnr = bufnr or api.nvim_get_current_buf()
local cached_local = query_cache.get(query_group, bufnr)
if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then
update_cached_matches(bufnr, api.nvim_buf_get_changedtick(bufnr), query_group)
end
return query_cache.get(query_group, bufnr).cache
end
end
---@param lang string
---@param query_name string
---@return string[]
local function runtime_queries(lang, query_name)
return api.nvim_get_runtime_file(string.format("queries/%s/%s.scm", lang, query_name), true) or {}
end
---@type table<string, table<string, boolean>>
local query_files_cache = {}
---@param lang string
---@param query_name string
---@return boolean
function M.has_query_files(lang, query_name)
if not query_files_cache[lang] then
query_files_cache[lang] = {}
end
if query_files_cache[lang][query_name] == nil then
local files = runtime_queries(lang, query_name)
query_files_cache[lang][query_name] = files and #files > 0
end
return query_files_cache[lang][query_name]
end
do
local mt = {}
mt.__index = function(tbl, key)
if rawget(tbl, key) == nil then
rawset(tbl, key, {})
end
return rawget(tbl, key)
end
-- cache will auto set the table for each lang if it is nil
---@type table<string, table<string, Query>>
local cache = setmetatable({}, mt)
-- Same as `vim.treesitter.query` except will return cached values
---@param lang string
---@param query_name string
function M.get_query(lang, query_name)
if cache[lang][query_name] == nil then
cache[lang][query_name] = ts.get_query(lang, query_name)
end
return cache[lang][query_name]
end
-- Invalidates the query file cache.
--
-- If lang and query_name is both present, will reload for only the lang and query_name.
-- If only lang is present, will reload all query_names for that lang
-- If none are present, will reload everything
---@param lang? string
---@param query_name? string
function M.invalidate_query_cache(lang, query_name)
if lang and query_name then
cache[lang][query_name] = nil
if query_files_cache[lang] then
query_files_cache[lang][query_name] = nil
end
elseif lang and not query_name then
query_files_cache[lang] = nil
for query_name0, _ in pairs(cache[lang]) do
M.invalidate_query_cache(lang, query_name0)
end
elseif not lang and not query_name then
query_files_cache = {}
for lang0, _ in pairs(cache) do
for query_name0, _ in pairs(cache[lang0]) do
M.invalidate_query_cache(lang0, query_name0)
end
end
else
error "Cannot have query_name by itself!"
end
end
end
-- This function is meant for an autocommand and not to be used. Only use if file is a query file.
---@param fname string
function M.invalidate_query_file(fname)
local fnamemodify = vim.fn.fnamemodify
M.invalidate_query_cache(fnamemodify(fname, ":p:h:t"), fnamemodify(fname, ":t:r"))
end
---@class QueryInfo
---@field root TSNode
---@field source integer
@@ -161,13 +14,13 @@ end
---@param root_lang string|nil
---@return Query|nil, QueryInfo|nil
local function prepare_query(bufnr, query_name, root, root_lang)
local buf_lang = parsers.get_buf_lang(bufnr)
local ft = vim.bo[bufnr].filetype
local buf_lang = vim.treesitter.language.get_lang(ft) or ft
if not buf_lang then
return
end
local parser = parsers.get_parser(bufnr, buf_lang)
local parser = vim.treesitter.get_parser(bufnr, buf_lang)
if not parser then
return
end
@@ -198,7 +51,7 @@ local function prepare_query(bufnr, query_name, root, root_lang)
return
end
local query = M.get_query(root_lang, query_name)
local query = vim.treesitter.query.get(root_lang, query_name)
if not query then
return
end
@@ -241,7 +94,7 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
---@return string[]
local function split(to_split)
local t = {}
for str in string.gmatch(to_split, "([^.]+)") do
for str in string.gmatch(to_split, '([^.]+)') do
table.insert(t, str)
end
@@ -259,9 +112,9 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
for id, node in pairs(match) do
local name = query.captures[id] -- name of the capture in the query
if name ~= nil then
local path = split(name .. ".node")
local path = split(name .. '.node')
M.insert_to_path(prepared_match, path, node)
local metadata_path = split(name .. ".metadata")
local metadata_path = split(name .. '.metadata')
M.insert_to_path(prepared_match, metadata_path, metadata[id])
end
end
@@ -272,16 +125,9 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
if preds then
for _, pred in pairs(preds) do
-- functions
if pred[1] == "set!" and type(pred[2]) == "string" then
if pred[1] == 'set!' and type(pred[2]) == 'string' then
M.insert_to_path(prepared_match, split(pred[2]), pred[3])
end
if pred[1] == "make-range!" and type(pred[2]) == "string" and #pred == 4 then
M.insert_to_path(
prepared_match,
split(pred[2] .. ".node"),
tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]])
)
end
end
end
@@ -291,103 +137,6 @@ function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
return iterator
end
-- Return all nodes corresponding to a specific capture path (like @definition.var, @reference.type)
-- Works like M.get_references or M.get_scopes except you can choose the capture
-- Can also be a nested capture like @definition.function to get all nodes defining a function.
--
---@param bufnr integer the buffer
---@param captures string|string[]
---@param query_group string the name of query group (highlights or injections for example)
---@param root TSNode|nil node from where to start the search
---@param lang string|nil the language from where to get the captures.
--- Root nodes can have several languages.
---@return table|nil
function M.get_capture_matches(bufnr, captures, query_group, root, lang)
if type(captures) == "string" then
captures = { captures }
end
local strip_captures = {} ---@type string[]
for i, capture in ipairs(captures) do
if capture:sub(1, 1) ~= "@" then
error 'Captures must start with "@"'
return
end
-- Remove leading "@".
strip_captures[i] = capture:sub(2)
end
local matches = {}
for match in M.iter_group_results(bufnr, query_group, root, lang) do
for _, capture in ipairs(strip_captures) do
local insert = utils.get_at_path(match, capture)
if insert then
table.insert(matches, insert)
end
end
end
return matches
end
function M.iter_captures(bufnr, query_name, root, lang)
local query, params = prepare_query(bufnr, query_name, root, lang)
if not query then
return EMPTY_ITER
end
assert(params)
local iter = query:iter_captures(params.root, params.source, params.start, params.stop)
local function wrapped_iter()
local id, node, metadata = iter()
if not id then
return
end
local name = query.captures[id]
if string.sub(name, 1, 1) == "_" then
return wrapped_iter()
end
return name, node, metadata
end
return wrapped_iter
end
---@param bufnr integer
---@param capture_string string
---@param query_group string
---@param filter_predicate fun(match: table): boolean
---@param scoring_function fun(match: table): number
---@param root TSNode
---@return table|unknown
function M.find_best_match(bufnr, capture_string, query_group, filter_predicate, scoring_function, root)
if string.sub(capture_string, 1, 1) == "@" then
--remove leading "@"
capture_string = string.sub(capture_string, 2)
end
local best ---@type table|nil
local best_score ---@type number
for maybe_match in M.iter_group_results(bufnr, query_group, root) do
local match = utils.get_at_path(maybe_match, capture_string)
if match and filter_predicate(match) then
local current_score = scoring_function(match)
if not best then
best = match
best_score = current_score
end
if current_score > best_score then
best = match
best_score = current_score
end
end
end
return best
end
---Iterates matches from a query file.
---@param bufnr integer the buffer
---@param query_group string the query file to use
@@ -413,41 +162,4 @@ function M.collect_group_results(bufnr, query_group, root, lang)
return matches
end
---@alias CaptureResFn function(string, LanguageTree, LanguageTree): string, string
-- Same as get_capture_matches except this will recursively get matches for every language in the tree.
---@param bufnr integer The buffer
---@param capture_or_fn string|CaptureResFn The capture to get. If a function is provided then that
--- function will be used to resolve both the capture and query argument.
--- The function can return `nil` to ignore that tree.
---@param query_type string? The query to get the capture from. This is ignored if a function is provided
--- for the capture argument.
---@return table[]
function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type)
---@type CaptureResFn
local type_fn
if type(capture_or_fn) == "function" then
type_fn = capture_or_fn
else
type_fn = function(_, _, _)
return capture_or_fn, query_type
end
end
local parser = parsers.get_parser(bufnr)
local matches = {}
if parser then
parser:for_each_tree(function(tree, lang_tree)
local lang = lang_tree:lang()
local capture, type_ = type_fn(lang, tree, lang_tree)
if capture then
vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang) or {})
end
end)
end
return matches
end
return M

View File

@@ -1,167 +0,0 @@
local query = require "vim.treesitter.query"
local html_script_type_languages = {
["importmap"] = "json",
["module"] = "javascript",
["application/ecmascript"] = "javascript",
["text/ecmascript"] = "javascript",
}
local non_filetype_match_injection_language_aliases = {
ex = "elixir",
pl = "perl",
sh = "bash",
uxn = "uxntal",
ts = "typescript",
}
-- compatibility shim for breaking change on nightly/0.11
local opts = vim.fn.has "nvim-0.10" == 1 and { force = true, all = false } or true
local function get_parser_from_markdown_info_string(injection_alias)
local match = vim.filetype.match { filename = "a." .. injection_alias }
return match or non_filetype_match_injection_language_aliases[injection_alias] or injection_alias
end
local function error(str)
vim.api.nvim_err_writeln(str)
end
local function valid_args(name, pred, count, strict_count)
local arg_count = #pred - 1
if strict_count then
if arg_count ~= count then
error(string.format("%s must have exactly %d arguments", name, count))
return false
end
elseif arg_count < count then
error(string.format("%s must have at least %d arguments", name, count))
return false
end
return true
end
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("nth?", function(match, _pattern, _bufnr, pred)
if not valid_args("nth?", pred, 2, true) then
return
end
local node = match[pred[2]] ---@type TSNode
local n = tonumber(pred[3])
if node and node:parent() and node:parent():named_child_count() > n then
return node:parent():named_child(n) == node
end
return false
end, opts)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("is?", function(match, _pattern, bufnr, pred)
if not valid_args("is?", pred, 2) then
return
end
-- Avoid circular dependencies
local locals = require "nvim-treesitter.locals"
local node = match[pred[2]]
local types = { unpack(pred, 3) }
if not node then
return true
end
local _, _, kind = locals.find_definition(node, bufnr)
return vim.tbl_contains(types, kind)
end, opts)
---@param match (TSNode|nil)[]
---@param _pattern string
---@param _bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_predicate("kind-eq?", function(match, _pattern, _bufnr, pred)
if not valid_args(pred[1], pred, 2) then
return
end
local node = match[pred[2]]
local types = { unpack(pred, 3) }
if not node then
return true
end
return vim.tbl_contains(types, node:type())
end, opts)
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_directive("set-lang-from-mimetype!", function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
local node = match[capture_id]
if not node then
return
end
local type_attr_value = vim.treesitter.get_node_text(node, bufnr)
local configured = html_script_type_languages[type_attr_value]
if configured then
metadata["injection.language"] = configured
else
local parts = vim.split(type_attr_value, "/", {})
metadata["injection.language"] = parts[#parts]
end
end, opts)
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_directive("set-lang-from-info-string!", function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
local node = match[capture_id]
if not node then
return
end
local injection_alias = vim.treesitter.get_node_text(node, bufnr):lower()
metadata["injection.language"] = get_parser_from_markdown_info_string(injection_alias)
end, opts)
-- Just avoid some annoying warnings for this directive
query.add_directive("make-range!", function() end, opts)
--- transform node text to lower case (e.g., to make @injection.language case insensitive)
---
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_directive("downcase!", function(match, _, bufnr, pred, metadata)
local id = pred[2]
local node = match[id]
if not node then
return
end
local text = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] }) or ""
if not metadata[id] then
metadata[id] = {}
end
metadata[id].text = string.lower(text)
end, opts)

View File

@@ -0,0 +1,255 @@
local uv = vim.loop
local utils = require('nvim-treesitter.utils')
local iswin = uv.os_uname().sysname == 'Windows_NT'
local M = {}
---@param executables string[]
---@return string|nil
function M.select_executable(executables)
return vim.tbl_filter(function(c) ---@param c string
return c ~= vim.NIL and vim.fn.executable(c) == 1
end, executables)[1]
end
-- Returns the compiler arguments based on the compiler and OS
---@param repo InstallInfo
---@param compiler string
---@return string[]
function M.select_compiler_args(repo, compiler)
if compiler:find('cl$') or compiler:find('cl.exe$') then
return {
'/Fe:',
'parser.so',
'/Isrc',
repo.files,
'-Os',
'/utf-8',
'/LD',
}
elseif compiler:find('zig$') or compiler:find('zig.exe$') then
return {
'c++',
'-o',
'parser.so',
repo.files,
'-lc',
'-Isrc',
'-shared',
'-Os',
}
else
local args = {
'-o',
'parser.so',
'-I./src',
repo.files,
'-Os',
}
if uv.os_uname().sysname == 'Darwin' then
table.insert(args, '-bundle')
else
table.insert(args, '-shared')
end
if
#vim.tbl_filter(function(file) ---@param file string
local ext = vim.fn.fnamemodify(file, ':e')
return ext == 'cc' or ext == 'cpp' or ext == 'cxx'
end, repo.files) > 0
then
table.insert(args, '-lstdc++')
end
if not iswin then
table.insert(args, '-fPIC')
end
return args
end
end
-- Returns the compile command based on the OS and user options
---@param repo InstallInfo
---@param cc string
---@param compile_location string
---@return Command
function M.select_compile_command(repo, cc, compile_location)
local make = M.select_executable({ 'gmake', 'make' })
if cc:find('cl$') or cc:find('cl.exe$') or not repo.use_makefile or iswin or not make then
return {
cmd = cc,
info = 'Compiling...',
err = 'Error during compilation',
opts = {
args = vim.tbl_flatten(M.select_compiler_args(repo, cc)),
cwd = compile_location,
},
}
else
return {
cmd = make,
info = 'Compiling...',
err = 'Error during compilation',
opts = {
args = {
'--makefile=' .. utils.get_package_path('scripts', 'compile_parsers.makefile'),
'CC=' .. cc,
},
cwd = compile_location,
},
}
end
end
---@param repo InstallInfo
---@param project_name string
---@param cache_dir string
---@param revision string|nil
---@param prefer_git boolean
---@return table
function M.select_download_commands(repo, project_name, cache_dir, revision, prefer_git)
local can_use_tar = vim.fn.executable('tar') == 1 and vim.fn.executable('curl') == 1
local is_github = repo.url:find('github.com', 1, true)
local is_gitlab = repo.url:find('gitlab.com', 1, true)
local project_dir = utils.join_path(cache_dir, project_name)
revision = revision or repo.branch or 'master'
if can_use_tar and (is_github or is_gitlab) and not prefer_git then
local url = repo.url:gsub('.git$', '')
local dir_rev = revision
if is_github and revision:find('^v%d') then
dir_rev = revision:sub(2)
end
local temp_dir = project_dir .. '-tmp'
return {
{
cmd = function()
vim.fn.delete(temp_dir, 'rf')
end,
},
{
cmd = 'curl',
info = 'Downloading ' .. project_name .. '...',
err = 'Error during download, please verify your internet connection',
opts = {
args = {
'--silent',
'-L', -- follow redirects
is_github and url .. '/archive/' .. revision .. '.tar.gz'
or url
.. '/-/archive/'
.. revision
.. '/'
.. project_name
.. '-'
.. revision
.. '.tar.gz',
'--output',
project_name .. '.tar.gz',
},
cwd = cache_dir,
},
},
{
cmd = function()
--TODO(clason): use vim.fn.mkdir(temp_dir, 'p') in case stdpath('cache') is not created
uv.fs_mkdir(temp_dir, 493)
end,
info = 'Creating temporary directory',
err = 'Could not create ' .. project_name .. '-tmp',
},
{
cmd = 'tar',
info = 'Extracting ' .. project_name .. '...',
err = 'Error during tarball extraction.',
opts = {
args = {
'-xvzf',
project_name .. '.tar.gz',
'-C',
project_name .. '-tmp',
},
cwd = cache_dir,
},
},
{
cmd = function()
uv.fs_unlink(project_dir .. '.tar.gz')
end,
},
{
cmd = function()
uv.fs_rename(
utils.join_path(temp_dir, url:match('[^/]-$') .. '-' .. dir_rev),
project_dir
)
end,
},
{
cmd = function()
vim.fn.delete(temp_dir, 'rf')
end,
},
}
else
local git_dir = project_dir
local clone_error = 'Error during download, please verify your internet connection'
return {
{
cmd = 'git',
info = 'Downloading ' .. project_name .. '...',
err = clone_error,
opts = {
args = {
'clone',
repo.url,
project_name,
},
cwd = cache_dir,
},
},
{
cmd = 'git',
info = 'Checking out locked revision',
err = 'Error while checking out revision',
opts = {
args = {
'checkout',
revision,
},
cwd = git_dir,
},
},
}
end
end
--TODO(clason): only needed for iter_cmd_sync -> replace with uv.spawn?
-- Convert path for cmd.exe on Windows (needed when shellslash is set)
---@param p string
---@return string
local function cmdpath(p)
return vim.o.shellslash and p:gsub('/', '\\') or p
end
---@param dir string
---@param command string
---@return string command
function M.make_directory_change_for_command(dir, command)
if iswin then
if string.find(vim.o.shell, 'cmd') ~= nil then
return string.format('pushd %s & %s & popd', cmdpath(dir), command)
else
return string.format('pushd %s ; %s ; popd', cmdpath(dir), command)
end
else
return string.format('cd %s;\n %s', dir, command)
end
end
return M

View File

@@ -1,368 +0,0 @@
local fn = vim.fn
local utils = require "nvim-treesitter.utils"
local uv = vim.uv or vim.loop
-- Convert path for cmd.exe on Windows.
-- This is needed when vim.opt.shellslash is in use.
---@param p string
---@return string
local function cmdpath(p)
if vim.opt.shellslash:get() then
local r = p:gsub("/", "\\")
return r
else
return p
end
end
local M = {}
-- Returns the mkdir command based on the OS
---@param directory string
---@param cwd string
---@param info_msg string
---@return table
function M.select_mkdir_cmd(directory, cwd, info_msg)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "mkdir", cmdpath(directory) },
cwd = cwd,
},
info = info_msg,
err = "Could not create " .. directory,
}
else
return {
cmd = "mkdir",
opts = {
args = { directory },
cwd = cwd,
},
info = info_msg,
err = "Could not create " .. directory,
}
end
end
-- Returns the remove command based on the OS
---@param file string
---@param info_msg string
---@return table
function M.select_rm_file_cmd(file, info_msg)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "if", "exist", cmdpath(file), "del", cmdpath(file) },
},
info = info_msg,
err = "Could not delete " .. file,
}
else
return {
cmd = "rm",
opts = {
args = { file },
},
info = info_msg,
err = "Could not delete " .. file,
}
end
end
---@param executables string[]
---@return string|nil
function M.select_executable(executables)
return vim.tbl_filter(function(c) ---@param c string
return c ~= vim.NIL and fn.executable(c) == 1
end, executables)[1]
end
-- Returns the compiler arguments based on the compiler and OS
---@param repo InstallInfo
---@param compiler string
---@return string[]
function M.select_compiler_args(repo, compiler)
if string.match(compiler, "cl$") or string.match(compiler, "cl.exe$") then
return {
"/Fe:",
"parser.so",
"/Isrc",
repo.files,
"-Os",
"/std:c11",
"/utf-8",
"/LD",
}
elseif string.match(compiler, "zig$") or string.match(compiler, "zig.exe$") then
return {
"c++",
"-o",
"parser.so",
repo.files,
"-lc",
"-Isrc",
"-shared",
"-Os",
"-std=c11",
}
else
local args = {
"-o",
"parser.so",
"-I./src",
repo.files,
"-Os",
"-std=c11",
}
if fn.has "mac" == 1 then
table.insert(args, "-bundle")
else
table.insert(args, "-shared")
end
if
#vim.tbl_filter(function(file) ---@param file string
local ext = vim.fn.fnamemodify(file, ":e")
return ext == "cc" or ext == "cpp" or ext == "cxx"
end, repo.files) > 0
then
table.insert(args, "-lstdc++")
end
if fn.has "win32" == 0 then
table.insert(args, "-fPIC")
end
return args
end
end
-- Returns the compile command based on the OS and user options
---@param repo InstallInfo
---@param cc string
---@param compile_location string
---@return Command
function M.select_compile_command(repo, cc, compile_location)
local make = M.select_executable { "gmake", "make" }
if
string.match(cc, "cl$")
or string.match(cc, "cl.exe$")
or not repo.use_makefile
or fn.has "win32" == 1
or not make
then
return {
cmd = cc,
info = "Compiling...",
err = "Error during compilation",
opts = {
args = require("nvim-treesitter.compat").flatten(M.select_compiler_args(repo, cc)),
cwd = compile_location,
},
}
else
return {
cmd = make,
info = "Compiling...",
err = "Error during compilation",
opts = {
args = {
"--makefile=" .. utils.join_path(utils.get_package_path(), "scripts", "compile_parsers.makefile"),
"CC=" .. cc,
"CXX_STANDARD=" .. (repo.cxx_standard or "c++14"),
},
cwd = compile_location,
},
}
end
end
-- Returns the remove command based on the OS
---@param cache_folder string
---@param project_name string
---@return Command
function M.select_install_rm_cmd(cache_folder, project_name)
if fn.has "win32" == 1 then
local dir = cache_folder .. "\\" .. project_name
return {
cmd = "cmd",
opts = {
args = { "/C", "if", "exist", cmdpath(dir), "rmdir", "/s", "/q", cmdpath(dir) },
},
}
else
return {
cmd = "rm",
opts = {
args = { "-rf", cache_folder .. "/" .. project_name },
},
}
end
end
-- Returns the move command based on the OS
---@param from string
---@param to string
---@param cwd string
---@return Command
function M.select_mv_cmd(from, to, cwd)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "move", "/Y", cmdpath(from), cmdpath(to) },
cwd = cwd,
},
}
else
return {
cmd = "mv",
opts = {
args = { "-f", from, to },
cwd = cwd,
},
}
end
end
---@param repo InstallInfo
---@param project_name string
---@param cache_folder string
---@param revision string|nil
---@param prefer_git boolean
---@return table
function M.select_download_commands(repo, project_name, cache_folder, revision, prefer_git)
local can_use_tar = vim.fn.executable "tar" == 1 and vim.fn.executable "curl" == 1
local is_github = repo.url:find("github.com", 1, true)
local is_gitlab = repo.url:find("gitlab.com", 1, true)
revision = revision or repo.branch or "master"
if can_use_tar and (is_github or is_gitlab) and not prefer_git then
local path_sep = utils.get_path_sep()
local url = repo.url:gsub(".git$", "")
local folder_rev = revision
if is_github and revision:match "^v%d" then
folder_rev = revision:sub(2)
end
return {
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"),
{
cmd = "curl",
info = "Downloading " .. project_name .. "...",
err = "Error during download, please verify your internet connection",
opts = {
args = {
"--silent",
"--show-error",
"-L", -- follow redirects
is_github and url .. "/archive/" .. revision .. ".tar.gz"
or url .. "/-/archive/" .. revision .. "/" .. project_name .. "-" .. revision .. ".tar.gz",
"--output",
project_name .. ".tar.gz",
},
cwd = cache_folder,
},
},
M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"),
{
cmd = "tar",
info = "Extracting " .. project_name .. "...",
err = "Error during tarball extraction.",
opts = {
args = {
"-xvzf",
project_name .. ".tar.gz",
"-C",
project_name .. "-tmp",
},
cwd = cache_folder,
},
},
M.select_rm_file_cmd(cache_folder .. path_sep .. project_name .. ".tar.gz"),
M.select_mv_cmd(
utils.join_path(project_name .. "-tmp", url:match "[^/]-$" .. "-" .. folder_rev),
project_name,
cache_folder
),
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"),
}
else
local git_folder = utils.join_path(cache_folder, project_name)
local clone_error = "Error during download, please verify your internet connection"
-- Running `git clone` or `git checkout` while running under Git (such as
-- editing a `git commit` message) will likely fail to install parsers
-- (such as 'gitcommit') and can also corrupt the index file of the current
-- Git repository. Check for typical git environment variables and abort if found.
for _, k in pairs {
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
"GIT_CEILING_DIRECTORIES",
"GIT_DIR",
"GIT_INDEX",
"GIT_INDEX_FILE",
"GIT_OBJECT_DIRECTORY",
"GIT_PREFIX",
"GIT_WORK_TREE",
} do
if uv.os_getenv(k) then
vim.api.nvim_err_writeln(
string.format(
"Cannot install %s with git in an active git session. Exit the session and run ':TSInstall %s' manually",
project_name,
project_name
)
)
return {}
end
end
return {
{
cmd = "git",
info = "Downloading " .. project_name .. "...",
err = clone_error,
opts = {
args = {
"clone",
repo.url,
project_name,
"--filter=blob:none",
},
cwd = cache_folder,
},
},
{
cmd = "git",
info = "Checking out locked revision",
err = "Error while checking out revision",
opts = {
args = {
"checkout",
revision,
},
cwd = git_folder,
},
},
}
end
end
---@param dir string
---@param command string
---@return string command
function M.make_directory_change_for_command(dir, command)
if fn.has "win32" == 1 then
if string.find(vim.o.shell, "cmd") ~= nil then
return string.format("pushd %s & %s", cmdpath(dir), command)
else
return string.format("pushd %s ; %s", cmdpath(dir), command)
end
else
return string.format("cd %s;\n%s", dir, command)
end
end
return M

View File

@@ -1,53 +0,0 @@
local parsers = require "nvim-treesitter.parsers"
local ts_utils = require "nvim-treesitter.ts_utils"
local M = {}
-- Trim spaces and opening brackets from end
local transform_line = function(line)
return line:gsub("%s*[%[%(%{]*%s*$", "")
end
function M.statusline(opts)
if not parsers.has_parser() then
return
end
local options = opts or {}
if type(opts) == "number" then
options = { indicator_size = opts }
end
local bufnr = options.bufnr or 0
local indicator_size = options.indicator_size or 100
local type_patterns = options.type_patterns or { "class", "function", "method" }
local transform_fn = options.transform_fn or transform_line
local separator = options.separator or " -> "
local allow_duplicates = options.allow_duplicates or false
local current_node = ts_utils.get_node_at_cursor()
if not current_node then
return ""
end
local lines = {}
local expr = current_node
while expr do
local line = ts_utils._get_line_for_node(expr, type_patterns, transform_fn, bufnr)
if line ~= "" then
if allow_duplicates or not vim.tbl_contains(lines, line) then
table.insert(lines, 1, line)
end
end
expr = expr:parent()
end
local text = table.concat(lines, separator)
local text_len = #text
if text_len > indicator_size then
return "..." .. text:sub(text_len - indicator_size, text_len)
end
return text
end
return M

View File

@@ -1,482 +0,0 @@
local api = vim.api
local parsers = require "nvim-treesitter.parsers"
local utils = require "nvim-treesitter.utils"
local ts = vim.treesitter
local M = {}
local function get_node_text(node, bufnr)
bufnr = bufnr or api.nvim_get_current_buf()
if not node then
return {}
end
-- We have to remember that end_col is end-exclusive
local start_row, start_col, end_row, end_col = ts.get_node_range(node)
if start_row ~= end_row then
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false)
if next(lines) == nil then
return {}
end
lines[1] = string.sub(lines[1], start_col + 1)
-- end_row might be just after the last line. In this case the last line is not truncated.
if #lines == end_row - start_row + 1 then
lines[#lines] = string.sub(lines[#lines], 1, end_col)
end
return lines
else
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1]
-- If line is nil then the line is empty
return line and { string.sub(line, start_col + 1, end_col) } or {}
end
end
---@private
---@param node TSNode
---@param type_patterns string[]
---@param transform_fn fun(line: string): string
---@param bufnr integer
---@return string
function M._get_line_for_node(node, type_patterns, transform_fn, bufnr)
local node_type = node:type()
local is_valid = false
for _, rgx in ipairs(type_patterns) do
if node_type:find(rgx) then
is_valid = true
break
end
end
if not is_valid then
return ""
end
local line = transform_fn(vim.trim(get_node_text(node, bufnr)[1] or ""), node)
-- Escape % to avoid statusline to evaluate content as expression
return line:gsub("%%", "%%%%")
end
-- Gets the actual text content of a node
-- @deprecated Use vim.treesitter.get_node_text
-- @param node the node to get the text from
-- @param bufnr the buffer containing the node
-- @return list of lines of text of the node
function M.get_node_text(node, bufnr)
vim.notify_once(
"nvim-treesitter.ts_utils.get_node_text is deprecated: use vim.treesitter.get_node_text",
vim.log.levels.WARN
)
return get_node_text(node, bufnr)
end
-- Determines whether a node is the parent of another
-- @param dest the possible parent
-- @param source the possible child node
function M.is_parent(dest, source)
if not (dest and source) then
return false
end
local current = source
while current ~= nil do
if current == dest then
return true
end
current = current:parent()
end
return false
end
-- Get next node with same parent
---@param node TSNode
---@param allow_switch_parents? boolean allow switching parents if last node
---@param allow_next_parent? boolean allow next parent if last node and next parent without children
function M.get_next_node(node, allow_switch_parents, allow_next_parent)
local destination_node ---@type TSNode
local parent = node:parent()
if not parent then
return
end
local found_pos = 0
for i = 0, parent:named_child_count() - 1, 1 do
if parent:named_child(i) == node then
found_pos = i
break
end
end
if parent:named_child_count() > found_pos + 1 then
destination_node = parent:named_child(found_pos + 1)
elseif allow_switch_parents then
local next_node = M.get_next_node(node:parent())
if next_node and next_node:named_child_count() > 0 then
destination_node = next_node:named_child(0)
elseif next_node and allow_next_parent then
destination_node = next_node
end
end
return destination_node
end
-- Get previous node with same parent
---@param node TSNode
---@param allow_switch_parents? boolean allow switching parents if first node
---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children
function M.get_previous_node(node, allow_switch_parents, allow_previous_parent)
local destination_node ---@type TSNode
local parent = node:parent()
if not parent then
return
end
local found_pos = 0
for i = 0, parent:named_child_count() - 1, 1 do
if parent:named_child(i) == node then
found_pos = i
break
end
end
if 0 < found_pos then
destination_node = parent:named_child(found_pos - 1)
elseif allow_switch_parents then
local previous_node = M.get_previous_node(node:parent())
if previous_node and previous_node:named_child_count() > 0 then
destination_node = previous_node:named_child(previous_node:named_child_count() - 1)
elseif previous_node and allow_previous_parent then
destination_node = previous_node
end
end
return destination_node
end
function M.get_named_children(node)
local nodes = {} ---@type TSNode[]
for i = 0, node:named_child_count() - 1, 1 do
nodes[i + 1] = node:named_child(i)
end
return nodes
end
function M.get_node_at_cursor(winnr, ignore_injected_langs)
winnr = winnr or 0
local cursor = api.nvim_win_get_cursor(winnr)
local cursor_range = { cursor[1] - 1, cursor[2] }
local buf = vim.api.nvim_win_get_buf(winnr)
local root_lang_tree = parsers.get_parser(buf)
if not root_lang_tree then
return
end
local root ---@type TSNode|nil
if ignore_injected_langs then
for _, tree in pairs(root_lang_tree:trees()) do
local tree_root = tree:root()
if tree_root and ts.is_in_node_range(tree_root, cursor_range[1], cursor_range[2]) then
root = tree_root
break
end
end
else
root = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree)
end
if not root then
return
end
return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2])
end
function M.get_root_for_position(line, col, root_lang_tree)
if not root_lang_tree then
if not parsers.has_parser() then
return
end
root_lang_tree = parsers.get_parser()
end
local lang_tree = root_lang_tree:language_for_range { line, col, line, col }
while true do
for _, tree in pairs(lang_tree:trees()) do
local root = tree:root()
if root and ts.is_in_node_range(root, line, col) then
return root, tree, lang_tree
end
end
if lang_tree == root_lang_tree then
break
end
-- This case can happen when the cursor is at the start of a line that ends a injected region,
-- e.g., the first `]` in the following lua code:
-- ```
-- vim.cmd[[
-- ]]
-- ```
lang_tree = lang_tree:parent() -- NOTE: parent() method is private
end
-- This isn't a likely scenario, since the position must belong to a tree somewhere.
return nil, nil, lang_tree
end
---comment
---@param node TSNode
---@return TSNode result
function M.get_root_for_node(node)
local parent = node
local result = node
while parent ~= nil do
result = parent
parent = result:parent()
end
return result
end
function M.highlight_node(node, buf, hl_namespace, hl_group)
if not node then
return
end
M.highlight_range({ node:range() }, buf, hl_namespace, hl_group)
end
-- Get a compatible vim range (1 index based) from a TS node range.
--
-- TS nodes start with 0 and the end col is ending exclusive.
-- They also treat a EOF/EOL char as a char ending in the first
-- col of the next row.
---comment
---@param range integer[]
---@param buf integer|nil
---@return integer, integer, integer, integer
function M.get_vim_range(range, buf)
---@type integer, integer, integer, integer
local srow, scol, erow, ecol = unpack(range)
srow = srow + 1
scol = scol + 1
erow = erow + 1
if ecol == 0 then
-- Use the value of the last col of the previous row instead.
erow = erow - 1
if not buf or buf == 0 then
ecol = vim.fn.col { erow, "$" } - 1
else
ecol = #api.nvim_buf_get_lines(buf, erow - 1, erow, false)[1]
end
ecol = math.max(ecol, 1)
end
return srow, scol, erow, ecol
end
function M.highlight_range(range, buf, hl_namespace, hl_group)
---@type integer, integer, integer, integer
local start_row, start_col, end_row, end_col = unpack(range)
---@diagnostic disable-next-line: missing-parameter
vim.highlight.range(buf, hl_namespace, hl_group, { start_row, start_col }, { end_row, end_col })
end
-- Set visual selection to node
-- @param selection_mode One of "charwise" (default) or "v", "linewise" or "V",
-- "blockwise" or "<C-v>" (as a string with 5 characters or a single character)
function M.update_selection(buf, node, selection_mode)
local start_row, start_col, end_row, end_col = M.get_vim_range({ ts.get_node_range(node) }, buf)
local v_table = { charwise = "v", linewise = "V", blockwise = "<C-v>" }
selection_mode = selection_mode or "charwise"
-- Normalise selection_mode
if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then
selection_mode = v_table[selection_mode]
end
-- enter visual mode if normal or operator-pending (no) mode
-- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html
-- If your operator-pending mapping ends with some text visually selected, Vim will operate on that text.
-- Otherwise, Vim will operate on the text between the original cursor position and the new position.
local mode = api.nvim_get_mode()
if mode.mode ~= selection_mode then
-- Call to `nvim_replace_termcodes()` is needed for sending appropriate command to enter blockwise mode
selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true)
api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {})
end
api.nvim_win_set_cursor(0, { start_row, start_col - 1 })
vim.cmd "normal! o"
api.nvim_win_set_cursor(0, { end_row, end_col - 1 })
end
-- Byte length of node range
---@param node TSNode
---@return number
function M.node_length(node)
local _, _, start_byte = node:start()
local _, _, end_byte = node:end_()
return end_byte - start_byte
end
---@deprecated Use `vim.treesitter.is_in_node_range()` instead
function M.is_in_node_range(node, line, col)
vim.notify_once(
"nvim-treesitter.ts_utils.is_in_node_range is deprecated: use vim.treesitter.is_in_node_range",
vim.log.levels.WARN
)
return ts.is_in_node_range(node, line, col)
end
---@deprecated Use `vim.treesitter.get_node_range()` instead
function M.get_node_range(node_or_range)
vim.notify_once(
"nvim-treesitter.ts_utils.get_node_range is deprecated: use vim.treesitter.get_node_range",
vim.log.levels.WARN
)
return ts.get_node_range(node_or_range)
end
---@param node TSNode
---@return table
function M.node_to_lsp_range(node)
local start_line, start_col, end_line, end_col = ts.get_node_range(node)
local rtn = {}
rtn.start = { line = start_line, character = start_col }
rtn["end"] = { line = end_line, character = end_col }
return rtn
end
-- Memoizes a function based on the buffer tick of the provided bufnr.
-- The cache entry is cleared when the buffer is detached to avoid memory leaks.
-- The options argument is a table with two optional values:
-- - bufnr: extracts a bufnr from the given arguments.
-- - key: extracts the cache key from the given arguments.
---@param fn function the fn to memoize, taking the buffer as first argument
---@param options? {bufnr: integer?, key: string|fun(...): string?} the memoization options
---@return function: a memoized function
function M.memoize_by_buf_tick(fn, options)
options = options or {}
---@type table<string, {result: any, last_tick: integer}>
local cache = setmetatable({}, { __mode = "kv" })
local bufnr_fn = utils.to_func(options.bufnr or utils.identity)
local key_fn = utils.to_func(options.key or utils.identity)
return function(...)
local bufnr = bufnr_fn(...)
local key = key_fn(...)
local tick = api.nvim_buf_get_changedtick(bufnr)
if cache[key] then
if cache[key].last_tick == tick then
return cache[key].result
end
else
local function detach_handler()
cache[key] = nil
end
-- Clean up logic only!
api.nvim_buf_attach(bufnr, false, {
on_detach = detach_handler,
on_reload = detach_handler,
})
end
cache[key] = {
result = fn(...),
last_tick = tick,
}
return cache[key].result
end
end
function M.swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)
if not node_or_range1 or not node_or_range2 then
return
end
local range1 = M.node_to_lsp_range(node_or_range1)
local range2 = M.node_to_lsp_range(node_or_range2)
local text1 = get_node_text(node_or_range1, bufnr)
local text2 = get_node_text(node_or_range2, bufnr)
local edit1 = { range = range1, newText = table.concat(text2, "\n") }
local edit2 = { range = range2, newText = table.concat(text1, "\n") }
bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr
vim.lsp.util.apply_text_edits({ edit1, edit2 }, bufnr, "utf-8")
if cursor_to_second then
utils.set_jump()
local char_delta = 0
local line_delta = 0
if
range1["end"].line < range2.start.line
or (range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character)
then
line_delta = #text2 - #text1
end
if range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character then
if line_delta ~= 0 then
--- why?
--correction_after_line_change = -range2.start.character
--text_now_before_range2 = #(text2[#text2])
--space_between_ranges = range2.start.character - range1["end"].character
--char_delta = correction_after_line_change + text_now_before_range2 + space_between_ranges
--- Equivalent to:
char_delta = #text2[#text2] - range1["end"].character
-- add range1.start.character if last line of range1 (now text2) does not start at 0
if range1.start.line == range2.start.line + line_delta then
char_delta = char_delta + range1.start.character
end
else
char_delta = #text2[#text2] - #text1[#text1]
end
end
api.nvim_win_set_cursor(
api.nvim_get_current_win(),
{ range2.start.line + 1 + line_delta, range2.start.character + char_delta }
)
end
end
function M.goto_node(node, goto_end, avoid_set_jump)
if not node then
return
end
if not avoid_set_jump then
utils.set_jump()
end
local range = { M.get_vim_range { node:range() } }
---@type table<number>
local position
if not goto_end then
position = { range[1], range[2] }
else
position = { range[3], range[4] }
end
-- Enter visual mode if we are in operator pending mode
-- If we don't do this, it will miss the last character.
local mode = vim.api.nvim_get_mode()
if mode.mode == "no" then
vim.cmd "normal! v"
end
-- Position is 1, 0 indexed.
api.nvim_win_set_cursor(0, { position[1], position[2] - 1 })
end
return M

View File

@@ -1,154 +0,0 @@
local M = {}
local TSRange = {}
TSRange.__index = TSRange
local api = vim.api
local ts_utils = require "nvim-treesitter.ts_utils"
local parsers = require "nvim-treesitter.parsers"
local function get_byte_offset(buf, row, col)
return api.nvim_buf_get_offset(buf, row) + vim.fn.byteidx(api.nvim_buf_get_lines(buf, row, row + 1, false)[1], col)
end
function TSRange.new(buf, start_row, start_col, end_row, end_col)
return setmetatable({
start_pos = { start_row, start_col, get_byte_offset(buf, start_row, start_col) },
end_pos = { end_row, end_col, get_byte_offset(buf, end_row, end_col) },
buf = buf,
[1] = start_row,
[2] = start_col,
[3] = end_row,
[4] = end_col,
}, TSRange)
end
function TSRange.from_nodes(buf, start_node, end_node)
TSRange.__index = TSRange
local start_pos = start_node and { start_node:start() } or { end_node:start() }
local end_pos = end_node and { end_node:end_() } or { start_node:end_() }
return setmetatable({
start_pos = { start_pos[1], start_pos[2], start_pos[3] },
end_pos = { end_pos[1], end_pos[2], end_pos[3] },
buf = buf,
[1] = start_pos[1],
[2] = start_pos[2],
[3] = end_pos[1],
[4] = end_pos[2],
}, TSRange)
end
function TSRange.from_table(buf, range)
return setmetatable({
start_pos = { range[1], range[2], get_byte_offset(buf, range[1], range[2]) },
end_pos = { range[3], range[4], get_byte_offset(buf, range[3], range[4]) },
buf = buf,
[1] = range[1],
[2] = range[2],
[3] = range[3],
[4] = range[4],
}, TSRange)
end
function TSRange:parent()
local root_lang_tree = parsers.get_parser(self.buf)
local root = ts_utils.get_root_for_position(self[1], self[2], root_lang_tree)
return root
and root:named_descendant_for_range(self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2])
or nil
end
function TSRange:field() end
function TSRange:child_count()
return #self:collect_children()
end
function TSRange:named_child_count()
return #self:collect_children(function(c)
return c:named()
end)
end
function TSRange:iter_children()
local raw_iterator = self:parent().iter_children()
return function()
while true do
local node = raw_iterator()
if not node then
return
end
local _, _, start_byte = node:start()
local _, _, end_byte = node:end_()
if start_byte >= self.start_pos[3] and end_byte <= self.end_pos[3] then
return node
end
end
end
end
function TSRange:collect_children(filter_fun)
local children = {}
for _, c in self:iter_children() do
if not filter_fun or filter_fun(c) then
table.insert(children, c)
end
end
return children
end
function TSRange:child(index)
return self:collect_children()[index + 1]
end
function TSRange:named_child(index)
return self:collect_children(function(c)
return c.named()
end)[index + 1]
end
function TSRange:start()
return unpack(self.start_pos)
end
function TSRange:end_()
return unpack(self.end_pos)
end
function TSRange:range()
return self.start_pos[1], self.start_pos[2], self.end_pos[1], self.end_pos[2]
end
function TSRange:type()
return "nvim-treesitter-range"
end
function TSRange:symbol()
return -1
end
function TSRange:named()
return false
end
function TSRange:missing()
return false
end
function TSRange:has_error()
return #self:collect_children(function(c)
return c:has_error()
end) > 0 and true or false
end
function TSRange:sexpr()
return table.concat(
vim.tbl_map(function(c)
return c:sexpr()
end, self:collect_children()),
" "
)
end
M.TSRange = TSRange
return M

View File

@@ -1,237 +1,12 @@
local api = vim.api
local fn = vim.fn
local luv = vim.loop
local M = {}
-- Wrapper around vim.notify with common options set.
---@param msg string
---@param log_level number|nil
---@param opts table|nil
function M.notify(msg, log_level, opts)
local default_opts = { title = "nvim-treesitter" }
vim.notify(msg, log_level, vim.tbl_extend("force", default_opts, opts or {}))
--TODO(clason): replace by vim.fs._join_paths
function M.join_path(...)
return (table.concat({ ... }, '/'):gsub('//+', '/'))
end
-- Returns the system-specific path separator.
---@return string
function M.get_path_sep()
return (fn.has "win32" == 1 and not vim.opt.shellslash:get()) and "\\" or "/"
end
-- Returns a function that joins the given arguments with separator. Arguments
-- can't be nil. Example:
--
--[[
print(M.generate_join(" ")("foo", "bar"))
--]]
--prints "foo bar"
---@param separator string
---@return fun(...: string): string
function M.generate_join(separator)
return function(...)
return table.concat({ ... }, separator)
end
end
M.join_path = M.generate_join(M.get_path_sep())
M.join_space = M.generate_join " "
---@class Command
---@field run function
---@field f_args string
---@field args string
-- Define user defined vim command which calls nvim-treesitter module function
-- - If module name is 'mod', it should be defined in hierarchy 'nvim-treesitter.mod'
-- - A table with name 'commands' should be defined in 'mod' which needs to be passed as
-- the commands param of this function
--
---@param mod string Name of the module that resides in the hierarchy - nvim-treesitter.module
---@param commands table<string, Command> Command list for the module
--- - {command_name} Name of the vim user defined command, Keys:
--- - {run}: (function) callback function that needs to be executed
--- - {f_args}: (string, default <f-args>)
--- - type of arguments that needs to be passed to the vim command
--- - {args}: (string, optional)
--- - vim command attributes
---
---* @example
--- If module is nvim-treesitter.custom_mod
--- <pre>
--- M.commands = {
--- custom_command = {
--- run = M.module_function,
--- f_args = "<f-args>",
--- args = {
--- "-range"
--- }
--- }
--- }
---
--- utils.setup_commands("custom_mod", require("nvim-treesitter.custom_mod").commands)
--- </pre>
---
--- Will generate command :
--- <pre>
--- command! -range custom_command \
--- lua require'nvim-treesitter.custom_mod'.commands.custom_command['run<bang>'](<f-args>)
--- </pre>
function M.setup_commands(mod, commands)
for command_name, def in pairs(commands) do
local f_args = def.f_args or "<f-args>"
local call_fn =
string.format("lua require'nvim-treesitter.%s'.commands.%s['run<bang>'](%s)", mod, command_name, f_args)
local parts = require("nvim-treesitter.compat").flatten {
"command!",
"-bar",
def.args,
command_name,
call_fn,
}
api.nvim_command(table.concat(parts, " "))
end
end
---@param dir string
---@param create_err string
---@param writeable_err string
---@return string|nil, string|nil
function M.create_or_reuse_writable_dir(dir, create_err, writeable_err)
create_err = create_err or M.join_space("Could not create dir '", dir, "': ")
writeable_err = writeable_err or M.join_space("Invalid rights, '", dir, "' should be read/write")
-- Try creating and using parser_dir if it doesn't exist
if not luv.fs_stat(dir) then
local ok, error = pcall(vim.fn.mkdir, dir, "p", "0755")
if not ok then
return nil, M.join_space(create_err, error)
end
return dir
end
-- parser_dir exists, use it if it's read/write
if luv.fs_access(dir, "RW") then
return dir
end
-- parser_dir exists but isn't read/write, give up
return nil, M.join_space(writeable_err, dir, "'")
end
function M.get_package_path()
-- Path to this source file, removing the leading '@'
local source = string.sub(debug.getinfo(1, "S").source, 2)
-- Path to the package root
return fn.fnamemodify(source, ":p:h:h:h")
end
function M.get_cache_dir()
local cache_dir = fn.stdpath "data"
if luv.fs_access(cache_dir, "RW") then
return cache_dir
elseif luv.fs_access("/tmp", "RW") then
return "/tmp"
end
return nil, M.join_space("Invalid cache rights,", fn.stdpath "data", "or /tmp should be read/write")
end
-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in
-- runtimepath
function M.get_site_dir()
return M.join_path(fn.stdpath "data", "site")
end
-- Gets a property at path
---@param tbl table the table to access
---@param path string the '.' separated path
---@return table|nil result the value at path or nil
function M.get_at_path(tbl, path)
if path == "" then
return tbl
end
local segments = vim.split(path, ".", true)
---@type table[]|table
local result = tbl
for _, segment in ipairs(segments) do
if type(result) == "table" then
---@type table
-- TODO: figure out the actual type of tbl
result = result[segment]
end
end
return result
end
function M.set_jump()
vim.cmd "normal! m'"
end
-- Filters a list based on the given predicate
---@param tbl any[] The list to filter
---@param predicate fun(v:any, i:number):boolean The predicate to filter with
function M.filter(tbl, predicate)
local result = {}
for i, v in ipairs(tbl) do
if predicate(v, i) then
table.insert(result, v)
end
end
return result
end
-- Returns a list of all values from the first list
-- that are not present in the second list.
---@param tbl1 any[] The first table
---@param tbl2 any[] The second table
---@return table
function M.difference(tbl1, tbl2)
return M.filter(tbl1, function(v)
return not vim.tbl_contains(tbl2, v)
end)
end
function M.identity(a)
return a
end
-- Returns a function returning the given value
---@param a any
---@return fun():any
function M.constant(a)
return function()
return a
end
end
-- Returns a function that returns the given value if it is a function,
-- otherwise returns a function that returns the given value.
---@param a any
---@return fun(...):any
function M.to_func(a)
return type(a) == "function" and a or M.constant(a)
end
---@return string|nil
function M.ts_cli_version()
if fn.executable "tree-sitter" == 1 then
local handle = io.popen "tree-sitter -V"
if not handle then
return
end
local result = handle:read "*a"
handle:close()
return vim.split(result, "\n")[1]:match "[^tree%psitter ].*"
end
function M.get_package_path(...)
return M.join_path(vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':p:h:h:h'), ...)
end
return M

View File

@@ -1,2 +0,0 @@
*
!.gitignore

2
parser/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!.gitignore

66
plugin/filetypes.lua Normal file
View File

@@ -0,0 +1,66 @@
local filetypes = {
angular = { 'htmlangular' },
bash = { 'sh' },
bibtex = { 'bib' },
c_sharp = { 'cs', 'csharp', 'c-sharp' },
commonlisp = { 'lisp' },
cooklang = { 'cook' },
devicetree = { 'dts' },
diff = { 'gitdiff' },
eex = { 'eelixir' },
elixir = { 'ex' },
embedded_template = { 'eruby' },
erlang = { 'erl' },
facility = { 'fsd' },
faust = { 'dsp' },
gdshader = { 'gdshaderinc' },
git_config = { 'gitconfig' },
git_rebase = { 'gitrebase' },
glimmer = { 'handlebars', 'html.handlebars' },
godot_resource = { 'gdresource' },
haskell = { 'hs' },
haskell_persistent = { 'haskellpersistent' },
idris = { 'idris2' },
janet_simple = { 'janet' },
javascript = { 'javascriptreact', 'ecma', 'jsx', 'js' },
javascript_glimmer = { 'javascript.glimmer' },
linkerscript = { 'ld' },
latex = { 'tex' },
m68k = { 'asm68k' },
make = { 'automake' },
markdown = { 'pandoc', 'quarto', 'rmd' },
muttrc = { 'neomuttrc' },
ocaml_interface = { 'ocamlinterface' },
perl = { 'pl' },
poe_filter = { 'poefilter' },
properties = { 'jproperties' },
python = { 'py', 'gyp' },
qmljs = { 'qml' },
runescript = { 'clientscript' },
scala = { 'sbt' },
slang = { 'shaderslang' },
sqp = { 'mysqp' },
ssh_config = { 'sshconfig' },
starlark = { 'bzl' },
surface = { 'sface' },
t32 = { 'trace32' },
tcl = { 'expect' },
terraform = { 'terraform-vars' },
textproto = { 'pbtxt' },
tlaplus = { 'tla' },
tsx = { 'typescriptreact', 'typescript.tsx' },
typescript = { 'ts' },
typescript_glimmer = { 'typescript.glimmer' },
typst = { 'typ' },
udev = { 'udevrules' },
uxntal = { 'tal', 'uxn' },
v = { 'vlang' },
verilog = { 'systemverilog' },
vhs = { 'tape' },
xml = { 'xsd', 'xslt', 'svg' },
xresources = { 'xdefaults' },
}
for lang, ft in pairs(filetypes) do
vim.treesitter.language.register(lang, ft)
end

View File

@@ -1,34 +1,86 @@
-- Last Change: 2022 Apr 16
if vim.g.loaded_nvim_treesitter then
return
end
vim.g.loaded_nvim_treesitter = true
-- setup modules
require("nvim-treesitter").setup()
local api = vim.api
-- define autocommands
local augroup = api.nvim_create_augroup("NvimTreesitter", {})
local function complete_available_parsers(arglead)
return vim.iter.filter(function(v)
return v:find(arglead)
end, require('nvim-treesitter.parsers').get_available())
end
api.nvim_create_autocmd("Filetype", {
pattern = "query",
group = augroup,
callback = function()
api.nvim_clear_autocmds {
group = augroup,
event = "BufWritePost",
}
api.nvim_create_autocmd("BufWritePost", {
group = augroup,
buffer = 0,
callback = function(opts)
require("nvim-treesitter.query").invalidate_query_file(opts.file)
end,
desc = "Invalidate query file",
})
end,
desc = "Reload query",
local function complete_installed_parsers(arglead)
return vim.iter.filter(function(v)
return v:find(arglead)
end, require('nvim-treesitter.config').installed_parsers())
end
-- create user commands
api.nvim_create_user_command('TSInstallInfo', function()
require('nvim-treesitter.install').info()
end, { nargs = 0, desc = 'List available treesitter parsers' })
api.nvim_create_user_command('TSInstall', function(args)
require('nvim-treesitter.install').install(args.fargs, { force = args.bang })
end, {
nargs = '+',
bang = true,
bar = true,
complete = complete_available_parsers,
desc = 'Install treesitter parsers',
})
api.nvim_create_user_command('TSInstallFromGrammar', function(args)
require('nvim-treesitter.install').install(args.fargs, {
generate_from_grammar = true,
force = args.bang,
})
end, {
nargs = '+',
bang = true,
bar = true,
complete = complete_available_parsers,
desc = 'Install treesitter parsers from grammar',
})
api.nvim_create_user_command('TSInstallSync', function(args)
require('nvim-treesitter.install').install(args.fargs, {
with_sync = true,
force = args.bang,
})
end, {
nargs = '+',
bang = true,
bar = true,
complete = complete_available_parsers,
desc = 'Install treesitter parsers synchronously',
})
api.nvim_create_user_command('TSUpdate', function(args)
require('nvim-treesitter.install').update(args.fargs)
end, {
nargs = '*',
bar = true,
complete = complete_installed_parsers,
desc = 'Update installed treesitter parsers',
})
api.nvim_create_user_command('TSUpdateSync', function(args)
require('nvim-treesitter.install').update(args.fargs, { with_sync = true })
end, {
nargs = '*',
bar = true,
complete = complete_installed_parsers,
desc = 'Update installed treesitter parsers synchronously',
})
api.nvim_create_user_command('TSUninstall', function(args)
require('nvim-treesitter.install').uninstall(args.fargs)
end, {
nargs = '+',
bar = true,
complete = complete_installed_parsers,
desc = 'Uninstall treesitter parsers',
})

141
plugin/query_predicates.lua Normal file
View File

@@ -0,0 +1,141 @@
local query = vim.treesitter.query
-- register custom predicates
---@param match (TSNode|nil)[]
---@param pred string[]
---@return boolean|nil
query.add_predicate('kind-eq?', function(match, _, _, pred)
local node = match[pred[2]]
if not node then
return true
end
local types = { unpack(pred, 3) }
return vim.list_contains(types, node:type())
end)
-- register custom directives
local mimetype_aliases = {
['importmap'] = 'json',
['module'] = 'javascript',
['application/ecmascript'] = 'javascript',
['text/ecmascript'] = 'javascript',
}
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_directive('set-lang-from-mimetype!', function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
local node = match[capture_id]
if not node then
return
end
local type_attr_value = vim.treesitter.get_node_text(node, bufnr)
local configured = mimetype_aliases[type_attr_value]
if configured then
metadata['injection.language'] = configured
else
local parts = vim.split(type_attr_value, '/', {})
metadata['injection.language'] = parts[#parts]
end
end)
local injection_aliases = {
ex = 'elixir',
pl = 'perl',
sh = 'bash',
uxn = 'uxntal',
ts = 'typescript',
}
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@return boolean|nil
query.add_directive('set-lang-from-info-string!', function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
local node = match[capture_id]
if not node then
return
end
local injection_alias = vim.treesitter.get_node_text(node, bufnr)
local filetype = vim.filetype.match({ filename = 'a.' .. injection_alias })
metadata['injection.language'] = filetype or injection_aliases[injection_alias] or injection_alias
end)
query.add_directive('downcase!', function(match, _, bufnr, pred, metadata)
local text, key, value ---@type string|string[], string, string|integer
if #pred == 3 then
-- (#downcase! @capture "key")
key = pred[3]
value = metadata[pred[2]][key]
else
-- (#downcase! "key")
key = pred[2]
value = metadata[key]
end
if type(value) == 'string' then
text = value
else
local node = match[value]
text = vim.treesitter.get_node_text(node, bufnr) or ''
end
if #pred == 3 then
metadata[pred[2]][key] = string.lower(text)
else
metadata[key] = string.lower(text)
end
end)
-- Trim blank lines from end of the region
-- Arguments are the captures to trim.
---@param match (TSNode|nil)[]
---@param _ string
---@param bufnr integer
---@param pred string[]
---@param metadata table
---TODO(clason): upstream
query.add_directive('trim!', function(match, _, bufnr, pred, metadata)
for _, id in ipairs({ select(2, unpack(pred)) }) do
local node = match[id]
if not node then
return
end
local start_row, start_col, end_row, end_col = node:range()
-- Don't trim if region ends in middle of a line
if end_col ~= 0 then
return
end
while true do
-- As we only care when end_col == 0, always inspect one line above end_row.
local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
if end_line ~= '' then
break
end
end_row = end_row - 1
end
-- If this produces an invalid range, we just skip it.
if start_row < end_row or (start_row == end_row and start_col <= end_col) then
if not metadata[id] then
metadata[id] = {}
end
metadata[id].range = { start_row, start_col, end_row, end_col }
end
end
end)

Some files were not shown because too many files have changed in this diff Show More