mirror of
https://github.com/chenasraf/nvim-treesitter.git
synced 2026-05-17 17:38:02 +00:00
feat!: drop modules, general refactor and cleanup
This commit is contained in:
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/test-queries.yml
vendored
2
.github/workflows/test-queries.yml
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
7
Makefile
7
Makefile
@@ -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
754
README.md
@@ -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`.**
|
||||
|
||||

|
||||
|
||||
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
305
SUPPORTED_LANGUAGES.md
Normal 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
34
TODO.md
Normal 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)
|
||||
BIN
assets/logo.png
BIN
assets/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
144
lua/nvim-treesitter/config.lua
Normal file
144
lua/nvim-treesitter/config.lua
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
11
lua/nvim-treesitter/init.lua
Normal file
11
lua/nvim-treesitter/init.lua
Normal 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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
255
lua/nvim-treesitter/shell_cmds.lua
Normal file
255
lua/nvim-treesitter/shell_cmds.lua
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
2
parser-info/.gitignore
vendored
2
parser-info/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
parser/.gitignore
vendored
2
parser/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
66
plugin/filetypes.lua
Normal file
66
plugin/filetypes.lua
Normal 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
|
||||
@@ -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
141
plugin/query_predicates.lua
Normal 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
Reference in New Issue
Block a user