1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-02 20:30:05 +08:00

chore(treesitter): use bundle treesitter

This commit is contained in:
wsdjeg 2022-04-14 12:01:23 +08:00
parent 4d04e3ff96
commit d5cf952778
620 changed files with 29164 additions and 1 deletions

View File

@ -13,7 +13,13 @@
function! SpaceVim#layers#treesitter#plugins() abort
let plugins = []
call add(plugins, ['nvim-treesitter/nvim-treesitter', {'do' : ':TSUpdate'}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/nvim-treesitter',
\ {
\ 'merged' : 0,
\ 'loadconf' : 1 ,
\ 'do' : 'TSUpdate',
\ 'loadconf_before' : 1
\ }])
return plugins
endfunction

View File

@ -0,0 +1,20 @@
root = true
[*]
indent_style = space
indent_size = 2
tab_width = 8
end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.lua]
tab_width = 2
[*.py]
indent_size = 4
tab_width = 4
[{Makefile,**/Makefile,runtime/doc/*.txt}]
indent_style = tab
indent_size = 8

View File

@ -0,0 +1,7 @@
/lua/nvim-treesitter/textobjects/ @theHamsta
/lua/nvim-treesitter/incremental_selection.lua @theHamsta
/lua/nvim-treesitter/fold.lua @vigoux
/lua/nvim-treesitter/highlight.lua @vigoux
/lua/nvim-treesitter/refactor/ @steelsojka

View File

@ -0,0 +1 @@
open_collective: "nvim-treesitter"

View File

@ -0,0 +1,58 @@
name: Bug report
description: Create a report to help us improve
labels: [ bug ]
body:
- type: markdown
attributes:
value: |
# Before reporting
Please do the following steps before reporting an issue.
- I have updated my neovim version to latest _master_
- I have updated my plugin to the latest version
- I have run `:TSUpdate`
- I have read the [troubleshooting section](https://github.com/nvim-treesitter/nvim-treesitter#troubleshooting)
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
- type: textarea
attributes:
label: Output of `:checkhealth nvim-treesitter`
render: markdown
validations:
required: true
- type: textarea
attributes:
label: Output of `nvim --version`
render: text
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,98 @@
name: Highlighting issue
description: Missing or incorrect highlights or you want to change the way something is highlighted
labels: [ highlights ]
body:
- type: markdown
attributes:
value: |
# Before reporting
Please perform the following steps before reporting an issue.
- I have updated my neovim version to latest _master_.
- I have updated my plugin to the latest version.
- I have run `:TSUpdate`.
- I have inspected the syntax tree using https://github.com/nvim-treesitter/playground and made sure
that no `ERROR` nodes are in the syntax tree. nvim-treesitter can not guarantee correct highlighting in the
presence of `ERROR`s -- in this case, please report the bug directly at corresponding parser's repository. (You can find all repository URLs in [README.md](https://github.com/nvim-treesitter/nvim-treesitter#supported-languages).)
- I have used `:TSHighlightCapturesUnderCursor` from https://github.com/nvim-treesitter/playground to inspect which highlight groups Neovim is using and that legacy syntax highlighting is not interfering (i.e., what you are observing is actual tree-sitter highlighting).
- type: textarea
attributes:
label: Describe the highlighting problem
description: A clear and concise description of what should be highlighted in a different way.
validations:
required: true
- type: textarea
attributes:
label: Example snippet that causes the problem
description: Please provide an example snippet in plain text that causes the problem.
validations:
required: true
- type: textarea
attributes:
label: Tree-sitter parsing result
description: |
Please provide the output of `:TSPlaygroundToggle` from https://github.com/nvim-treesitter/playground
(screenshot or plain text) with the following options enabled (pressing the key):
- `I` (name of the parsed language)
- `t` (toggle injected languages)
- `a` (show anonymous nodes)
placeholder: |
This should look somehow like this:
```
preproc_ifdef [0, 0] - [4, 6] cpp
"#ifdef" [0, 0] - [0, 6] cpp
name: identifier [0, 7] - [0, 17] cpp
preproc_def [1, 0] - [2, 0] cpp
"#define" [1, 0] - [1, 7] cpp
name: identifier [1, 8] - [1, 16] cpp
value: preproc_arg [1, 16] - [1, 27] cpp
"\n" [1, 27] - [2, 0] cpp
alternative: preproc_else [2, 0] - [4, 0] cpp
"#else" [2, 0] - [2, 5] cpp
preproc_def [3, 0] - [4, 0] cpp
"#define" [3, 0] - [3, 7] cpp
name: identifier [3, 8] - [3, 16] cpp
value: preproc_arg [3, 16] - [3, 29] cpp
```
validations:
required: true
- type: textarea
attributes:
label: Example screenshot
description: |
Please provide a screenshot of the current highlighting. Please also tell us the `:h colorscheme` you are using
and how to install it. If applicable, you can also upload a screenshot with the contents of
`:TSHighlightCapturesUnderCursor'.
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: |
A clear and concise description of what you expect to be changed. You can provide screenshot of
other editors or traditional Vim highlighting that don't show this problem or show a screenshot how
nvim-treesitter highlighting would look like when a problematic query would be removed/altered.
- type: textarea
attributes:
label: Output of `:checkhealth nvim-treesitter`
render: markdown
validations:
required: true
- type: textarea
attributes:
label: Output of `nvim --version`
render: text
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any context about the problem here.

View File

@ -0,0 +1,12 @@
---
name: Language request
about: Request for a new language to be supported
title: ''
labels: enhancement, good first issue, help wanted
assignees: ''
---
**Language information**
Please paste any useful information here !

View File

@ -0,0 +1,37 @@
pull_request_rules:
- name: Merge lockfile updates
conditions:
- "title=Update lockfile.json"
actions:
review:
type: APPROVE
message: Automatically approving lockfile updates
merge:
method: merge
- name: Prepare for merge
conditions:
- and:
- "-draft"
- "#approved-reviews-by=1"
- "#review-requested=0"
actions:
comment:
message: |
This PR is ready to be merged, and will be in 1 day if nothing happens before.
If you want other people to review your PR, request their reviews.
If you don't want this PR to be merged now, mark it as a Draft.
- name: Merge on approval
conditions:
- and:
- or:
- "#approved-reviews-by>=2"
- and:
- "#approved-reviews-by=1"
- "updated-at>=1 day ago"
- "-draft"
- "#review-requested=0"
actions:
merge:
method: rebase

View File

@ -0,0 +1,33 @@
name: Linting and style checking
on:
push:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
luacheck:
name: Luacheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Prepare
run: |
sudo apt-get update
sudo apt-get install luarocks -y
sudo luarocks install luacheck
- name: Run Luacheck
run: luacheck .
stylua:
name: StyLua
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Lint with stylua
uses: JohnnyMorganz/stylua-action@1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --check .

View File

@ -0,0 +1,92 @@
name: Test queries
on:
push:
branches:
- 'master'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- 'master'
# Cancel any in-progress CI runs for a PR if it is updated
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
check_compilation:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-2022, macos-latest]
cc: [ gcc, clang ]
nvim_tag: [ stable ]
exclude:
- os: ubuntu-latest
cc: clang
nvim_tag: stable
- os: macos-latest
cc: gcc
nvim_tag: stable
- os: windows-2022
cc: clang
nvim_tag: stable
include:
- os: windows-2022
cc: cl
nvim_tag: stable
- os: ubuntu-latest
cc: gcc
nvim_tag: nightly
name: Parser compilation
runs-on: ${{ matrix.os }}
env:
CC: ${{ matrix.cc }}
NVIM: ${{ matrix.os == 'windows-2022' && 'Neovim\\bin\\nvim.exe' || 'nvim' }}
steps:
- uses: actions/checkout@v2
- uses: ilammy/msvc-dev-cmd@v1
- uses: actions/setup-node@v2
- name: Install and prepare Neovim
env:
NVIM_TAG: ${{ matrix.nvim_tag }}
TREE_SITTER_CLI_TAG: v0.20.4
run: |
bash ./scripts/ci-install-${{ matrix.os }}.sh
- name: Setup Parsers Cache
id: parsers-cache
uses: actions/cache@v2
with:
path: |
./parser/
~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/
key: ${{ matrix.os }}-${{ matrix.cc }}-${{ matrix.nvim_tag }}-parsers-v1-${{ hashFiles('./lockfile.json', './lua/nvim-treesitter/parsers.lua', './lua/nvim-treesitter/install.lua', './lua/nvim-treesitter/shell_selectors.lua') }}
- name: Compile parsers
run: $NVIM --headless -c "lua require'nvim-treesitter.install'.prefer_git=false" -c "TSInstallSync all" -c "q"
- name: Post compile Windows
if: matrix.os == 'windows-2022'
run: cp -r ~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/* parser
# NOTE: this is a temporary workaround to skip swift tests on ubuntu
# stable and should be removed once neovim 0.7 is released.
- if: matrix.os == 'ubuntu-latest' && matrix.nvim_tag == 'stable'
run: echo "SKIP_SWIFT_CHECK=TRUE" >> $GITHUB_ENV
- name: Check query files
env:
SKIP_SWIFT_CHECK: ${{ env.SKIP_SWIFT_CHECK }}
run: $NVIM --headless -c "luafile ./scripts/check-queries.lua" -c "q"

View File

@ -0,0 +1,63 @@
name: Tests
on:
push:
branches:
- 'master'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- 'master'
# Cancel any in-progress CI runs for a PR if it is updated
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
jobs:
check_compilation:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
cc: [ gcc ]
name: Run tests
runs-on: ${{ matrix.os }}
env:
CC: ${{ matrix.cc }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- name: Test Dependencies
run: |
mkdir -p ~/.local/share/nvim/site/pack/plenary.nvim/start
cd ~/.local/share/nvim/site/pack/plenary.nvim/start
git clone https://github.com/nvim-lua/plenary.nvim
curl -L https://github.com/theHamsta/highlight-assertions/releases/download/v0.1.5/highlight-assertions_v0.1.5_x86_64-unknown-linux-gnu.tar.gz | tar -xz
cp highlight-assertions /usr/local/bin
- name: Install and prepare Neovim
env:
NVIM_TAG: stable
TREE_SITTER_CLI_TAG: v0.20.4
run: |
bash ./scripts/ci-install-${{ matrix.os }}.sh
- name: Setup Parsers Cache
id: parsers-cache
uses: actions/cache@v2
with:
path: |
./parser/
~/AppData/Local/nvim/pack/nvim-treesitter/start/nvim-treesitter/parser/
key: ${{ matrix.os }}-${{ matrix.cc }}-parsers-v1-${{ hashFiles('./lockfile.json', './lua/nvim-treesitter/parsers.lua', './lua/nvim-treesitter/install.lua', './lua/nvim-treesitter/shell_selectors.lua') }}
- name: Compile parsers Unix like
if: ${{ matrix.os != 'windows-latest' && steps.parsers-cache.outputs.cache-hit != 'true' }}
run: |
nvim --headless -c "TSInstallSync all" -c "q"
- name: Tests
run: PATH=/usr/local/bin:$PATH ./scripts/run_tests.sh

View File

@ -0,0 +1,55 @@
name: Update lockfile
on:
schedule:
- cron: '30 6 * * *'
push:
branches:
- master
jobs:
update-lockfile:
name: Update lockfile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: master
- name: Prepare
env:
NVIM_TAG: stable
run: |
wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
mv ./jq-linux64 /tmp/jq
chmod +x /tmp/jq
wget https://github.com/neovim/neovim/releases/download/${NVIM_TAG}/nvim.appimage
chmod u+x nvim.appimage
mkdir -p ~/.local/share/nvim/site/pack/nvim-treesitter/start
ln -s $(pwd) ~/.local/share/nvim/site/pack/nvim-treesitter/start
- name: Update parsers
env:
SKIP_LOCKFILE_UPDATE_FOR_LANGS: verilog,gleam,nix
run: |
./nvim.appimage --headless -c "luafile ./scripts/write-lockfile.lua" -c "q"
# Pretty print
cp lockfile.json /tmp/lockfile.json
cat /tmp/lockfile.json | /tmp/jq --sort-keys > lockfile.json
- name: Commit changes
run: |
git config user.name "GitHub"
git config user.email "noreply@github.com"
git add lockfile.json
git commit -m "Update lockfile.json" || echo 'No commit necessary!'
git clean -xf
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: Update lockfile.json
title: Update lockfile.json
branch: update-lockfile-pr
base: ${{ github.head_ref }}
draft: true

View File

@ -0,0 +1,40 @@
name: Update README
on:
push:
branches:
- master
jobs:
update-readme:
name: Update README
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Prepare
env:
NVIM_TAG: stable
run: |
wget https://github.com/neovim/neovim/releases/download/${NVIM_TAG}/nvim.appimage
chmod u+x nvim.appimage
mkdir -p ~/.local/share/nvim/site/pack/nvim-treesitter/start
ln -s $(pwd) ~/.local/share/nvim/site/pack/nvim-treesitter/start
- name: Check README
run: |
git config user.email "actions@github"
git config user.name "Github Actions"
./nvim.appimage --headless -c "luafile ./scripts/update-readme.lua" -c "q" || echo "Needs update"
git add README.md
git commit -m "Update README" || echo 'No commit necessary!'
git clean -xf
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: Update README
title: Update README
branch: update-readme-pr
base: ${{ github.head_ref }}
draft: true

4
bundle/nvim-treesitter/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
doc/tags
.luacheckcache
/tags
nvim.appimage

View File

@ -0,0 +1,21 @@
-- Rerun tests only if their modification time changed.
cache = true
codes = true
exclude_files = {
"tests/indent/lua/"
}
-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html
ignore = {
"212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off.
"411", -- Redefining a local variable.
"412", -- Redefining an argument.
"422", -- Shadowing an argument
"122" -- Indirectly setting a readonly global
}
-- Global objects defined by the C code
read_globals = {
"vim",
}

View File

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

View File

@ -0,0 +1 @@
tests/indent/lua/

View File

@ -0,0 +1,299 @@
# Contributing to `nvim-treesitter`
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 [Zulip]
server, so we can help you with any question you might have!
There is also a [Matrix channel] for tree-sitter support in Neovim.
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
Depending on which part of the plugin you want to contribute to, please read the appropriate section.
## Style Checks and Tests
We haven't implemented any functional tests yet. Feel free to contribute.
However, we check code style with `luacheck` and `stylua`!
Please install luacheck and activate our `pre-push` hook to automatically check style before
every push:
```bash
luarocks install luacheck
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
[Zulip] or on the "Neovim tree-sitter" [Matrix channel].
## Parser configurations
Contributing to parser configurations is basically modifying one of the `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).
Each query has an appropriate name, which is then used by modules to extract data from the syntax tree.
For now these are the types of queries used by `nvim-treesitter`:
- `highlights.scm`: used for syntax highlighting, using the `highlight` module.
- `locals.scm`: used to extract keyword definitions, scopes, references, etc, using the `locals` module.
- `textobjects.scm`: used to define text objects.
- `folds.scm`: used to define folds.
- `injections.scm`: used to define injections.
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://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries),
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/)
- Matches in the bottom will override queries that are above of them.
If your language is an extension of a language (TypeScript is an extension of JavaScript for
example), you can include the queries from your base language by adding the following _as the first
line of your file_.
```query
; inherits: lang1,(optionallang)
```
If you want to inherit a language, but don't want the languages inheriting from yours to inherit it,
you can mark the language as optional (by putting it between parenthesis).
### Highlights
As languages differ quite a lot, here is a set of captures available to you when building a `highlights.scm` query.
One important thing to note is that many of these capture groups are not supported by `neovim` for now, and will not have any
effect on highlighting. We will work on improving highlighting in the near future though.
#### Misc
```
@comment
@debug
@error for error `ERROR` nodes.
@none to disable completely the highlight
@preproc
@punctuation.delimiter for `;` `.` `,`
@punctuation.bracket for `()` or `{}`
@punctuation.special for symbols with special meaning like `{}` in string interpolation.
```
#### Constants
```
@constant
@constant.builtin
@constant.macro
@string
@string.regex
@string.escape
@string.special
@character
@character.special
@number
@boolean
@float
```
#### Functions
```
@function
@function.builtin
@function.macro
@parameter
@method
@field
@property
@constructor
```
#### Keywords
```
@conditional (e.g. `if`, `else`)
@repeat (e.g. `for`, `while`)
@label for C/Lua-like labels
@keyword
@keyword.function (keyword to define a function, e.g. `func` in Go, `def` in Python)
@keyword.operator (for operators that are English words, e.g. `and`, `or`)
@keyword.return
@operator (for symbolic operators, e.g. `+`, `*`)
@exception (e.g. `throw`, `catch`)
@include keywords for including modules (e.g. import/from in Python)
@storageclass
@type
@type.builtin
@type.definition
@type.qualifier
@namespace for identifiers referring to namespaces
@symbol for identifiers referring to symbols
@attribute for e.g. Python decorators
```
@conceal followed by `(#set! conceal "")` for captures that are not used for highlights but only for concealing.
#### Variables
```
@variable
@variable.builtin
```
#### Text
Mainly for markup languages.
```
@text
@text.strong
@text.emphasis
@text.underline
@text.strike
@text.title
@text.literal
@text.uri
@text.math (e.g. for LaTeX math environments)
@text.environment (e.g. for text environments of markup languages)
@text.environment.name (e.g. for the name/the string indicating the type of text environment)
@text.reference (for footnotes, text references, citations)
@text.note
@text.warning
@text.danger
@todo
```
#### Tags
Used for xml-like tags
```
@tag
@tag.attribute
@tag.delimiter
```
#### Conceal
@conceal followed by `(#set! conceal "")` for captures that are not used for highlights but only for concealing.
### Locals
```
@definition for various definitions
@definition.constant
@definition.function
@definition.method
@definition.var
@definition.parameter
@definition.macro
@definition.type
@definition.field
@definition.enum
@definition.namespace for modules or C++ namespaces
@definition.import for imported names
@definition.associated to determine the type of a variable
@definition.doc for documentation adjacent to a definition. E.g.
@scope
@reference
@constructor
```
#### Definition Scope
You can set the scope of a definition by setting the `scope` property on the definition.
For example, a javascript function declaration creates a scope. The function name is captured as the definition.
This means that the function definition would only be available WITHIN the scope of the function, which is not the case.
The definition can be used in the scope the function was defined in.
```javascript
function doSomething() {}
doSomething(); // Should point to the declaration as the definition
```
```query
(function_declaration
((identifier) @definition.var)
(#set! "definition.var.scope" "parent"))
```
Possible scope values are:
- `parent`: The definition is valid in the containing scope and one more scope above that scope
- `global`: The definition is valid in the root scope
- `local`: The definition is valid in the containing scope. This is the default behavior
### Folds
You can define folds for a given language by adding a `folds.scm` query :
```
@fold
```
If the `fold.scm` query is not present, this will fallback to the `@scope` captures in the `locals`
query.
### Injections
Some captures are related to language injection (like markdown code blocks). They are used in `injections.scm`.
You can directly use the name of the language that you want to inject (e.g. `@html` to inject html).
If you want to dynamically detect the language (e.g. for Markdown blocks) use the `@language` to capture
the node describing the language and `@content` to describe the injection region.
```
@{language} ; e.g. @html to describe a html region
@language ; dynamic detection of the injection language (i.e. the text of the captured node describes the language).
@content ; region for the dynamically detected language.
@combined ; This will combine all matches of a pattern as one single block of content.
```
### Indents
```
@indent ; Indent children when matching this node
@indent_end ; Marks the end of indented block
@aligned_indent ; Behaves like python aligned/hanging indent
@dedent ; Dedent children when matching this node
@branch ; Dedent itself when matching this node
@ignore ; Do not indent in this node
@auto ; Behaves like 'autoindent' buffer option
@zero_indent ; Sets this node at position 0 (no indent)
```
[Zulip]: https://nvim-treesitter.zulipchat.com
[Matrix channel]: https://matrix.to/#/#nvim-treesitter:matrix.org

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,569 @@
<div align="center">
<h1>nvim-treesitter</h1>
<p>
<a href="https://nvim-treesitter.zulipchat.com/">
<img alt="Zulip Chat" src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" />
</a>
<a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Linting+and+style+checking%22+branch%3Amaster">
<img alt="Linting and Style" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Linting%20and%20style%20checking/badge.svg" />
</a>
<a href="https://github.com/nvim-treesitter/nvim-treesitter/actions?query=workflow%3A%22Check+loading+of+syntax+files%22+branch%3Amaster">
<img alt="Syntax files" src="https://github.com/nvim-treesitter/nvim-treesitter/workflows/Check%20loading%20of%20syntax%20files/badge.svg" />
</a>
</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>
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:
![cpp example](assets/example-cpp.png)
Traditional highlighting (left) vs Treesitter-based highlighting (right).
More examples can be found in [our gallery](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Gallery).
**Warning: Treesitter and nvim-treesitter highlighting are an experimental feature of Neovim.
Please consider the experience with this plug-in as experimental until Tree-Sitter support in Neovim is stable!
We recommend using the nightly builds of Neovim if possible.
You can find the current roadmap [here](https://github.com/nvim-treesitter/nvim-treesitter/projects/1).
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)
---
# Quickstart
## Requirements
- Neovim latest stable version or [nightly](https://github.com/neovim/neovim#install-from-source)
- `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)).
## 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 `:help lua-heredoc`.
All modules are disabled by default and need to be activated explicitly in your `init.lua`, e.g., via
```lua
require'nvim-treesitter.configs'.setup {
-- A list of parser names, or "all"
ensure_installed = { "c", "lua", "rust" },
-- Install parsers synchronously (only applied to `ensure_installed`)
sync_install = false,
-- List of parsers to ignore installing (for "all")
ignore_install = { "javascript" },
highlight = {
-- `false` will disable the whole extension
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" },
-- 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,
},
}
```
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.
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] [astro](https://github.com/virchau13/tree-sitter-astro) (maintained by @virchau13)
- [x] [bash](https://github.com/tree-sitter/tree-sitter-bash) (maintained by @TravonteD)
- [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] [c](https://github.com/tree-sitter/tree-sitter-c) (maintained by @vigoux)
- [x] [c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) (maintained by @Luxed)
- [x] [clojure](https://github.com/sogaiu/tree-sitter-clojure) (maintained by @sogaiu)
- [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] [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] [cuda](https://github.com/theHamsta/tree-sitter-cuda) (maintained by @theHamsta)
- [x] [d](https://github.com/CyberShadow/tree-sitter-d) (experimental, maintained by @nawordar)
- [x] [dart](https://github.com/UserNobody14/tree-sitter-dart) (maintained by @Akin909)
- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar)
- [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] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay)
- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @jonatanklosko, @connorlay)
- [ ] [elm](https://github.com/elm-tooling/tree-sitter-elm)
- [x] [elvish](https://github.com/ckafi/tree-sitter-elvish) (maintained by @ckafi)
- [x] [erlang](https://github.com/AbstractMachinesLab/tree-sitter-erlang) (maintained by @ostera)
- [x] [fennel](https://github.com/travonted/tree-sitter-fennel) (maintained by @TravonteD)
- [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)
- [ ] [fortran](https://github.com/stadelmanma/tree-sitter-fortran)
- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn)
- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @Shatur95)
- [x] [gleam](https://github.com/J3RN/tree-sitter-gleam) (maintained by @connorlay)
- [x] [Glimmer and Ember](https://github.com/alexlafroscia/tree-sitter-glimmer) (maintained by @alexlafroscia)
- [x] [glsl](https://github.com/theHamsta/tree-sitter-glsl) (maintained by @theHamsta)
- [x] [go](https://github.com/tree-sitter/tree-sitter-go) (maintained by @theHamsta, @WinWisely268)
- [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] [gowork](https://github.com/omertuc/tree-sitter-go-work) (maintained by @omertuc)
- [x] [graphql](https://github.com/bkegley/tree-sitter-graphql) (maintained by @bkegley)
- [ ] [hack](https://github.com/slackhq/tree-sitter-hack)
- [ ] [haskell](https://github.com/tree-sitter/tree-sitter-haskell)
- [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] [help](https://github.com/vigoux/tree-sitter-vimdoc) (experimental, maintained by @vigoux)
- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410)
- [x] [hocon](https://github.com/antosha417/tree-sitter-hocon) (maintained by @antosha417)
- [x] [html](https://github.com/tree-sitter/tree-sitter-html) (maintained by @TravonteD)
- [x] [http](https://github.com/NTBBloodbath/tree-sitter-http) (maintained by @NTBBloodbath)
- [x] [java](https://github.com/tree-sitter/tree-sitter-java) (maintained by @p00f)
- [x] [javascript](https://github.com/tree-sitter/tree-sitter-javascript) (maintained by @steelsojka)
- [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] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @mroavi, @theHamsta)
- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa)
- [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] [llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) (maintained by @benwilliamgraham)
- [x] [lua](https://github.com/MunifTanjim/tree-sitter-lua) (maintained by @muniftanjim)
- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991)
- [ ] [markdown](https://github.com/MDeiml/tree-sitter-markdown)
- [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, @mrossinek)
- [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] [pascal](https://github.com/Isopod/tree-sitter-pascal.git) (maintained by @isopod)
- [x] [perl](https://github.com/ganezdragon/tree-sitter-perl) (maintained by @ganezdragon)
- [x] [php](https://github.com/tree-sitter/tree-sitter-php) (maintained by @tk-shirasaka)
- [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] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva)
- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (maintained by @zealot128)
- [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] [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 @jimhester)
- [x] [rasi](https://github.com/Fymyte/tree-sitter-rasi) (maintained by @Fymyte)
- [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] [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] [rust](https://github.com/tree-sitter/tree-sitter-rust) (maintained by @vigoux)
- [x] [scala](https://github.com/tree-sitter/tree-sitter-scala) (maintained by @stevanmilic)
- [x] [scheme](https://github.com/6cdh/tree-sitter-scheme) (maintained by @6cdh)
- [x] [scss](https://github.com/serenadeai/tree-sitter-scss) (maintained by @elianiva)
- [x] [slint](https://github.com/jrmoulton/tree-sitter-slint) (experimental, maintained by @jrmoulton)
- [x] [solidity](https://github.com/YongJieYongJie/tree-sitter-solidity) (maintained by @YongJieYongJie)
- [x] [sparql](https://github.com/BonaBeavis/tree-sitter-sparql) (maintained by @bonabeavis)
- [x] [supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) (maintained by @madskjeldgaard)
- [x] [surface](https://github.com/connorlay/tree-sitter-surface) (maintained by @connorlay)
- [x] [svelte](https://github.com/Himujjal/tree-sitter-svelte) (maintained by @elianiva)
- [ ] [swift](https://github.com/alex-pinkus/tree-sitter-swift)
- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce)
- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer, @susliko)
- [x] [todotxt](https://github.com/arnarg/tree-sitter-todotxt.git) (experimental, maintained by @arnarg)
- [x] [toml](https://github.com/ikatyang/tree-sitter-toml) (maintained by @tk-shirasaka)
- [x] [tsx](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka)
- [x] [turtle](https://github.com/BonaBeavis/tree-sitter-turtle) (maintained by @bonabeavis)
- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka)
- [x] [vala](https://github.com/matbme/tree-sitter-vala) (maintained by @matbme)
- [x] [verilog](https://github.com/tree-sitter/tree-sitter-verilog) (experimental, maintained by @zegervdv)
- [x] [vim](https://github.com/vigoux/tree-sitter-viml) (maintained by @vigoux)
- [x] [vue](https://github.com/ikatyang/tree-sitter-vue) (maintained by @WhyNotHugo)
- [x] [wgsl](https://github.com/szebniok/tree-sitter-wgsl) (maintained by @szebniok)
- [x] [yaml](https://github.com/ikatyang/tree-sitter-yaml) (maintained by @stsewd)
- [x] [yang](https://github.com/Hubro/tree-sitter-yang) (maintained by @Hubro)
- [x] [zig](https://github.com/maxxnino/tree-sitter-zig) (maintained by @maxxnino)
<!--parserinfo-->
# Available modules
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).
#### Highlight
Consistent syntax highlighting.
```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,
},
}
```
You can add custom highlight captures with:
```vim
lua <<EOF
require"nvim-treesitter.highlight".set_custom_captures {
-- Highlight the @foo.bar capture group with the "Identifier" highlight group.
["foo.bar"] = "Identifier",
}
EOF
```
#### 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",
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. *(Technically not a module because it's per windows and not per buffer.)*
```vim
set foldmethod=expr
set foldexpr=nvim_treesitter#foldexpr()
```
This will respect your `foldminlines` and `foldnestmax` settings.
# Advanced setup
## Adding parsers
If you have a parser that is not on the list of supported languages (either as a repository on Github or in a local directory), you can add it manually for use by `nvim-treesitter` as follows:
1. Clone the repository or [create a new project](https://tree-sitter.github.io/tree-sitter/creating-parsers#project-setup) in, say, `~/projects/tree-sitter-zimbu`. Make sure that the `tree-sitter-cli` executable is installed and in your path; see <https://tree-sitter.github.io/tree-sitter/creating-parsers#installation> for installation instructions.
2. Run `tree-sitter generate` in this directory (followed by `tree-sitter test` for good measure).
3. Add the following snippet to your `init.lua`:
```lua
local parser_config = require "nvim-treesitter.parsers".get_parser_configs()
parser_config.zimbu = {
install_info = {
url = "~/projects/tree-sitter-zimbu", -- local path or git repo
files = {"src/parser.c"},
-- optional entries:
branch = "main", -- default branch in case of git repo if different from master
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 wish to set a specific parser for a filetype, you should extend the `filetype_to_parsername` table:
```lua
local ft_to_parser = require"nvim-treesitter.parsers".filetype_to_parsername
ft_to_parser.someft = "python" -- the someft filetype will use the python parser and queries.
```
4. 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()) (0.7.0 and above) 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).
All queries found in the runtime directories will be combined.
By convention, if you want to write a query, use the `queries/` directory,
but if you want to extend a query use the `after/queries/` directory.
If you want to completely override a query, you can use `:h set_query()`.
For example, to override the `injections` queries from `c` with your own:
```lua
require("vim.treesitter.query").set_query("c", "injections", "(comment) @comment")
```
Note: when using `set_query`, 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}`...
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.
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).
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.
#### I get `Error detected while processing .../plugin/nvim-treesitter.vim` every time I open Neovim
This is probably due to a change in a parser's grammar or its queries.
Try updating the parser that you suspect has changed (`:TSUpdate {language}`) or all of them (`:TSUpdate`).
If the error persists after updating all parsers,
please [open an issue](https://github.com/nvim-treesitter/nvim-treesitter/issues/new/choose).
#### I get `query error: invalid node type at position`
This could be due a query file outside this plugin using outdated nodes,
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.
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:
```lua
require("nvim-treesitter.install").prefer_git = true
```
#### I want to use a HTTP proxy for downloading the parsers
You can either configure curl to use additional CLI arguments in your Lua config:
```lua
require("nvim-treesitter.install").command_extra_args = {
curl = { "--proxy", "<proxy url>" },
}
```
or you can configure git via `.gitconfig` and use git instead of curl
```lua
require("nvim-treesitter.install").prefer_git = true
```
#### I want to use a mirror instead of "https://github.com/"
In your Lua config:
```lua
for _, config in pairs(require("nvim-treesitter.parsers").get_parser_configs()) do
config.install_info.url = config.install_info.url:gsub("https://github.com/", "something else")
end
require'nvim-treesitter.configs'.setup {
--
--
}
```

View File

@ -0,0 +1 @@
setlocal commentstring=#\ %s

View File

@ -0,0 +1 @@
setlocal commentstring=#\ %s

View File

@ -0,0 +1 @@
setlocal commentstring=<%#\ %s\ %>

View File

@ -0,0 +1 @@
setlocal commentstring=;\ %s

View File

@ -0,0 +1 @@
setlocal commentstring={!--\ %s\ --}

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,3 @@
function! health#nvim_treesitter#check()
lua require 'nvim-treesitter.health'.check()
endfunction

View File

@ -0,0 +1,27 @@
function! nvim_treesitter#statusline(...) abort
return luaeval("require'nvim-treesitter'.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', 'maintained'], "\n")
endfunction
function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all', 'maintained'], "\n")
endfunction
function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n")
endfunction
function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort
return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n")
endfunction
function! nvim_treesitter#indent() abort
return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum))
endfunction

View File

@ -0,0 +1,775 @@
*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim.
Minimum version of neovim: nightly
Authors:
Kiyan Yazdani <yazdani.kiyan@protonmail.com>
Thomas Vigouroux <tomvig38@gmail.com>
Stephan Seitz <stephan.seitz@fau.de>
Steven Sojka <Steven.Sojka@tdameritrade.com>
Santos Gallegos <stsewd@protonmail.com>
https://github.com/nvim-treesitter/nvim-treesitter/graphs/contributors
Type |gO| to see the table of contents.
==============================================================================
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.
==============================================================================
QUICK START *nvim-treesitter-quickstart*
Install the parser for your language
>
:TSInstall {language}
<
To get a list of supported languages
>
:TSInstallInfo
<
By default, everything is disabled.
To enable supported features, put this in your `init.lua` file:
>
require'nvim-treesitter.configs'.setup {
-- A list of parser names, or "all"
ensure_installed = { "c", "lua", "rust" },
-- Install parsers synchronously (only applied to `ensure_installed`)
sync_install = false,
-- List of parsers to ignore installing (for "all")
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,
},
}
<
See |nvim-treesitter-modules| for a list of all available modules and its options.
==============================================================================
MODULES *nvim-treesitter-modules*
|nvim-treesitter| provides several functionalities via modules (and submodules),
each module makes use of the query files defined for each 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.
>
require'nvim-treesitter.configs'.setup {
-- Modules and its options go here
highlight = { enable = true },
incremental_selection = { enable = true },
textobjects = { enable = true },
}
<
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.
>
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
disable = { "cpp", "lua" },
},
}
<
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:
>
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,
},
}
<
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.
External plugins can provide their own modules with their own options,
those can also be configured using the `nvim-treesitter.configs.setup`
function.
------------------------------------------------------------------------------
HIGHLIGHT *nvim-treesitter-highlight-mod*
Consistent syntax highlighting.
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 based on treesitter for the |=| operator.
NOTE: this is an experimental feature.
Query files: `indents.scm`.
Supported options:
- enable: `true` or `false`.
- disable: list of languages.
>
require'nvim-treesitter.configs'.setup {
indent = {
enable = true
},
}
<
==============================================================================
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 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.get_node_text*
get_node_text(node, bufnr)~
Returns the text content of a `node`.
*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.get_node_range*
get_node_range(node_or_range)~
Get the range from either a node or a 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) return line:gsub('%s*[%[%(%{]*%s*$', '') end,
separator = ' -> '
}
<
- `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 removes opening brackets and spaces from end.
- `separator` - Separator between nodes.
*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, hiting `zx` should fix folding.
In any case, feel free to open an issue with the reproducing steps.
==============================================================================
HIGHLIGHTS *nvim-treesitter-highlights*
The following is a list of highlights groups, the syntactic elements they
apply to, and some examples.
*hl-TSAttribute*
`TSAttribute`
Annotations that can be attached to the code to denote some kind of meta
information. e.g. C++/Dart attributes.
*hl-TSBoolean*
`TSBoolean`
Boolean literals: `True` and `False` in Python.
*hl-TSCharacter*
`TSCharacter`
Character literals: `'a'` in C.
*hl-TSCharacterSpecial*
`TSCharacterSpecial`
Special characters.
*hl-TSComment*
`TSComment`
Line comments and block comments.
*hl-TSConditional*
`TSConditional`
Keywords related to conditionals: `if`, `when`, `cond`, etc.
*hl-TSConstant*
`TSConstant`
Constants identifiers. These might not be semantically constant.
E.g. uppercase variables in Python.
*hl-TSConstBuiltin*
`TSConstBuiltin`
Built-in constant values: `nil` in Lua.
*hl-TSConstMacro*
`TSConstMacro`
Constants defined by macros: `NULL` in C.
*hl-TSConstructor*
`TSConstructor`
Constructor calls and definitions: `{}` in Lua, and Java constructors.
*hl-TSDebug*
`TSDebug`
Debugging statements.
*hl-TSDefine*
`TSDefine`
Preprocessor #define statements.
*hl-TSError*
`TSError`
Syntax/parser errors. This might highlight large sections of code while the
user is typing still incomplete code, use a sensible highlight.
*hl-TSException*
`TSException`
Exception related keywords: `try`, `except`, `finally` in Python.
*hl-TSField*
`TSField`
Object and struct fields.
*hl-TSFloat*
`TSFloat`
Floating-point number literals.
*hl-TSFunction*
`TSFunction`
Function calls and definitions.
*hl-TSFuncBuiltin*
`TSFuncBuiltin`
Built-in functions: `print` in Lua.
*hl-TSFuncMacro*
`TSFuncMacro`
Macro defined functions (calls and definitions): each `macro_rules` in
Rust.
*hl-TSInclude*
`TSInclude`
File or module inclusion keywords: `#include` in C, `use` or `extern crate` in
Rust.
*hl-TSKeyword*
`TSKeyword`
Keywords that don't fit into other categories.
*hl-TSKeywordFunction*
`TSKeywordFunction`
Keywords used to define a function: `function` in Lua, `def` and `lambda` in
Python.
*hl-TSKeywordOperator*
`TSKeywordOperator`
Unary and binary operators that are English words: `and`, `or` in Python;
`sizeof` in C.
*hl-TSKeywordReturn*
`TSKeywordReturn`
Keywords like `return` and `yield`.
*hl-TSLabel*
`TSLabel`
GOTO labels: `label:` in C, and `::label::` in Lua.
*hl-TSMethod*
`TSMethod`
Method calls and definitions.
*hl-TSNamespace*
`TSNamespace`
Identifiers referring to modules and namespaces.
*hl-None*
`TSNone`
No highlighting (sets all highlight arguments to `NONE`). this group is used
to clear certain ranges, for example, string interpolations. Don't change the
values of this highlight group.
*hl-TSNumber*
`TSNumber`
Numeric literals that don't fit into other categories.
*hl-TSOperator*
`TSOperator`
Binary or unary operators: `+`, and also `->` and `*` in C.
*hl-TSParameter*
`TSParameter`
Parameters of a function.
*hl-TSParameterReference*
`TSParameterReference`
References to parameters of a function.
*hl-TSPreProc*
`TSPreProc`
Preprocessor #if, #else, #endif, etc.
*hl-TSProperty*
`TSProperty`
Same as `TSField`.
*hl-TSPunctDelimiter*
`TSPunctDelimiter`
Punctuation delimiters: Periods, commas, semicolons, etc.
*hl-TSPunctBracket*
`TSPunctBracket`
Brackets, braces, parentheses, etc.
*hl-TSPunctSpecial*
`TSPunctSpecial`
Special punctuation that doesn't fit into the previous categories.
*hl-TSRepeat*
`TSRepeat`
Keywords related to loops: `for`, `while`, etc.
*hl-StorageClass*
`TSStorageClass`
Keywords that affect how a variable is stored: `static`, `comptime`, `extern`,
etc.
*hl-TSString*
`TSString`
String literals.
*hl-TSStringRegex*
`TSStringRegex`
Regular expression literals.
*hl-TSStringEscape*
`TSStringEscape`
Escape characters within a string: `\n`, `\t`, etc.
*hl-TSStringSpecial*
`TSStringSpecial`
Strings with special meaning that don't fit into the previous categories.
*hl-TSSymbol*
`TSSymbol`
Identifiers referring to symbols or atoms.
*hl-TSTag*
`TSTag`
Tags like HTML tag names.
*hl-TSTagAttribute*
`TSTagAttribute`
HTML tag attributes.
*hl-TSTagDelimiter*
`TSTagDelimiter`
Tag delimiters like `<` `>` `/`.
*hl-TSText*
`TSText`
Non-structured text. Like text in a markup language.
*hl-TSSTrong*
`TSStrong`
Text to be represented in bold.
*hl-TSEmphasis*
`TSEmphasis`
Text to be represented with emphasis.
*hl-TSUnderline*
`TSUnderline`
Text to be represented with an underline.
*hl-TSStrike*
`TSStrike`
Strikethrough text.
*hl-TSTitle*
`TSTitle`
Text that is part of a title.
*hl-TSLiteral*
`TSLiteral`
Literal or verbatim text.
*hl-TSURI*
`TSURI`
URIs like hyperlinks or email addresses.
*hl-TSMath*
`TSMath`
Math environments like LaTeX's `$ ... $`
*hl-TSTextReference*
`TSTextReference`
Footnotes, text references, citations, etc.
*hl-TSEnvironment*
`TSEnvironment`
Text environments of markup languages.
*hl-TSEnvironmentName*
`TSEnvironmentName`
Text/string indicating the type of text environment. Like the name of a
`\begin` block in LaTeX.
*hl-TSNote*
`TSNote`
Text representation of an informational note.
*TSWarning*
`TSWarning`
Text representation of a warning note.
*TSDanger*
`TSDanger`
Text representation of a danger note.
*hl-TSTodo*
`TSTodo`
Anything that needs extra attention, such as keywords like TODO or FIXME.
*hl-TSType*
`TSType`
Type (and class) definitions and annotations.
*hl-TSTypeBuiltin*
`TSTypeBuiltin`
Built-in types: `i32` in Rust.
*hl-TSTypeQualifier*
`TSTypeQualifier`
Qualifiers on types, e.g. `const` or `volatile` in C or `mut` in Rust.
*hl-TSTypeDefinition*
`TSTypeDefinition`
Type definitions, e.g. `typedef` in C.
*hl-TSVariable*
`TSVariable`
Variable names that don't fit into other categories.
*hl-TSVariableBuiltin*
`TSVariableBuiltin`
Variable names defined by the language: `this` or `self` in Javascript.
==============================================================================
PERFORMANCE *nvim-treesitter-performance*
`nvim-treesitter` checks the 'runtimepath' on startup in order to discover
available parsers and queries and index them. As a consequence, a very long
'runtimepath' might result in delayed startup times.
vim:tw=78:ts=8:expandtab:noet:ft=help:norl:

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.cook set filetype=cooklang

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.fusion setfiletype fusion

View File

@ -0,0 +1,2 @@
autocmd BufRead,BufNewFile *.tscn setlocal ft=gdresource
autocmd BufRead,BufNewFile *.tres setlocal ft=gdresource

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.gd set ft=gdscript

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.hbs set ft=handlebars

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.glsl set filetype=glsl

View File

@ -0,0 +1 @@
au BufRead,BufNewFile go.work set filetype=gowork

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.graphql,*.graphqls,*.gql setfiletype graphql

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.hack,*.hackpartial setfiletype hack

View File

@ -0,0 +1,2 @@
autocmd BufRead,BufNewFile *.hcl set filetype=hcl
autocmd BufRead,BufNewFile *.tf,*.tfvars set filetype=terraform

View File

@ -0,0 +1 @@
au BufRead,BufNewFile *.heex set filetype=heex

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.hjson set filetype=hjson

View File

@ -0,0 +1 @@
autocmd BufNewFile,BufRead *.json5 set ft=json5

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.ldg,*.ledger,*.journal setfiletype ledger

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.nix setfiletype nix

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.prisma set filetype=prisma

View File

@ -0,0 +1 @@
au BufRead,BufNewFile *.pug setlocal filetype=pug

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.ql,*.qll setfiletype ql

View File

@ -0,0 +1,11 @@
" Last Change: 2020 Sep 01
function! s:shouldFt(path)
let l:q_dir = fnamemodify(a:path, ":p:h:h:t")
if l:q_dir =~? "queries"
setlocal ft=query
endif
endfunction
autocmd BufNewFile,BufRead *.scm call s:shouldFt(expand("%"))

View File

@ -0,0 +1 @@
au BufRead,BufNewFile *.sface set filetype=surface

View File

@ -0,0 +1 @@
autocmd BufRead,BufNewFile *.tl setfiletype teal

View File

@ -0,0 +1 @@
au BufRead,BufNewFile *.tla set filetype=tla

View File

@ -0,0 +1 @@
au BufRead,BufNewFile *.yang set filetype=yang

View File

@ -0,0 +1,332 @@
{
"astro": {
"revision": "6602ccbf9a5f79845dad62f1e118e07f97e3533c"
},
"bash": {
"revision": "275effdfc0edce774acf7d481f9ea195c6c403cd"
},
"beancount": {
"revision": "78b8ddca3ab774573a4e3bf64eabd79e9452cea9"
},
"bibtex": {
"revision": "ccfd77db0ed799b6c22c214fe9d2937f47bc8b34"
},
"c": {
"revision": "e348e8ec5efd3aac020020e4af53d2ff18f393a9"
},
"c_sharp": {
"revision": "5b6ae1f88e741b9ed738891ad1362fb9f2041671"
},
"clojure": {
"revision": "879f0e726295807d917d576fcf9e1e432c4c20fc"
},
"cmake": {
"revision": "5020572408a386d5d2dfac3516584f5edda7a49b"
},
"comment": {
"revision": "a37ca370310ac6f89b6e0ebf2b86b2219780494e"
},
"commonlisp": {
"revision": "c7e814975ab0d0d04333d1f32391c41180c58919"
},
"cooklang": {
"revision": "5e113412aadb78955c27010daa4dbe1d202013cf"
},
"cpp": {
"revision": "a832195eb3685a279856bb480ce19cff19554b6d"
},
"css": {
"revision": "a03f1d2d1dfbf6f8e0fdca5f9ff030228241eb57"
},
"cuda": {
"revision": "5178a7a5b25dc7ee4a69bf72f31bd6d3ff0a0795"
},
"d": {
"revision": "c2fbf21bd3aa45495fe13247e040ad5815250032"
},
"dart": {
"revision": "6a25376685d1d47968c2cef06d4db8d84a70025e"
},
"devicetree": {
"revision": "877adbfa0174d25894c40fa75ad52d4515a36368"
},
"dockerfile": {
"revision": "25c71d6a24cdba8f0c74ef40d4d2d93defd7e196"
},
"dot": {
"revision": "917230743aa10f45a408fea2ddb54bbbf5fbe7b7"
},
"eex": {
"revision": "f742f2fe327463335e8671a87c0b9b396905d1d1"
},
"elixir": {
"revision": "1dabc1c790e07115175057863808085ea60dd08a"
},
"elm": {
"revision": "9835f900e4df40277f0701d1bc0b6cee0853f81b"
},
"elvish": {
"revision": "e50787cadd3bc54f6d9c0704493a79078bb8a4e5"
},
"erlang": {
"revision": "fab680273af1a8f5cc0c3a0c62cbf5b1bea71f39"
},
"fennel": {
"revision": "d37fd84a702b78ff0282f223ece83c61ab062a6e"
},
"fish": {
"revision": "d482d70ea8e191c05b2c1b613ed6fdff30a14da0"
},
"foam": {
"revision": "fdb7f14b885abfc4df57728c9b2a2f2ad24d3cb7"
},
"fortran": {
"revision": "f0f2f100952a353e64e26b0fa710b4c296d7af13"
},
"fusion": {
"revision": "19db2f47ba4c3a0f6238d4ae0e2abfca16e61dd6"
},
"gdscript": {
"revision": "2a6abdaa47fcb91397e09a97c7433fd995ea46c6"
},
"gleam": {
"revision": "cfcbca3f8f734773878e00d7bfcedea98eb10be2"
},
"glimmer": {
"revision": "5caf63ac0e3c549b90c9aeb042871206b4b7bc16"
},
"glsl": {
"revision": "ffb93961426926554a0ba4a389ea6e9d6fafdea9"
},
"go": {
"revision": "c8fed1f0847a65a04a4b8cb7655f5f416e0742ca"
},
"godot_resource": {
"revision": "b6ef0768711086a86b3297056f9ffb5cc1d77b4a"
},
"gomod": {
"revision": "e8f51f8e4363a3d9a427e8f63f4c1bbc5ef5d8d0"
},
"gowork": {
"revision": "6dd9dd79fb51e9f2abc829d5e97b15015b6a8ae2"
},
"graphql": {
"revision": "5e66e961eee421786bdda8495ed1db045e06b5fe"
},
"hack": {
"revision": "d434b4e6c9af0524b163d7a912c8ce79d2b1f778"
},
"haskell": {
"revision": "ed976b81b00ce7b72b99bca75e7a616cc526220c"
},
"hcl": {
"revision": "d559c46ba170808b23a73da0cf49f315d221d095"
},
"heex": {
"revision": "592e22292a367312c35e13de7fdb888f029981d6"
},
"help": {
"revision": "41ac50500e68fadc139737bbee2a0618628b822b"
},
"hjson": {
"revision": "02fa3b79b3ff9a296066da6277adfc3f26cbc9e0"
},
"hocon": {
"revision": "bb412e2633f4a3611a4e16efe58d917093bb4782"
},
"html": {
"revision": "161a92474a7bb2e9e830e48e76426f38299d99d1"
},
"http": {
"revision": "bfddd16b1cf78e0042fd1f6846a179f76a254e20"
},
"java": {
"revision": "881b84fe7078651af5077cc4cea4c85f9fddde3b"
},
"javascript": {
"revision": "fdeb68ac8d2bd5a78b943528bb68ceda3aade2eb"
},
"jsdoc": {
"revision": "189a6a4829beb9cdbe837260653b4a3dfb0cc3db"
},
"json": {
"revision": "203e239408d642be83edde8988d6e7b20a19f0e8"
},
"json5": {
"revision": "5dd5cdc418d9659682556b6adca2dd9ace0ac6d2"
},
"jsonc": {
"revision": "02b01653c8a1c198ae7287d566efa86a135b30d5"
},
"julia": {
"revision": "12ea597262125fc22fd2e91aa953ac69b19c26ca"
},
"kotlin": {
"revision": "a4f71eb9b8c9b19ded3e0e9470be4b1b77c2b569"
},
"lalrpop": {
"revision": "7744b56f03ac1e5643fad23c9dd90837fe97291e"
},
"latex": {
"revision": "ab9c1c59de5da1a8d8c629c81d856a6378b6f116"
},
"ledger": {
"revision": "1050a25df55a62878102d10e524b5184b316b7ad"
},
"llvm": {
"revision": "e9948edc41e9e5869af99dddb2b5ff5cc5581af6"
},
"lua": {
"revision": "2b4ffd5a5ffd0c6b4c84f0d9e003050a70db2a37"
},
"make": {
"revision": "a4b9187417d6be349ee5fd4b6e77b4172c6827dd"
},
"markdown": {
"revision": "d24196f9b3e5af6fcb2ec2a0b6cbc5c06f58b85e"
},
"ninja": {
"revision": "0a95cfdc0745b6ae82f60d3a339b37f19b7b9267"
},
"nix": {
"revision": "6d6aaa50793b8265b6a8b6628577a0083d3b923d"
},
"norg": {
"revision": "17d61df817c1e0a9cdef8d915d4e4c556b7cf68c"
},
"ocaml": {
"revision": "23d419ba45789c5a47d31448061557716b02750a"
},
"ocaml_interface": {
"revision": "23d419ba45789c5a47d31448061557716b02750a"
},
"ocamllex": {
"revision": "ac1d5957e719d49bd6acd27439b79843e4daf8ed"
},
"pascal": {
"revision": "2fd40f477d3e2794af152618ccfac8d92eb72a66"
},
"perl": {
"revision": "bbf86084d9b7eb4768f3fb9fe094b3e0600057b1"
},
"php": {
"revision": "3c17a28da38afac41332d3ce79bbd8951867f346"
},
"phpdoc": {
"revision": "d9150d4cb5941f6ff10adba89bfa1f11f4b05b37"
},
"pioasm": {
"revision": "924aadaf5dea2a6074d72027b064f939acf32e20"
},
"prisma": {
"revision": "bf0833cbedb2c5b39250f5ba900f1239a16c6749"
},
"pug": {
"revision": "5875f9a7d94836708119b0a1102bb5792e8bf673"
},
"python": {
"revision": "78c4e9b6b2f08e1be23b541ffced47b15e2972ad"
},
"ql": {
"revision": "8e7fd7e638d4a0ec7a792ee16b19dbc6407aa810"
},
"query": {
"revision": "5217c6805c09f8fc00ed13d17d5fcb791437aee6"
},
"r": {
"revision": "cc04302e1bff76fa02e129f332f44636813b0c3c"
},
"rasi": {
"revision": "e2961f02244c068a67549adf896b0779e4a29516"
},
"regex": {
"revision": "e1cfca3c79896ff79842f057ea13e529b66af636"
},
"rego": {
"revision": "858ca3013da4d50ea1bdb32162a2bfb43641b422"
},
"rst": {
"revision": "b74770c0166f28c1a0ab293513a78712ca1c338b"
},
"ruby": {
"revision": "0b107de5415e7470a30ef1a390d9db3306432bdb"
},
"rust": {
"revision": "0509e440ae042db6483984b3a56b3c5f24b5d9b9"
},
"scala": {
"revision": "ec6047f531e7d4c13787d4ff208b94a84de34165"
},
"scheme": {
"revision": "a1d2233f4e5498bb305858323c93525e1dd165c0"
},
"scss": {
"revision": "c478c6868648eff49eb04a4df90d703dc45b312a"
},
"slint": {
"revision": "0d4dda94f96623302dfc234e06be62a5717f47da"
},
"solidity": {
"revision": "52ed0880c0126df2f2c7693f215fe6f38e4a2e0a"
},
"sparql": {
"revision": "05f949d3c1c15e3261473a244d3ce87777374dec"
},
"supercollider": {
"revision": "0f0e5b5a96dd3e048a9c3db648ed969c44068bff"
},
"surface": {
"revision": "f4586b35ac8548667a9aaa4eae44456c1f43d032"
},
"svelte": {
"revision": "84c90ee15f851e1541c25c86e8a4338f5b4d5af2"
},
"swift": {
"revision": "897df54ad1b2d103dcab3df94f335fceefa230dc"
},
"teal": {
"revision": "fcc5f6f4d194dede4e676834ff28a506e39e17b4"
},
"tlaplus": {
"revision": "dde405e5128c3c47ab8aa014d21b6e5296ca450f"
},
"todotxt": {
"revision": "0207f6a4ab6aeafc4b091914d31d8235049a2578"
},
"toml": {
"revision": "8bd2056818b21860e3d756b5a58c4f6e05fb744e"
},
"tsx": {
"revision": "8e9dba7bd7cf089838a036a98be94db53ba2d0a9"
},
"turtle": {
"revision": "085437f5cb117703b7f520dd92161140a684f092"
},
"typescript": {
"revision": "8e9dba7bd7cf089838a036a98be94db53ba2d0a9"
},
"vala": {
"revision": "31a08784cc74b61cb10c5d8314cf8a8aa98aa9a8"
},
"verilog": {
"revision": "8f6b1f357d1231c420404b5f7a368a73c25adfa2"
},
"vim": {
"revision": "68f7cacfd9ae4698181511c9fdd6641bc25a260b"
},
"vue": {
"revision": "91fe2754796cd8fba5f229505a23fa08f3546c06"
},
"wgsl": {
"revision": "5ca98b174f7dddf69fb7a80c54d49badd1f1f8c4"
},
"yaml": {
"revision": "0e36bed171768908f331ff7dff9d956bae016efb"
},
"yang": {
"revision": "8e9d175982afcefa3dac8ca20d40d1643accd2bd"
},
"zig": {
"revision": "4cff36421dae9c05388b86cd64d2bab4b9ed6676"
}
}

View File

@ -0,0 +1,88 @@
if not pcall(require, "vim.treesitter.languagetree") then
error "nvim-treesitter requires a more recent Neovim nightly version!"
end
local install = require "nvim-treesitter.install"
local utils = require "nvim-treesitter.utils"
local ts_utils = require "nvim-treesitter.ts_utils"
local info = require "nvim-treesitter.info"
local configs = require "nvim-treesitter.configs"
local parsers = require "nvim-treesitter.parsers"
-- 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
function M.define_modules(...)
configs.define_modules(...)
end
local get_line_for_node = function(node, type_patterns, transform_fn)
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(ts_utils.get_node_text(node)[1] or ""))
-- Escape % to avoid statusline to evaluate content as expression
return line:gsub("%%", "%%%%")
end
-- 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 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 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 = get_line_for_node(expr, type_patterns, transform_fn)
if line ~= "" and not vim.tbl_contains(lines, line) then
table.insert(lines, 1, line)
end
expr = expr:parent()
end
local text = table.concat(lines, separator)
local text_len = #text
if text_len > indicator_size then
return "..." .. text:sub(text_len - indicator_size, text_len)
end
return text
end
return M

View File

@ -0,0 +1,48 @@
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 = {}
local items = setmetatable({}, {
__index = function(tbl, key)
rawset(tbl, key, {})
return rawget(tbl, key)
end,
})
function cache.set(type_name, bufnr, value)
if not cache.has(type_name, bufnr) then
-- Clean up the cache if the buffer is detached
-- to avoid memory leaks
api.nvim_buf_attach(bufnr, false, {
on_detach = function()
cache.remove(type_name, bufnr)
return true
end,
})
end
items[type_name][bufnr] = value
end
function cache.get(type_name, bufnr)
return items[type_name][bufnr]
end
function cache.has(type_name, bufnr)
return cache.get(type_name, bufnr) ~= nil
end
function cache.remove(type_name, bufnr)
items[type_name][bufnr] = nil
end
return cache
end
return M

View File

@ -0,0 +1,559 @@
local api = vim.api
local queries = require "nvim-treesitter.query"
local ts_query = require "vim.treesitter.query"
local parsers = require "nvim-treesitter.parsers"
local utils = require "nvim-treesitter.utils"
local caching = require "nvim-treesitter.caching"
local M = {}
local config = {
modules = {},
sync_install = false,
ensure_installed = {},
ignore_install = {},
update_strategy = "lockfile",
}
-- List of modules that need to be setup on initialization.
local queued_modules_defs = {}
-- Whether we've initialized the plugin yet.
local is_initialized = false
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",
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.
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 path to module
-- @param bufnr buffer number, defaults to current buffer
-- @param lang 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 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
local cmd = string.format("lua require'nvim-treesitter.configs'.reattach_module('%s')", mod)
api.nvim_command(string.format("autocmd NvimTreesitter FileType * %s", cmd))
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 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 path to module
-- @param bufnr 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 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
-- TODO(kyazdani): detach the correct autocmd... doesn't work when using %s, cmd.
-- This will remove all autocomands!
api.nvim_command "autocmd! NvimTreesitter FileType *"
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 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 path to module
-- @param bufnr buffer number, defaults to current buffer
-- @param lang 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 root configuration table to start at
-- @param path 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
if not vim.ui then
vim.ui = {
select = function(items, opts, on_choice)
vim.validate {
items = { items, "table", false },
on_choice = { on_choice, "function", false },
}
opts = opts or {}
local choices = { opts.prompt or "Select one of:" }
local format_item = opts.format_item or tostring
for i, item in pairs(items) do
table.insert(choices, string.format("%d: %s", i, format_item(item)))
end
local choice = vim.fn.inputlist(choices)
if choice < 1 or choice > #items then
on_choice(nil, nil)
else
on_choice(items[choice], choice)
end
end,
}
end
function M.edit_query_file(query_group, lang)
lang = lang or parsers.get_buf_lang()
local files = ts_query.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
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: module (string)
-- @param lang: the language of the buffer (string)
-- @param bufnr: the bufnr (number)
function M.is_enabled(mod, lang, bufnr)
if not parsers.list[lang] or 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 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 {}
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
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
-- }
-- }
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 the module name
-- @param bufnr the bufnr
-- @param lang 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 the module name
-- @param bufnr the bufnr
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 the module name
-- @param bufnr the bufnr
-- @param lang 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 root table to find modules
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 path to the module
-- @returns 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 the module table
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
function M.get_update_strategy()
return config.update_strategy
end
function M.get_ignored_parser_installs()
return config.ignore_install or {}
end
return M

View File

@ -0,0 +1,112 @@
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
local start_counts = {}
local stop_counts = {}
local prev_start = -1
local prev_stop = -1
local min_fold_lines = api.nvim_win_get_option(0, "foldminlines")
for _, node in ipairs(matches) do
local start, _, stop, stop_col = node.node:range()
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
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)
function M.get_fold_indic(lnum)
if not parsers.has_parser() or not lnum then
return "0"
end
local buf = api.nvim_get_current_buf()
local levels = folds_levels(buf) or {}
return levels[lnum] or "0"
end
return M

View File

@ -0,0 +1,168 @@
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 health_start = vim.fn["health#report_start"]
local health_ok = vim.fn["health#report_ok"]
local health_error = vim.fn["health#report_error"]
local health_warn = vim.fn["health#report_warn"]
local M = {}
local NVIM_TREESITTER_MINIMUM_ABI = 13
local function install_health()
health_start "Installation"
if fn.executable "tree-sitter" == 0 then
health_warn(
"`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar,"
.. " not required for :TSInstall)"
)
else
health_ok(
"`tree-sitter` found "
.. (utils.ts_cli_version() or "(unknown version)")
.. " (parser generator, only needed for :TSInstallFromGrammar)"
)
end
if fn.executable "node" == 0 then
health_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"
handle:close()
local version = vim.split(result, "\n")[1]
health_ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)")
end
if fn.executable "git" == 0 then
health_error("`git` executable not found.", {
"Install it with your package manager.",
"Check that your `$PATH` is set correctly.",
})
else
health_ok "`git` executable found."
end
local cc = shell.select_executable(install.compilers)
if not cc then
health_error("`cc` executable not found.", {
"Check that any of "
.. vim.inspect(install.compilers)
.. " 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]
health_ok(
"`"
.. cc
.. "` executable found. Selected from "
.. vim.inspect(install.compilers)
.. (version and ("\nVersion: " .. version) or "")
)
end
if vim.treesitter.language_version then
if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then
health_ok(
"Neovim was compiled with tree-sitter runtime ABI version "
.. vim.treesitter.language_version
.. " (required >="
.. NVIM_TREESITTER_MINIMUM_ABI
.. "). Parsers must be compatible with runtime ABI."
)
else
health_error(
"Neovim was compiled with tree-sitter runtime ABI version "
.. vim.treesitter.language_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."
)
end
end
end
local function query_status(lang, query_group)
local ok, err = pcall(queries.get_query, lang, query_group)
if not ok then
return "x", err
elseif not err then
return "."
else
return ""
end
end
function M.check()
local error_collection = {}
-- Installation dependency checks
install_health()
queries.invalidate_query_cache()
-- Parser installation checks
local parser_installation = { "Parser/Features 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(" ", 15 - (#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
end
table.insert(parser_installation, out)
end
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}]]
table.insert(parser_installation, legend)
-- Finally call the report function
health_start(table.concat(parser_installation, "\n"))
if #error_collection > 0 then
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 = vim.treesitter.query.get_query_files(lang, type)
if #files > 0 then
table.insert(lines, lang .. "(" .. type .. ") is concatenated from the following files:")
for _, file in ipairs(files) do
local fd = io.open(file, "r")
if fd then
local ok, file_err = pcall(vim.treesitter.query.parse_query, lang, fd:read "*a")
if ok then
table.insert(lines, '| [OK]:"' .. file .. '"')
else
table.insert(lines, '| [ERROR]:"' .. file .. '", failed to load: ' .. file_err)
end
fd:close()
end
end
end
health_error(table.concat(lines, "\n"))
end
end
end
return M

View File

@ -0,0 +1,159 @@
local api = vim.api
local ts = vim.treesitter
local parsers = require "nvim-treesitter.parsers"
local configs = require "nvim-treesitter.configs"
local M = {}
local hlmap = vim.treesitter.highlighter.hl_map
-- nvim-treesitter Highlight Group Mappings
-- Note: Some highlight groups may not be applied upstream, some may be experimental
hlmap["annotation"] = "TSAnnotation"
hlmap["attribute"] = "TSAttribute"
hlmap["boolean"] = "TSBoolean"
hlmap["character"] = "TSCharacter"
hlmap["character.special"] = "TSCharacterSpecial"
hlmap["comment"] = "TSComment"
hlmap["conditional"] = "TSConditional"
hlmap["constant"] = "TSConstant"
hlmap["constant.builtin"] = "TSConstBuiltin"
hlmap["constant.macro"] = "TSConstMacro"
hlmap["constructor"] = "TSConstructor"
hlmap["debug"] = "TSDebug"
hlmap["define"] = "TSDefine"
hlmap["error"] = "TSError"
hlmap["exception"] = "TSException"
hlmap["field"] = "TSField"
hlmap["float"] = "TSFloat"
hlmap["function"] = "TSFunction"
hlmap["function.builtin"] = "TSFuncBuiltin"
hlmap["function.macro"] = "TSFuncMacro"
hlmap["include"] = "TSInclude"
hlmap["keyword"] = "TSKeyword"
hlmap["keyword.function"] = "TSKeywordFunction"
hlmap["keyword.operator"] = "TSKeywordOperator"
hlmap["keyword.return"] = "TSKeywordReturn"
hlmap["label"] = "TSLabel"
hlmap["method"] = "TSMethod"
hlmap["namespace"] = "TSNamespace"
hlmap["none"] = "TSNone"
hlmap["number"] = "TSNumber"
hlmap["operator"] = "TSOperator"
hlmap["parameter"] = "TSParameter"
hlmap["parameter.reference"] = "TSParameterReference"
hlmap["preproc"] = "TSPreProc"
hlmap["property"] = "TSProperty"
hlmap["punctuation.delimiter"] = "TSPunctDelimiter"
hlmap["punctuation.bracket"] = "TSPunctBracket"
hlmap["punctuation.special"] = "TSPunctSpecial"
hlmap["repeat"] = "TSRepeat"
hlmap["storageclass"] = "TSStorageClass"
hlmap["string"] = "TSString"
hlmap["string.regex"] = "TSStringRegex"
hlmap["string.escape"] = "TSStringEscape"
hlmap["string.special"] = "TSStringSpecial"
hlmap["symbol"] = "TSSymbol"
hlmap["tag"] = "TSTag"
hlmap["tag.attribute"] = "TSTagAttribute"
hlmap["tag.delimiter"] = "TSTagDelimiter"
hlmap["text"] = "TSText"
hlmap["text.strong"] = "TSStrong"
hlmap["text.emphasis"] = "TSEmphasis"
hlmap["text.underline"] = "TSUnderline"
hlmap["text.strike"] = "TSStrike"
hlmap["text.title"] = "TSTitle"
hlmap["text.literal"] = "TSLiteral"
hlmap["text.uri"] = "TSURI"
hlmap["text.math"] = "TSMath"
hlmap["text.reference"] = "TSTextReference"
hlmap["text.environment"] = "TSEnvironment"
hlmap["text.environment.name"] = "TSEnvironmentName"
hlmap["text.note"] = "TSNote"
hlmap["text.warning"] = "TSWarning"
hlmap["text.danger"] = "TSDanger"
hlmap["todo"] = "TSTodo"
hlmap["type"] = "TSType"
hlmap["type.builtin"] = "TSTypeBuiltin"
hlmap["type.qualifier"] = "TSTypeQualifier"
hlmap["type.definition"] = "TSTypeDefinition"
hlmap["variable"] = "TSVariable"
hlmap["variable.builtin"] = "TSVariableBuiltin"
local function should_enable_vim_regex(config, lang)
local additional_hl = config.additional_vim_regex_highlighting
local is_table = type(additional_hl) == "table"
return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang))
end
local function enable_syntax(bufnr)
api.nvim_buf_set_option(bufnr, "syntax", "ON")
end
function M.stop(bufnr)
if ts.highlighter.active[bufnr] then
ts.highlighter.active[bufnr]:destroy()
end
end
function M.start(bufnr, lang)
local parser = parsers.get_parser(bufnr, lang)
ts.highlighter.new(parser, {})
end
function M.attach(bufnr, lang)
local config = configs.get_module "highlight"
M.start(bufnr, lang)
if should_enable_vim_regex(config, lang) then
enable_syntax(bufnr)
end
end
function M.detach(bufnr)
M.stop(bufnr)
enable_syntax(bufnr)
end
function M.set_custom_captures(captures)
for k, v in pairs(captures) do
hlmap[k] = v
end
end
return M

View File

@ -0,0 +1,148 @@
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 M = {}
local selections = {}
function M.init_selection()
local buf = api.nvim_get_current_buf()
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 start with 1 and the ending is inclusive.
local function visual_selection_range()
local _, csrow, cscol, _ = unpack(vim.fn.getpos "'<")
local _, cerow, cecol, _ = unpack(vim.fn.getpos "'>")
local start_row, start_col, end_row, end_col
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
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
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 root = parsers.get_parser():parse()[1]:root()
local node = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol)
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]
while true do
local parent = get_parent(node)
if not parent or parent == node then
-- Keep searching in the main tree
-- TODO: we should search on the parent tree of the current node.
local root = parsers.get_parser():parse()[1]:root()
parent = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol)
if not parent or root == node or parent == node then
ts_utils.update_selection(buf, node)
return
end
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]
ts_utils.update_selection(buf, node)
end
function M.attach(bufnr)
local config = configs.get_module "incremental_selection"
for funcname, mapping in pairs(config.keymaps) do
local mode
if funcname == "init_selection" then
mode = "n"
else
mode = "x"
end
local cmd = string.format(":lua require'nvim-treesitter.incremental_selection'.%s()<CR>", funcname)
api.nvim_buf_set_keymap(bufnr, mode, mapping, cmd, { silent = true, noremap = true })
end
end
function M.detach(bufnr)
local config = configs.get_module "incremental_selection"
for f, mapping in pairs(config.keymaps) do
if f == "init_selection" then
api.nvim_buf_del_keymap(bufnr, "n", mapping)
else
api.nvim_buf_del_keymap(bufnr, "x", mapping)
end
end
end
return M

View File

@ -0,0 +1,205 @@
local parsers = require "nvim-treesitter.parsers"
local queries = require "nvim-treesitter.query"
local tsutils = require "nvim-treesitter.ts_utils"
local M = {}
M.avoid_force_reparsing = {
yaml = true,
}
M.comment_parsers = {
comment = true,
jsdoc = true,
phpdoc = true,
}
local function get_first_node_at_line(root, lnum)
local col = vim.fn.indent(lnum)
return root:descendant_for_range(lnum - 1, col, lnum - 1, col)
end
local function get_last_node_at_line(root, lnum)
local col = #vim.fn.getline(lnum) - 1
return root:descendant_for_range(lnum - 1, col, lnum - 1, col)
end
local function find_delimiter(bufnr, node, delimiter)
for child, _ in node:iter_children() do
if child:type() == delimiter then
local linenr = child:start()
local line = vim.api.nvim_buf_get_lines(bufnr, linenr, linenr + 1, false)[1]
local end_char = { child:end_() }
return child, #line == end_char[2]
end
end
end
local get_indents = tsutils.memoize_by_buf_tick(function(bufnr, root, lang)
local map = {
auto = {},
indent = {},
indent_end = {},
dedent = {},
branch = {},
ignore = {},
aligned_indent = {},
zero_indent = {},
}
for name, node, metadata in queries.iter_captures(bufnr, "indents", root, lang) do
map[name][node:id()] = metadata or {}
end
return map
end, {
-- Memoize by bufnr and lang together.
key = function(bufnr, root, lang)
return tostring(bufnr) .. root:id() .. "_" .. lang
end,
})
---@param lnum number (1-indexed)
function M.get_indent(lnum)
local bufnr = vim.api.nvim_get_current_buf()
local parser = parsers.get_parser(bufnr)
if not parser or not lnum then
return -1
end
local root_lang = parsers.get_buf_lang(bufnr)
-- 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()
end
-- Get language tree with smallest range around node that's not a comment parser
local root, lang_tree
parser:for_each_tree(function(tstree, tree)
if not tstree or M.comment_parsers[tree:lang()] then
return
end
local local_root = tstree:root()
if tsutils.is_in_node_range(local_root, lnum - 1, 0) then
if not root or tsutils.node_length(root) >= tsutils.node_length(local_root) then
root = local_root
lang_tree = tree
end
end
end)
-- Not likely, but just in case...
if not root then
return 0
end
local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang())
local is_empty_line = string.match(vim.fn.getline(lnum), "^%s*$") ~= nil
local node
if is_empty_line then
local prevlnum = vim.fn.prevnonblank(lnum)
node = get_last_node_at_line(root, prevlnum)
if q.indent_end[node:id()] then
node = get_first_node_at_line(root, lnum)
end
else
node = get_first_node_at_line(root, lnum)
end
local indent_size = vim.fn.shiftwidth()
local indent = 0
local _, _, root_start = root:start()
if root_start ~= 0 then
-- injected tree
indent = vim.fn.indent(root:start() + 1)
end
-- tracks to ensure multiple indent levels are not applied for same line
local is_processed_by_row = {}
if q.zero_indent[node:id()] then
return 0
end
while node do
-- do 'autoindent' if not marked as @indent
if not q.indent[node:id()] and q.auto[node:id()] and node:start() < lnum - 1 and lnum - 1 <= node:end_() then
return -1
end
-- Do not indent if we are inside an @ignore block.
-- 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[node:id()] and q.ignore[node:id()] and node:start() < lnum - 1 and lnum - 1 <= node:end_() then
return 0
end
local srow, _, erow = node:range()
local is_processed = false
if
not is_processed_by_row[srow]
and ((q.branch[node:id()] and srow == lnum - 1) or (q.dedent[node:id()] and srow ~= lnum - 1))
then
indent = indent - indent_size
is_processed = true
end
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
if
not is_processed_by_row[srow]
-- Dear stylua, please don't change the semantics of this statement!
-- stylua: ignore start
and (q.indent[node:id()] and srow ~= erow and ((srow ~= lnum - 1) or q.indent[node:id()].start_at_same_line))
-- stylua: ignore end
then
indent = indent + indent_size
is_processed = true
end
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
if q.aligned_indent[node:id()] and srow ~= erow and (srow ~= lnum - 1) then
local metadata = q.aligned_indent[node:id()]
local o_delim_node, is_last_in_line
if metadata.delimiter then
local opening_delimiter = metadata.delimiter and metadata.delimiter:sub(1, 1)
o_delim_node, is_last_in_line = find_delimiter(bufnr, node, opening_delimiter)
else
o_delim_node = node
end
if o_delim_node then
if is_last_in_line then
-- hanging indent (previous line ended with starting delimiter)
indent = indent + indent_size * 1
else
local _, o_scol = o_delim_node:start()
return math.max(indent, 0) + o_scol + (metadata.increment or 1)
end
end
end
is_processed_by_row[srow] = is_processed_by_row[srow] or is_processed
node = node:parent()
end
return indent
end
local indent_funcs = {}
function M.attach(bufnr)
indent_funcs[bufnr] = vim.bo.indentexpr
vim.bo.indentexpr = "nvim_treesitter#indent()"
vim.api.nvim_command("au Filetype " .. vim.bo.filetype .. " setlocal indentexpr=nvim_treesitter#indent()")
end
function M.detach(bufnr)
vim.bo.indentexpr = indent_funcs[bufnr]
end
return M

View File

@ -0,0 +1,174 @@
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 _, ft in pairs(parser_list) do
local is_installed = #api.nvim_get_runtime_file("parser/" .. ft .. ".so", false) > 0
api.nvim_out_write(ft .. string.rep(" ", max_len - #ft + 1))
if is_installed then
api.nvim_out_write "[✓] installed\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'}}
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
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
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
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")
api.nvim_exec(
[[
syntax match TSModuleInfoGood //
syntax match TSModuleInfoBad //
syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace
syntax match TSModuleInfoNamespace /^>> \w*/ contained
syntax match TSModuleInfoParser /^[^> ]*\ze /
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
]],
false
)
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
function M.installed_parsers()
local installed = {}
for _, p in pairs(parsers.available_parsers()) do
if parsers.has_parser(p) then
table.insert(installed, p)
end
end
return installed
end
M.commands = {
TSInstallInfo = {
run = install_info,
args = {
"-nargs=0",
},
},
TSModuleInfo = {
run = module_info,
args = {
"-nargs=?",
"-complete=custom,nvim_treesitter#available_modules",
},
},
}
return M

View File

@ -0,0 +1,600 @@
local api = vim.api
local fn = vim.fn
local luv = vim.loop
local utils = require "nvim-treesitter.utils"
local parsers = require "nvim-treesitter.parsers"
local info = require "nvim-treesitter.info"
local configs = require "nvim-treesitter.configs"
local shell = require "nvim-treesitter.shell_command_selectors"
local M = {}
local lockfile = {}
M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" }
M.prefer_git = fn.has "win32" == 1
M.command_extra_args = {}
M.ts_generate_args = nil
local started_commands = 0
local finished_commands = 0
local failed_commands = 0
local complete_std_output = {}
local complete_error_output = {}
local function reset_progress_counter()
if started_commands ~= finished_commands then
return
end
started_commands = 0
finished_commands = 0
failed_commands = 0
complete_std_output = {}
complete_error_output = {}
end
local function get_job_status()
return "[nvim-treesitter] ["
.. finished_commands
.. "/"
.. started_commands
.. (failed_commands > 0 and ", failed: " .. failed_commands or "")
.. "]"
end
local function get_parser_install_info(lang, validate)
local parser_config = parsers.get_parser_configs()[lang]
if not parser_config then
return error("Parser not available for language " .. lang)
end
local install_info = parser_config.install_info
if validate then
vim.validate {
url = { install_info.url, "string" },
files = { install_info.files, "table" },
}
end
return install_info
end
local function load_lockfile()
local filename = utils.join_path(utils.get_package_path(), "lockfile.json")
lockfile = vim.fn.filereadable(filename) == 1 and vim.fn.json_decode(vim.fn.readfile(filename)) or {}
end
local function get_revision(lang)
if #lockfile == 0 then
load_lockfile()
end
local install_info = get_parser_install_info(lang)
if install_info.revision then
return install_info.revision
end
return (lockfile[lang] and lockfile[lang].revision)
end
local function get_installed_revision(lang)
local lang_file = utils.join_path(utils.get_parser_info_dir(), lang .. ".revision")
if vim.fn.filereadable(lang_file) == 1 then
return vim.fn.readfile(lang_file)[1]
end
end
local function is_installed(lang)
return #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0
end
local function needs_update(lang)
local revision = get_revision(lang)
return not revision or revision ~= get_installed_revision(lang)
end
local function outdated_parsers()
return vim.tbl_filter(function(lang)
return needs_update(lang)
end, info.installed_parsers())
end
local function onread(handle, is_stderr)
return function(err, data)
if data then
if is_stderr then
complete_error_output[handle] = (complete_error_output[handle] or "") .. data
else
complete_std_output[handle] = (complete_std_output[handle] or "") .. data
end
end
end
end
function M.iter_cmd(cmd_list, i, lang, success_message)
if i == 1 then
started_commands = started_commands + 1
end
if i == #cmd_list + 1 then
finished_commands = finished_commands + 1
return print(get_job_status() .. " " .. success_message)
end
local attr = cmd_list[i]
if attr.info then
print(get_job_status() .. " " .. attr.info)
end
if attr.opts and attr.opts.args and M.command_extra_args[attr.cmd] then
vim.list_extend(attr.opts.args, M.command_extra_args[attr.cmd])
end
if type(attr.cmd) == "function" then
local ok, err = pcall(attr.cmd)
if ok then
M.iter_cmd(cmd_list, i + 1, lang, success_message)
else
failed_commands = failed_commands + 1
finished_commands = finished_commands + 1
return api.nvim_err_writeln(
(attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) .. "\n" .. vim.inspect(err)
)
end
else
local handle
local stdout = luv.new_pipe(false)
local stderr = luv.new_pipe(false)
attr.opts.stdio = { nil, stdout, stderr }
handle = luv.spawn(
attr.cmd,
attr.opts,
vim.schedule_wrap(function(code)
if code ~= 0 then
stdout:read_stop()
stderr:read_stop()
end
stdout:close()
stderr:close()
handle:close()
if code ~= 0 then
failed_commands = failed_commands + 1
finished_commands = finished_commands + 1
if complete_std_output[handle] and complete_std_output[handle] ~= "" then
print(complete_std_output[handle])
end
local err_msg = complete_error_output[handle] or ""
api.nvim_err_writeln(
"nvim-treesitter["
.. lang
.. "]: "
.. (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr)))
.. "\n"
.. err_msg
)
return
end
M.iter_cmd(cmd_list, i + 1, lang, success_message)
end)
)
luv.read_start(stdout, onread(handle, false))
luv.read_start(stderr, onread(handle, true))
end
end
local function get_command(cmd)
local options = ""
if cmd.opts and cmd.opts.args then
if M.command_extra_args[cmd.cmd] then
vim.list_extend(cmd.opts.args, M.command_extra_args[cmd.cmd])
end
for _, opt in ipairs(cmd.opts.args) do
options = string.format("%s %s", options, opt)
end
end
local final = string.format("%s %s", cmd.cmd, options)
if cmd.opts and cmd.opts.cwd then
final = shell.make_directory_change_for_command(cmd.opts.cwd, final)
end
return final
end
local function iter_cmd_sync(cmd_list)
for _, cmd in ipairs(cmd_list) do
if cmd.info then
print(cmd.info)
end
if type(cmd.cmd) == "function" then
cmd.cmd()
else
local ret = vim.fn.system(get_command(cmd))
if vim.v.shell_error ~= 0 then
print(ret)
api.nvim_err_writeln(
(cmd.err and cmd.err .. "\n" or "") .. "Failed to execute the following command:\n" .. vim.inspect(cmd)
)
return false
end
end
end
return true
end
local function run_install(cache_folder, install_folder, lang, repo, with_sync, generate_from_grammar)
parsers.reset_cache()
local path_sep = utils.get_path_sep()
local project_name = "tree-sitter-" .. lang
local maybe_local_path = vim.fn.expand(repo.url)
local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1
if from_local_path then
repo.url = maybe_local_path
end
-- compile_location only needed for typescript installs.
local compile_location
if from_local_path then
compile_location = repo.url
else
local repo_location = string.gsub(repo.location or project_name, "/", path_sep)
compile_location = cache_folder .. path_sep .. repo_location
end
local parser_lib_name = install_folder .. path_sep .. lang .. ".so"
generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar
if generate_from_grammar and vim.fn.executable "tree-sitter" ~= 1 then
api.nvim_err_writeln "tree-sitter CLI not found: `tree-sitter` is not executable!"
if repo.requires_generate_from_grammar then
api.nvim_err_writeln(
"tree-sitter CLI is needed because `"
.. lang
.. "` is marked that it needs "
.. "to be generated from the grammar definitions to be compatible with nvim!"
)
end
return
else
if not M.ts_generate_args then
local ts_cli_version = utils.ts_cli_version()
if ts_cli_version and vim.split(ts_cli_version, " ")[1] > "0.20.2" then
M.ts_generate_args = { "generate", "--abi", vim.treesitter.language_version }
else
M.ts_generate_args = { "generate" }
end
end
end
if generate_from_grammar and vim.fn.executable "node" ~= 1 then
api.nvim_err_writeln "Node JS not found: `node` is not executable!"
return
end
local cc = shell.select_executable(M.compilers)
if not cc then
api.nvim_err_writeln('No C compiler found! "' .. table.concat(
vim.tbl_filter(function(c)
return type(c) == "string"
end, M.compilers),
'", "'
) .. '" are not executable.')
return
end
local revision = configs.get_update_strategy() == "lockfile" and get_revision(lang)
local command_list = {}
if not from_local_path then
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) })
vim.list_extend(
command_list,
shell.select_download_commands(repo, project_name, cache_folder, revision, M.prefer_git)
)
end
if generate_from_grammar then
if repo.generate_requires_npm then
if vim.fn.executable "npm" ~= 1 then
api.nvim_err_writeln("`" .. lang .. "` requires NPM to be installed from grammar.js")
return
end
vim.list_extend(command_list, {
{
cmd = "npm",
info = "Installing NPM dependencies of " .. lang .. " parser",
err = "Error during `npm install` (required for parser generation of " .. lang .. " with npm dependencies)",
opts = {
args = { "install" },
cwd = compile_location,
},
},
})
end
vim.list_extend(command_list, {
{
cmd = vim.fn.exepath "tree-sitter",
info = "Generating source files from grammar.js...",
err = 'Error during "tree-sitter generate"',
opts = {
args = M.ts_generate_args,
cwd = compile_location,
},
},
})
end
vim.list_extend(command_list, {
shell.select_compile_command(repo, cc, compile_location),
shell.select_mv_cmd("parser.so", parser_lib_name, compile_location),
{
cmd = function()
vim.fn.writefile({ revision or "" }, utils.join_path(utils.get_parser_info_dir(), lang .. ".revision"))
end,
},
{ -- auto-attach modules after installation
cmd = function()
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if parsers.get_buf_lang(buf) == lang then
for _, mod in ipairs(require("nvim-treesitter.configs").available_modules()) do
require("nvim-treesitter.configs").reattach_module(mod, buf)
end
end
end
end,
},
})
if not from_local_path then
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) })
end
if with_sync then
if iter_cmd_sync(command_list) == true then
print("Treesitter parser for " .. lang .. " has been installed")
end
else
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed")
end
end
local function install_lang(lang, ask_reinstall, cache_folder, install_folder, with_sync, generate_from_grammar)
if is_installed(lang) and ask_reinstall ~= "force" then
if not ask_reinstall then
return
end
local yesno = fn.input(lang .. " parser already available: would you like to reinstall ? y/n: ")
print "\n "
if not string.match(yesno, "^y.*") then
return
end
end
local install_info = get_parser_install_info(lang, true)
run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar)
end
local function install(options)
options = options or {}
local with_sync = options.with_sync
local ask_reinstall = options.ask_reinstall
local generate_from_grammar = options.generate_from_grammar
local exclude_configured_parsers = options.exclude_configured_parsers
return function(...)
if fn.executable "git" == 0 then
return api.nvim_err_writeln "Git is required on your system to run this command"
end
local cache_folder, err = utils.get_cache_dir()
if err then
return api.nvim_err_writeln(err)
end
local install_folder, err = utils.get_parser_install_dir()
if err then
return api.nvim_err_writeln(err)
end
local languages
local ask
if ... == "all" then
languages = parsers.available_parsers()
ask = false
elseif ... == "maintained" then
languages = parsers.maintained_parsers()
ask = false
else
languages = vim.tbl_flatten { ... }
ask = ask_reinstall
end
if exclude_configured_parsers then
languages = utils.difference(languages, configs.get_ignored_parser_installs())
end
if #languages > 1 then
reset_progress_counter()
end
for _, lang in ipairs(languages) do
install_lang(lang, ask, cache_folder, install_folder, with_sync, generate_from_grammar)
end
end
end
function M.update(options)
options = options or {}
return function(...)
M.lockfile = {}
reset_progress_counter()
if ... and ... ~= "all" then
local languages = vim.tbl_flatten { ... }
local installed = 0
for _, lang in ipairs(languages) do
if (not is_installed(lang)) or (needs_update(lang)) then
installed = installed + 1
install {
ask_reinstall = "force",
with_sync = options.with_sync,
}(lang)
end
end
if installed == 0 then
utils.notify "Parsers are up-to-date!"
end
else
local parsers_to_update = configs.get_update_strategy() == "lockfile" and outdated_parsers()
or info.installed_parsers()
if #parsers_to_update == 0 then
utils.notify "All parsers are up-to-date!"
end
for _, lang in pairs(parsers_to_update) do
install {
ask_reinstall = "force",
exclude_configured_parsers = true,
with_sync = options.with_sync,
}(lang)
end
end
end
end
function M.uninstall(...)
local path_sep = "/"
if fn.has "win32" == 1 then
path_sep = "\\"
end
if vim.tbl_contains({ "all", "maintained" }, ...) then
reset_progress_counter()
local installed = info.installed_parsers()
if ... == "maintained" then
local maintained = parsers.maintained_parsers()
installed = vim.tbl_filter(function(l)
return vim.tbl_contains(maintained, l)
end, installed)
end
for _, langitem in pairs(installed) do
M.uninstall(langitem)
end
elseif ... then
local languages = vim.tbl_flatten { ... }
for _, lang in ipairs(languages) do
local install_dir, err = utils.get_parser_install_dir()
if err then
return api.nvim_err_writeln(err)
end
local parser_lib = install_dir .. path_sep .. lang .. ".so"
local command_list = {
shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang),
}
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled")
end
end
end
function M.write_lockfile(verbose, skip_langs)
local sorted_parsers = {}
-- Load previous lockfile
load_lockfile()
skip_langs = skip_langs or {}
for k, v in pairs(parsers.get_parser_configs()) do
table.insert(sorted_parsers, { name = k, parser = v })
end
table.sort(sorted_parsers, function(a, b)
return a.name < b.name
end)
for _, v in ipairs(sorted_parsers) do
if not vim.tbl_contains(skip_langs, v.name) then
-- I'm sure this can be done in aync way with iter_cmd
local sha
if v.parser.install_info.branch then
sha = vim.split(
vim.fn.systemlist(
"git ls-remote " .. v.parser.install_info.url .. " | grep refs/heads/" .. v.parser.install_info.branch
)[1],
"\t"
)[1]
else
sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1]
end
lockfile[v.name] = { revision = sha }
if verbose then
print(v.name .. ": " .. sha)
end
else
print("Skipping " .. v.name)
end
end
if verbose then
print(vim.inspect(lockfile))
end
vim.fn.writefile(
vim.fn.split(vim.fn.json_encode(lockfile), "\n"),
utils.join_path(utils.get_package_path(), "lockfile.json")
)
end
M.ensure_installed = install { exclude_configured_parsers = true }
M.ensure_installed_sync = install { with_sync = true, exclude_configured_parsers = true }
M.commands = {
TSInstall = {
run = install { ask_reinstall = true },
["run!"] = install { ask_reinstall = "force" },
args = {
"-nargs=+",
"-bang",
"-complete=custom,nvim_treesitter#installable_parsers",
},
},
TSInstallFromGrammar = {
run = install { generate_from_grammar = true, ask_reinstall = true },
["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" },
args = {
"-nargs=+",
"-bang",
"-complete=custom,nvim_treesitter#installable_parsers",
},
},
TSInstallSync = {
run = install { with_sync = true, ask_reinstall = true },
["run!"] = install { with_sync = true, ask_reinstall = "force" },
args = {
"-nargs=+",
"-bang",
"-complete=custom,nvim_treesitter#installable_parsers",
},
},
TSUpdate = {
run = M.update {},
args = {
"-nargs=*",
"-complete=custom,nvim_treesitter#installed_parsers",
},
},
TSUpdateSync = {
run = M.update { with_sync = true },
args = {
"-nargs=*",
"-complete=custom,nvim_treesitter#installed_parsers",
},
},
TSUninstall = {
run = M.uninstall,
args = {
"-nargs=+",
"-complete=custom,nvim_treesitter#installed_parsers",
},
},
}
return M

View File

@ -0,0 +1,349 @@
-- Functions to handle locals
-- Locals are a generalization of definition and scopes
-- its the way nvim-treesitter uses to "understand" the code
local queries = require "nvim-treesitter.query"
local ts_utils = require "nvim-treesitter.ts_utils"
local api = vim.api
local M = {}
function M.collect_locals(bufnr)
return queries.collect_group_results(bufnr, "locals")
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)
end
function M.get_locals(bufnr)
return queries.get_matches(bufnr, "locals")
end
--- Creates unique id for a node based on text and range
-- @param scope: the scope node of the definition
-- @param bufnr: the buffer
-- @param node_text: the node text to use
-- @returns 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() }, "_")
end
function M.get_definitions(bufnr)
local locals = M.get_locals(bufnr)
local defs = {}
for _, loc in ipairs(locals) do
if loc.definition then
table.insert(defs, loc.definition)
end
end
return defs
end
function M.get_scopes(bufnr)
local locals = M.get_locals(bufnr)
local scopes = {}
for _, loc in ipairs(locals) do
if loc.scope and loc.scope.node then
table.insert(scopes, loc.scope.node)
end
end
return scopes
end
function M.get_references(bufnr)
local locals = M.get_locals(bufnr)
local refs = {}
for _, loc in ipairs(locals) do
if loc.reference and loc.reference.node then
table.insert(refs, loc.reference.node)
end
end
return refs
end
--- Gets a table with all the scopes containing a node
-- The order is from most specific to least (bottom up)
function M.get_scope_tree(node, bufnr)
local scopes = {}
for scope in M.iter_scope_tree(node, bufnr) do
table.insert(scopes, scope)
end
return scopes
end
--- Iterates over a nodes scopes moving from the bottom up
function M.iter_scope_tree(node, bufnr)
local last_node = node
return function()
if not last_node then
return
end
local scope = M.containing_scope(last_node, bufnr, false) or ts_utils.get_root_for_node(node)
last_node = scope:parent()
return scope
end
end
-- Gets a table of all nodes and their 'kinds' from a locals list
-- @param local_def the local list result
-- @returns a list of node entries
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))
end)
return result
end
-- Recurse locals results until a node is found.
-- The accumulator function is given
-- * The table of the node
-- * The node
-- * The full definition match `@definition.var.something` -> 'var.something'
-- * The last definition match `@definition.var.something` -> 'something'
-- @param The locals result
-- @param The accumulator function
-- @param The full match path to append to
-- @param The last match
function M.recurse_local_nodes(local_def, accumulator, full_match, last_match)
if type(local_def) ~= "table" then
return
end
if local_def.node then
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)
end
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.
--
-- This is memoized by buffer tick. If the function is called in succession
-- without the buffer tick changing, then the previous result will be used
-- since the syntax tree hasn't changed.
--
-- Usage lookups require finding the definition of the node, so `find_definition`
-- is called very frequently, which is why this lookup must be fast as possible.
--
-- @param bufnr: the buffer
-- @returns a table for looking up definitions
M.get_definitions_lookup_table = ts_utils.memoize_by_buf_tick(function(bufnr)
local definitions = M.get_definitions(bufnr)
local result = {}
for _, definition in ipairs(definitions) do
for _, node_entry in ipairs(M.get_local_nodes(definition)) do
local scopes = M.get_definition_scopes(node_entry.node, bufnr, node_entry.scope)
-- Always use the highest valid scope
local scope = scopes[#scopes]
local node_text = ts_utils.get_node_text(node_entry.node, bufnr)[1]
local id = M.get_definition_id(scope, node_text)
result[id] = node_entry
end
end
return result
end)
--- Gets all the scopes of a definition based on the scope type
-- Scope types can be
--
-- "parent": Uses the parent of the containing scope, basically, skipping a scope
-- "global": Uses the top most scope
-- "local": Uses the containing scope of the definition. This is the default
--
-- @param node: the definition node
-- @param bufnr: the buffer
-- @param scope_type: the scope type
function M.get_definition_scopes(node, bufnr, scope_type)
local scopes = {}
local scope_count = 1
-- Definition is valid for the containing scope
-- and the containing scope of that scope
if scope_type == "parent" then
scope_count = 2
-- Definition is valid in all parent scopes
elseif scope_type == "global" then
scope_count = nil
end
local i = 0
for scope in M.iter_scope_tree(node, bufnr) do
table.insert(scopes, scope)
i = i + 1
if scope_count and i >= scope_count then
break
end
end
return scopes
end
function M.find_definition(node, bufnr)
local def_lookup = M.get_definitions_lookup_table(bufnr)
local node_text = ts_utils.get_node_text(node, bufnr)[1]
for scope in M.iter_scope_tree(node, bufnr) do
local id = M.get_definition_id(scope, node_text)
if def_lookup[id] then
local entry = def_lookup[id]
return entry.node, scope, entry.kind
end
end
return node, ts_utils.get_root_for_node(node), nil
end
-- Finds usages of a node in a given scope.
-- @param node the node to find usages for
-- @param scope_node the node to look within
-- @returns a list of nodes
function M.find_usages(node, scope_node, bufnr)
local bufnr = bufnr or api.nvim_get_current_buf()
local node_text = ts_utils.get_node_text(node, bufnr)[1]
if not node_text or #node_text < 1 then
return {}
end
local scope_node = scope_node or ts_utils.get_root_for_node(node)
local usages = {}
for match in M.iter_locals(bufnr, scope_node) do
if
match.reference
and match.reference.node
and ts_utils.get_node_text(match.reference.node, bufnr)[1] == node_text
then
local def_node, _, kind = M.find_definition(match.reference.node, bufnr)
if kind == nil or def_node == node then
table.insert(usages, match.reference.node)
end
end
end
return usages
end
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
local scopes = M.get_scopes(bufnr)
if not node or not scopes then
return
end
local iter_node = node
while iter_node ~= nil and not vim.tbl_contains(scopes, iter_node) do
iter_node = iter_node:parent()
end
return iter_node or (allow_scope and node or nil)
end
function M.nested_scope(node, cursor_pos)
local bufnr = api.nvim_get_current_buf()
local scopes = M.get_scopes(bufnr)
if not node or not scopes then
return
end
local row = cursor_pos.row
local col = cursor_pos.col
local scope = M.containing_scope(node)
for _, child in ipairs(ts_utils.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
end
end
end
function M.next_scope(node)
local bufnr = api.nvim_get_current_buf()
local scopes = M.get_scopes(bufnr)
if not node or not scopes then
return
end
local scope = M.containing_scope(node)
local parent = scope:parent()
if not parent then
return
end
local is_prev = true
for _, child in ipairs(ts_utils.get_named_children(parent)) do
if child == scope then
is_prev = false
elseif not is_prev and vim.tbl_contains(scopes, child) then
return child
end
end
end
function M.previous_scope(node)
local bufnr = api.nvim_get_current_buf()
local scopes = M.get_scopes(bufnr)
if not node or not scopes then
return
end
local scope = M.containing_scope(node)
local parent = scope:parent()
if not parent then
return
end
local is_prev = true
local children = ts_utils.get_named_children(parent)
for i = #children, 1, -1 do
if children[i] == scope then
is_prev = false
elseif not is_prev and vim.tbl_contains(scopes, children[i]) then
return children[i]
end
end
end
return M

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,390 @@
local api = vim.api
local tsq = require "vim.treesitter.query"
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.
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
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
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
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
local query_files_cache = {}
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
local cache = setmetatable({}, mt)
--- Same as `vim.treesitter.query` except will return cached values
function M.get_query(lang, query_name)
if cache[lang][query_name] == nil then
cache[lang][query_name] = tsq.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
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_name, _ in pairs(cache[lang]) do
M.invalidate_query_cache(lang, query_name)
end
elseif not lang and not query_name then
query_files_cache = {}
for lang, _ in pairs(cache) do
for query_name, _ in pairs(cache[lang]) do
M.invalidate_query_cache(lang, query_name)
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.
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
local function prepare_query(bufnr, query_name, root, root_lang)
local buf_lang = parsers.get_buf_lang(bufnr)
if not buf_lang then
return
end
local parser = parsers.get_parser(bufnr, buf_lang)
if not parser then
return
end
if not root then
local first_tree = parser:trees()[1]
if first_tree then
root = first_tree:root()
end
end
if not root then
return
end
local range = { root:range() }
if not root_lang then
local lang_tree = parser:language_for_range(range)
if lang_tree then
root_lang = lang_tree:lang()
end
end
if not root_lang then
return
end
local query = M.get_query(root_lang, query_name)
if not query then
return
end
return query,
{
root = root,
source = bufnr,
start = range[1],
-- The end row is exclusive so we need to add 1 to it.
stop = range[3] + 1,
}
end
function M.iter_prepared_matches(query, qnode, bufnr, start_row, end_row)
-- A function that splits a string on '.'
local function split(string)
local t = {}
for str in string.gmatch(string, "([^.]+)") do
table.insert(t, str)
end
return t
end
-- Given a path (i.e. a List(String)) this functions inserts value at path
local function insert_to_path(object, path, value)
local curr_obj = object
for index = 1, (#path - 1) do
if curr_obj[path[index]] == nil then
curr_obj[path[index]] = {}
end
curr_obj = curr_obj[path[index]]
end
curr_obj[path[#path]] = value
end
local matches = query:iter_matches(qnode, bufnr, start_row, end_row)
local function iterator()
local pattern, match = matches()
if pattern ~= nil then
local prepared_match = {}
-- Extract capture names from each match
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")
insert_to_path(prepared_match, path, node)
end
end
-- Add some predicates for testing
local preds = query.info.patterns[pattern]
if preds then
for _, pred in pairs(preds) do
-- functions
if pred[1] == "set!" and type(pred[2]) == "string" then
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
insert_to_path(
prepared_match,
split(pred[2] .. ".node"),
tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]])
)
end
end
end
return prepared_match
end
end
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 the buffer
-- @param captures a single string or a list of strings
-- @param query_group the name of query group (highlights or injections for example)
-- @param root (optional) node from where to start the search
-- @param lang (optional) the language from where to get the captures.
-- Root nodes can have several languages.
function M.get_capture_matches(bufnr, captures, query_group, root, lang)
if type(captures) == "string" then
captures = { captures }
end
local strip_captures = {}
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
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
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
local best_score
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 the buffer
-- @param query_group the query file to use
-- @param root the root node
-- @param root the root node lang, if known
function M.iter_group_results(bufnr, query_group, root, root_lang)
local query, params = prepare_query(bufnr, query_group, root, root_lang)
if not query then
return EMPTY_ITER
end
return M.iter_prepared_matches(query, params.root, params.source, params.start, params.stop)
end
function M.collect_group_results(bufnr, query_group, root, lang)
local matches = {}
for prepared_match in M.iter_group_results(bufnr, query_group, root, lang) do
table.insert(matches, prepared_match)
end
return matches
end
--- Same as get_capture_matches except this will recursively get matches for every language in the tree.
-- @param bufnr The bufnr
-- @param capture_or_fn 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 The query to get the capture from. This is ignore if a function is provided
-- for the captuer argument.
function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type)
local type_fn = type(capture_or_fn) == "function" and capture_or_fn
or function()
return capture_or_fn, query_type
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))
end
end)
end
return matches
end
return M

View File

@ -0,0 +1,130 @@
local query = require "vim.treesitter.query"
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
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]]
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)
local function has_ancestor(match, pattern, bufnr, pred)
if not valid_args(pred[1], pred, 2) then
return
end
local node = match[pred[2]]
local ancestor_types = { unpack(pred, 3) }
if not node then
return true
end
local just_direct_parent = pred[1]:find("has-parent", 1, true)
node = node:parent()
while node do
if vim.tbl_contains(ancestor_types, node:type()) then
return true
end
if just_direct_parent then
node = nil
else
node = node:parent()
end
end
return false
end
query.add_predicate("has-ancestor?", has_ancestor)
query.add_predicate("has-parent?", has_ancestor)
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)
query.add_predicate("has-type?", 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)
-- Just avoid some annoying warnings for this directive
query.add_directive("make-range!", function() end)
query.add_directive("downcase!", function(match, _, bufnr, pred, metadata)
local text, key, value
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 = query.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)

View File

@ -0,0 +1,264 @@
local fn = vim.fn
local utils = require "nvim-treesitter.utils"
local M = {}
function M.select_mkdir_cmd(directory, cwd, info_msg)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "mkdir", 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
function M.select_rm_file_cmd(file, info_msg)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "if", "exist", file, "del", 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
function M.select_executable(executables)
return vim.tbl_filter(function(c)
return c ~= vim.NIL and fn.executable(c) == 1
end, executables)[1]
end
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",
"/LD",
}
elseif string.match(compiler, "zig$") or string.match(compiler, "zig.exe$") then
return {
"c++",
"-o",
"parser.so",
repo.files,
"-lc",
"-Isrc",
"-shared",
"-Os",
}
else
local args = {
"-o",
"parser.so",
"-I./src",
repo.files,
"-shared",
"-Os",
"-lstdc++",
}
if fn.has "win32" == 0 then
table.insert(args, "-fPIC")
end
return args
end
end
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 = 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.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
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", dir, "rmdir", "/s", "/q", dir },
},
}
else
return {
cmd = "rm",
opts = {
args = { "-rf", cache_folder .. "/" .. project_name },
},
}
end
end
function M.select_mv_cmd(from, to, cwd)
if fn.has "win32" == 1 then
return {
cmd = "cmd",
opts = {
args = { "/C", "move", "/Y", from, to },
cwd = cwd,
},
}
else
return {
cmd = "mv",
opts = {
args = { from, to },
cwd = cwd,
},
}
end
end
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$", "")
return {
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"),
{
cmd = "curl",
info = "Downloading...",
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_folder,
},
},
M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"),
{
cmd = "tar",
info = "Extracting...",
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 "[^/]-$" .. "-" .. revision),
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"
return {
{
cmd = "git",
info = "Downloading...",
err = clone_error,
opts = {
args = {
"clone",
repo.url,
project_name,
},
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
function M.make_directory_change_for_command(dir, command)
if fn.has "win32" == 1 then
return string.format("pushd %s & %s & popd", dir, command)
else
return string.format("cd %s;\n %s", dir, command)
end
end
return M

View File

@ -0,0 +1,392 @@
local api = vim.api
local parsers = require "nvim-treesitter.parsers"
local utils = require "nvim-treesitter.utils"
local M = {}
--- Gets the actual text content of a node
-- @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)
local 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 = M.get_node_range(node)
if start_row ~= end_row then
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false)
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
--- 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 node
-- @param allow_switch_parents allow switching parents if last node
-- @param allow_next_parent 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
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 node
-- @param allow_switch_parents allow switching parents if first node
-- @param allow_previous_parent 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
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 = {}
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)
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 = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree)
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 }
for _, tree in ipairs(lang_tree:trees()) do
local root = tree:root()
if root and M.is_in_node_range(root, line, col) then
return root, tree, lang_tree
end
end
-- This isn't a likely scenario, since the position must belong to a tree somewhere.
return nil, nil, lang_tree
end
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.
function M.get_vim_range(range, buf)
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
end
return srow, scol, erow, ecol
end
function M.highlight_range(range, buf, hl_namespace, hl_group)
local start_row, start_col, end_row, end_col = unpack(range)
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)
selection_mode = selection_mode or "charwise"
local start_row, start_col, end_row, end_col = M.get_vim_range({ M.get_node_range(node) }, buf)
vim.fn.setpos(".", { buf, start_row, start_col, 0 })
-- Start visual selection in appropriate mode
local v_table = { charwise = "v", linewise = "V", blockwise = "<C-v>" }
---- Call to `nvim_replace_termcodes()` is needed for sending appropriate
---- command to enter blockwise mode
local mode_string = vim.api.nvim_replace_termcodes(v_table[selection_mode] or selection_mode, true, true, true)
vim.cmd("normal! " .. mode_string)
vim.fn.setpos(".", { buf, end_row, end_col, 0 })
end
-- Byte length of node range
function M.node_length(node)
local _, _, start_byte = node:start()
local _, _, end_byte = node:end_()
return end_byte - start_byte
end
--- Determines whether (line, col) position is in node range
-- @param node Node defining the range
-- @param line A line (0-based)
-- @param col A column (0-based)
function M.is_in_node_range(node, line, col)
local start_line, start_col, end_line, end_col = node:range()
if line >= start_line and line <= end_line then
if line == start_line and line == end_line then
return col >= start_col and col < end_col
elseif line == start_line then
return col >= start_col
elseif line == end_line then
return col < end_col
else
return true
end
else
return false
end
end
function M.get_node_range(node_or_range)
if type(node_or_range) == "table" then
return unpack(node_or_range)
else
return node_or_range:range()
end
end
function M.node_to_lsp_range(node)
local start_line, start_col, end_line, end_col = M.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.
-- @param fn: the fn to memoize, taking the bufnr as first argument
-- @param options:
-- - bufnr: extracts a bufnr from the given arguments.
-- - key: extracts the cache key from the given arguments.
-- @returns a memoized function
function M.memoize_by_buf_tick(fn, options)
options = options or {}
local cache = {}
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 = M.get_node_text(node_or_range1)
local text2 = M.get_node_text(node_or_range2)
local edit1 = { range = range1, newText = table.concat(text2, "\n") }
local edit2 = { range = range2, newText = table.concat(text1, "\n") }
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() } }
local position
if not goto_end then
position = { range[1], range[2] }
else
position = { range[3], range[4] }
end
-- Position is 1, 0 indexed.
api.nvim_win_set_cursor(0, { position[1], position[2] - 1 })
end
return M

View File

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

View File

@ -0,0 +1,236 @@
local api = vim.api
local fn = vim.fn
local luv = vim.loop
local M = {}
-- Wrapper around vim.notify with common options set.
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 {}))
end
--- 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 heirarchy - nvim-treesitter.module
---@param commands table, 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 = vim.tbl_flatten {
"command!",
def.args,
command_name,
call_fn,
}
api.nvim_command(table.concat(parts, " "))
end
end
function M.get_path_sep()
return fn.has "win32" == 1 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"
function M.generate_join(separator)
return function(...)
return table.concat({ ... }, separator)
end
end
M.join_path = M.generate_join(M.get_path_sep())
local join_space = M.generate_join " "
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, 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()
local path_sep = M.get_path_sep()
return M.join_path(fn.stdpath "data", path_sep, "site")
end
-- 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.
function M.get_parser_install_dir(folder_name)
folder_name = folder_name or "parser"
local package_path = M.get_package_path()
local package_path_parser_dir = M.join_path(package_path, folder_name)
-- If package_path is read/write, use that
if luv.fs_access(package_path_parser_dir, "RW") then
return package_path_parser_dir
end
local site_dir = M.get_site_dir()
local path_sep = M.get_path_sep()
local parser_dir = M.join_path(site_dir, path_sep, folder_name)
-- Try creating and using parser_dir if it doesn't exist
if not luv.fs_stat(parser_dir) then
local ok, error = pcall(vim.fn.mkdir, parser_dir, "p", "0755")
if not ok then
return nil, join_space("Couldn't create parser dir", parser_dir, ":", error)
end
return parser_dir
end
-- parser_dir exists, use it if it's read/write
if luv.fs_access(parser_dir, "RW") then
return parser_dir
end
-- package_path isn't read/write, parser_dir exists but isn't read/write
-- either, give up
return nil, join_space("Invalid cache rights,", package_path, "or", parser_dir, "should be read/write")
end
function M.get_parser_info_dir()
return M.get_parser_install_dir "parser-info"
end
-- Gets a property at path
-- @param tbl the table to access
-- @param path the '.' separated path
-- @returns 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)
local result = tbl
for _, segment in ipairs(segments) do
if type(result) == "table" then
result = result[segment]
end
end
return result
end
function M.set_jump()
vim.cmd "normal! m'"
end
function M.index_of(tbl, obj)
for i, o in ipairs(tbl) do
if o == obj then
return i
end
end
end
-- Filters a list based on the given predicate
-- @param tbl The list to filter
-- @param predicate 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.
-- @params tbl1 The first table
-- @params tbl2 The second 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
function M.constant(a)
return function()
return a
end
end
function M.to_func(a)
return type(a) == "function" and a or M.constant(a)
end
function M.ts_cli_version()
if fn.executable "tree-sitter" == 1 then
local handle = io.popen "tree-sitter -V"
local result = handle:read "*a"
handle:close()
return vim.split(result, "\n")[1]:match "[^tree%psitter ].*"
end
end
return M

View File

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

View File

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

View File

@ -0,0 +1,106 @@
" Last Change: 2020 Aug 13
if exists('g:loaded_nvim_treesitter')
finish
endif
augroup NvimTreesitter
" on every query file write we want to set an autocommand that will reload the cache
autocmd FileType query
\ autocmd! NvimTreesitter BufWritePost <buffer> call v:lua.require('nvim-treesitter.query').invalidate_query_file(expand('%:p'))
augroup END
let g:loaded_nvim_treesitter = 1
lua require'nvim-treesitter'.setup()
function s:has_attr(attr, mode)
let norm_color = synIDattr(hlID('Normal'), a:attr, a:mode)
return strlen(norm_color) > 0
endfunction
" if the ctermfg or guifg is not known by nvim then using the
" fg or foreground highlighting value will cause an E419 error
" so we check to see if either highlight has been set if not default to NONE
let cterm_normal = s:has_attr('fg', 'cterm') ? 'fg' : 'NONE'
let gui_normal = s:has_attr('fg', 'gui') ? 'foreground' : 'NONE'
execute 'highlight default TSNone term=NONE cterm=NONE gui=NONE guifg='.gui_normal.' ctermfg='.cterm_normal
highlight default link TSPunctDelimiter Delimiter
highlight default link TSPunctBracket Delimiter
highlight default link TSPunctSpecial Delimiter
highlight default link TSConstant Constant
highlight default link TSConstBuiltin Special
highlight default link TSConstMacro Define
highlight default link TSString String
highlight default link TSStringRegex String
highlight default link TSStringEscape SpecialChar
highlight default link TSStringSpecial SpecialChar
highlight default link TSCharacter Character
highlight default link TSCharacterSpecial SpecialChar
highlight default link TSNumber Number
highlight default link TSBoolean Boolean
highlight default link TSFloat Float
highlight default link TSFunction Function
highlight default link TSFuncBuiltin Special
highlight default link TSFuncMacro Macro
highlight default link TSParameter Identifier
highlight default link TSParameterReference TSParameter
highlight default link TSMethod Function
highlight default link TSField Identifier
highlight default link TSProperty Identifier
highlight default link TSConstructor Special
highlight default link TSAnnotation PreProc
highlight default link TSAttribute PreProc
highlight default link TSNamespace Include
highlight default link TSSymbol Identifier
highlight default link TSConditional Conditional
highlight default link TSRepeat Repeat
highlight default link TSLabel Label
highlight default link TSOperator Operator
highlight default link TSKeyword Keyword
highlight default link TSKeywordFunction Keyword
highlight default link TSKeywordOperator TSOperator
highlight default link TSKeywordReturn TSKeyword
highlight default link TSException Exception
highlight default link TSDebug Debug
highlight default link TSDefine Define
highlight default link TSPreProc PreProc
highlight default link TSStorageClass StorageClass
highlight default link TSTodo Todo
highlight default link TSType Type
highlight default link TSTypeBuiltin Type
highlight default link TSTypeQualifier Type
highlight default link TSTypeDefinition Typedef
highlight default link TSInclude Include
highlight default link TSVariableBuiltin Special
highlight default link TSText TSNone
highlight default TSStrong term=bold cterm=bold gui=bold
highlight default TSEmphasis term=italic cterm=italic gui=italic
highlight default TSUnderline term=underline cterm=underline gui=underline
highlight default TSStrike term=strikethrough cterm=strikethrough gui=strikethrough
highlight default link TSMath Special
highlight default link TSTextReference Constant
highlight default link TSEnvironment Macro
highlight default link TSEnvironmentName Type
highlight default link TSTitle Title
highlight default link TSLiteral String
highlight default link TSURI Underlined
highlight default link TSComment Comment
highlight default link TSNote SpecialComment
highlight default link TSWarning Todo
highlight default link TSDanger WarningMsg
highlight default link TSTag Label
highlight default link TSTagDelimiter Delimiter
highlight default link TSTagAttribute TSProperty

View File

@ -0,0 +1 @@
; inherits: html

View File

@ -0,0 +1,5 @@
; inherits: html
[ "---" ] @punctuation.delimiter
[ "{" "}" ] @punctuation.special

View File

@ -0,0 +1 @@
; inherits: html

View File

@ -0,0 +1,7 @@
; inherits: html
((frontmatter
(raw_text) @typescript))
((interpolation
(raw_text) @tsx))

View File

@ -0,0 +1 @@
; inherits: html

View File

@ -0,0 +1,8 @@
[
(function_definition)
(if_statement)
(case_statement)
(for_statement)
(while_statement)
(c_style_for_statement)
] @fold

View File

@ -0,0 +1,131 @@
(simple_expansion) @none
(expansion
"${" @punctuation.special
"}" @punctuation.special) @none
[
"("
")"
"(("
"))"
"{"
"}"
"["
"]"
"[["
"]]"
] @punctuation.bracket
[
";"
";;"
(heredoc_start)
] @punctuation.delimiter
[
"$"
] @punctuation.special
[
">"
">>"
"<"
"<<"
"&"
"&&"
"|"
"||"
"="
"=~"
"=="
"!="
] @operator
[
(string)
(raw_string)
(heredoc_body)
] @string
(variable_assignment (word) @string)
[
"if"
"then"
"else"
"elif"
"fi"
"case"
"in"
"esac"
] @conditional
[
"for"
"do"
"done"
"while"
] @repeat
[
"declare"
"export"
"local"
"readonly"
"unset"
] @keyword
"function" @keyword.function
(special_variable_name) @constant
((word) @constant.builtin
(#match? @constant.builtin "^SIG(INT|TERM|QUIT|TIN|TOU|STP|HUP)$"))
((word) @boolean
(#match? @boolean "^(true|false)$"))
(comment) @comment
(test_operator) @string
(command_substitution
[ "$(" ")" ] @punctuation.bracket)
(process_substitution
[ "<(" ")" ] @punctuation.bracket)
(function_definition
name: (word) @function)
(command_name (word) @function)
((command_name (word) @function.builtin)
(#any-of? @function.builtin
"alias" "cd" "clear" "echo" "eval" "exit" "getopts" "popd"
"pushd" "return" "set" "shift" "shopt" "source" "test"))
(command
argument: [
(word) @parameter
(concatenation (word) @parameter)
])
((word) @number
(#lua-match? @number "^[0-9]+$"))
(file_redirect
descriptor: (file_descriptor) @operator
destination: (word) @parameter)
(expansion
[ "${" "}" ] @punctuation.bracket)
(variable_name) @variable
((variable_name) @constant
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
(case_item
value: (word) @parameter)
(regex) @string.regex

View File

@ -0,0 +1 @@
(comment) @comment

View File

@ -0,0 +1,13 @@
; Scopes
(function_definition) @scope
; Definitions
(variable_assignment
name: (variable_name) @definition.var)
(function_definition
name: (word) @definition.function)
; References
(variable_name) @reference
(word) @reference

View File

@ -0,0 +1,4 @@
[
(transaction)
(heading)
] @fold

View File

@ -0,0 +1,26 @@
(date) @field
(txn) @attribute
(account) @type
(amount) @number
(incomplete_amount) @number
(amount_tolerance) @number
(currency) @property
(key) @label
(string) @string
(tag) @constant
(link) @constant
(comment) @comment
[
(balance) (open) (close) (commodity) (pad)
(event) (price) (note) (document) (query)
(custom) (pushtag) (poptag) (pushmeta)
(popmeta) (option) (include) (plugin)
] @keyword

View File

@ -0,0 +1,3 @@
[
(entry)
] @fold

View File

@ -0,0 +1,49 @@
; CREDITS @pfoerster (adapted from https://github.com/latex-lsp/tree-sitter-bibtex)
[
(string_type)
(preamble_type)
(entry_type)
] @keyword
[
(junk)
(comment)
] @comment
[
"="
"#"
] @operator
(command) @function.builtin
(number) @number
(field
name: (identifier) @field)
(token
(identifier) @parameter)
[
(brace_word)
(quote_word)
] @string
[
(key_brace)
(key_paren)
] @symbol
(string
name: (identifier) @constant)
[
"{"
"}"
"("
")"
] @punctuation.bracket
"," @punctuation.delimiter

View File

@ -0,0 +1,10 @@
[
(entry)
] @indent
[
"{"
"}"
] @branch
(comment) @ignore

View File

@ -0,0 +1,19 @@
[
(for_statement)
(if_statement)
(while_statement)
(switch_statement)
(case_statement)
(function_definition)
(struct_specifier)
(enum_specifier)
(comment)
(preproc_if)
(preproc_elif)
(preproc_else)
(preproc_ifdef)
(initializer_list)
] @fold
(compound_statement
(compound_statement) @fold)

View File

@ -0,0 +1,187 @@
(identifier) @variable
[
"const"
"default"
"enum"
"extern"
"inline"
"static"
"struct"
"typedef"
"union"
"volatile"
"goto"
"register"
] @keyword
"sizeof" @keyword.operator
"return" @keyword.return
[
"while"
"for"
"do"
"continue"
"break"
] @repeat
[
"if"
"else"
"case"
"switch"
] @conditional
"#define" @constant.macro
[
"#if"
"#ifdef"
"#ifndef"
"#else"
"#elif"
"#endif"
(preproc_directive)
] @keyword
"#include" @include
[
"="
"-"
"*"
"/"
"+"
"%"
"~"
"|"
"&"
"^"
"<<"
">>"
"->"
"<"
"<="
">="
">"
"=="
"!="
"!"
"&&"
"||"
"-="
"+="
"*="
"/="
"%="
"|="
"&="
"^="
">>="
"<<="
"--"
"++"
] @operator
[
(true)
(false)
] @boolean
[ "." ";" ":" "," ] @punctuation.delimiter
"..." @punctuation.special
(conditional_expression [ "?" ":" ] @conditional)
[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket
(string_literal) @string
(system_lib_string) @string
(escape_sequence) @string.escape
(null) @constant.builtin
(number_literal) @number
(char_literal) @character
[
(preproc_arg)
(preproc_defined)
] @function.macro
(((field_expression
(field_identifier) @property)) @_parent
(#not-has-parent? @_parent template_method function_declarator call_expression))
(((field_identifier) @property)
(#has-ancestor? @property field_declaration)
(#not-has-ancestor? @property function_declarator))
(statement_identifier) @label
[
(type_identifier)
(primitive_type)
(sized_type_specifier)
(type_descriptor)
] @type
(sizeof_expression value: (parenthesized_expression (identifier) @type))
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
(enumerator
name: (identifier) @constant)
(case_statement
value: (identifier) @constant)
;; Preproc def / undef
(preproc_def
name: (_) @constant)
(preproc_call
directive: (preproc_directive) @_u
argument: (_) @constant
(#eq? @_u "#undef"))
(call_expression
function: (identifier) @function)
(call_expression
function: (field_expression
field: (field_identifier) @function))
(function_declarator
declarator: (identifier) @function)
(preproc_function_def
name: (identifier) @function.macro)
(comment) @comment
;; Parameters
(parameter_declaration
declarator: (identifier) @parameter)
(parameter_declaration
declarator: (pointer_declarator) @parameter)
(preproc_params (identifier) @parameter)
[
"__attribute__"
"__cdecl"
"__clrcall"
"__stdcall"
"__fastcall"
"__thiscall"
"__vectorcall"
"_unaligned"
"__unaligned"
"__declspec"
] @attribute
(ERROR) @error

View File

@ -0,0 +1,49 @@
[
(compound_statement)
(preproc_arg)
(field_declaration_list)
(case_statement)
(enumerator_list)
(struct_specifier)
(compound_literal_expression)
(initializer_list)
(while_statement)
(for_statement)
(switch_statement)
(expression_statement)
] @indent
(if_statement condition: (_) @indent)
((if_statement
consequence: (_) @_consequence
(#not-has-type? @_consequence compound_statement)
) @indent)
(init_declarator) @indent
(compound_statement "}" @indent_end)
[
"else"
")"
"}"
(statement_identifier)
] @branch
[
"#define"
"#ifdef"
"#if"
"#else"
"#endif"
] @zero_indent
[
(preproc_arg)
(string_literal)
] @ignore
((ERROR (parameter_declaration)) @aligned_indent
(#set! "delimiter" "()"))
([(argument_list) (parameter_list)] @aligned_indent
(#set! "delimiter" "()"))
(comment) @auto

View File

@ -0,0 +1,3 @@
(preproc_arg) @c
(comment) @comment

View File

@ -0,0 +1,53 @@
;; Functions definitions
(function_declarator
declarator: (identifier) @definition.function)
(preproc_function_def
name: (identifier) @definition.macro) @scope
(preproc_def
name: (identifier) @definition.macro)
(pointer_declarator
declarator: (identifier) @definition.var)
(parameter_declaration
declarator: (identifier) @definition.parameter)
(init_declarator
declarator: (identifier) @definition.var)
(array_declarator
declarator: (identifier) @definition.var)
(declaration
declarator: (identifier) @definition.var)
(enum_specifier
name: (_) @definition.type
(enumerator_list
(enumerator name: (identifier) @definition.var)))
;; Type / Struct / Enum
(field_declaration
declarator: (field_identifier) @definition.field)
(type_definition
declarator: (type_identifier) @definition.type)
(struct_specifier
name: (type_identifier) @definition.type)
;; goto
(labeled_statement (statement_identifier) @definition)
;; References
(identifier) @reference
((field_identifier) @reference
(set! reference.kind "field"))
((type_identifier) @reference
(set! reference.kind "type"))
(goto_statement (statement_identifier) @reference)
;; Scope
[
(for_statement)
(if_statement)
(while_statement)
(translation_unit)
(function_definition)
(compound_statement) ; a block in curly braces
(struct_specifier)
] @scope

View File

@ -0,0 +1,15 @@
body: [
(declaration_list)
(switch_body)
(enum_member_declaration_list)
] @fold
accessors: [
(accessor_list)
] @fold
initializer: [
(initializer_expression)
] @fold
(block) @fold

View File

@ -0,0 +1,390 @@
(identifier) @variable
((identifier) @keyword
(#eq? @keyword "value")
(#has-ancestor? @keyword accessor_declaration))
(method_declaration
name: (identifier) @method)
(local_function_statement
name: (identifier) @method)
(method_declaration
type: (identifier) @type)
(local_function_statement
type: (identifier) @type)
(interpolation) @none
(invocation_expression
(member_access_expression
name: (identifier) @method))
(invocation_expression
function: (conditional_access_expression
(member_binding_expression
name: (identifier) @method)))
(namespace_declaration
name: [(qualified_name) (identifier)] @namespace)
(qualified_name
(identifier) @type)
(invocation_expression
(identifier) @method)
(field_declaration
(variable_declaration
(variable_declarator
(identifier) @field)))
(initializer_expression
(assignment_expression
left: (identifier) @field))
(parameter_list
(parameter
name: (identifier) @parameter))
(parameter_list
(parameter
type: (identifier) @type))
(integer_literal) @number
(real_literal) @float
(null_literal) @constant.builtin
(character_literal) @character
[
(string_literal)
(verbatim_string_literal)
(interpolated_string_expression)
] @string
(boolean_literal) @boolean
[
(predefined_type)
(void_keyword)
] @type.builtin
(implicit_type) @keyword
(comment) @comment
(using_directive
(identifier) @type)
(property_declaration
name: (identifier) @property)
(property_declaration
type: (identifier) @type)
(nullable_type
(identifier) @type)
(catch_declaration
type: (identifier) @type)
(interface_declaration
name: (identifier) @type)
(class_declaration
name: (identifier) @type)
(record_declaration
name: (identifier) @type)
(enum_declaration
name: (identifier) @type)
(constructor_declaration
name: (identifier) @constructor)
(constructor_initializer [
"base" @constructor
])
(variable_declaration
(identifier) @type)
(object_creation_expression
(identifier) @type)
; Generic Types.
(type_of_expression
(generic_name
(identifier) @type))
(type_argument_list
(generic_name
(identifier) @type))
(base_list
(generic_name
(identifier) @type))
(type_constraint
(generic_name
(identifier) @type))
(object_creation_expression
(generic_name
(identifier) @type))
(property_declaration
(generic_name
(identifier) @type))
(_
type: (generic_name
(identifier) @type))
; Generic Method invocation with generic type
(invocation_expression
function: (generic_name
. (identifier) @method))
(invocation_expression
(member_access_expression
(generic_name
(identifier) @method)))
(base_list
(identifier) @type)
(type_argument_list
(identifier) @type)
(type_parameter_list
(type_parameter) @type)
(type_parameter_constraints_clause
target: (identifier) @type)
(attribute
name: (identifier) @attribute)
(for_each_statement
type: (identifier) @type)
(tuple_element
type: (identifier) @type)
(tuple_expression
(argument
(declaration_expression
type: (identifier) @type)))
(as_expression
right: (identifier) @type)
(type_of_expression
(identifier) @type)
(name_colon
(identifier) @parameter)
(warning_directive) @text.warning
(error_directive) @exception
(define_directive
(identifier) @constant) @constant.macro
(undef_directive
(identifier) @constant) @constant.macro
(line_directive) @constant.macro
(line_directive
(preproc_integer_literal) @constant
(preproc_string_literal)? @string)
(pragma_directive
(identifier) @constant) @constant.macro
(pragma_directive
(preproc_string_literal) @string) @constant.macro
[
(nullable_directive)
(region_directive)
(endregion_directive)
] @constant.macro
[
"if"
"else"
"switch"
"break"
"case"
(if_directive)
(elif_directive)
(else_directive)
(endif_directive)
] @conditional
(if_directive
(identifier) @constant)
(elif_directive
(identifier) @constant)
[
"while"
"for"
"do"
"continue"
"goto"
"foreach"
] @repeat
[
"try"
"catch"
"throw"
"finally"
] @exception
[
"+"
"?"
":"
"++"
"-"
"--"
"&"
"&&"
"|"
"||"
"!"
"!="
"=="
"*"
"/"
"%"
"<"
"<="
">"
">="
"="
"-="
"+="
"*="
"/="
"%="
"^"
"^="
"&="
"|="
"~"
">>"
"<<"
"<<="
">>="
"=>"
] @operator
[
";"
"."
","
":"
] @punctuation.delimiter
[
"["
"]"
"{"
"}"
"("
")"
"<"
">"
] @punctuation.bracket
[
(this_expression)
(base_expression)
] @variable.builtin
[
"using"
] @include
(alias_qualified_name
(identifier "global") @include)
[
"with"
"new"
"typeof"
"nameof"
"sizeof"
"is"
"as"
"and"
"or"
"not"
"stackalloc"
"in"
"out"
"ref"
] @keyword.operator
[
"lock"
"params"
"operator"
"default"
"abstract"
"const"
"extern"
"implicit"
"explicit"
"internal"
"override"
"private"
"protected"
"public"
"internal"
"partial"
"readonly"
"sealed"
"static"
"virtual"
"volatile"
"async"
"await"
"class"
"delegate"
"enum"
"interface"
"namespace"
"struct"
"get"
"set"
"init"
"where"
"record"
"event"
"add"
"remove"
"checked"
"unchecked"
"fixed"
] @keyword
(parameter_modifier) @operator
(query_expression
(_ [
"from"
"orderby"
"select"
"group"
"by"
"ascending"
"descending"
"equals"
"let"
] @keyword))
[
"return"
"yield"
] @keyword.return

View File

@ -0,0 +1 @@
(comment) @comment

View File

@ -0,0 +1,41 @@
;; Definitions
(variable_declarator
. (identifier) @definition.var)
(variable_declarator
(tuple_pattern
(identifier) @definition.var))
(declaration_expression
name: (identifier) @definition.var)
(for_each_statement
left: (identifier) @definition.var)
(for_each_statement
left: (tuple_pattern
(identifier) @definition.var))
(parameter
(identifier) @definition.parameter)
(method_declaration
name: (identifier) @definition.method)
(local_function_statement
name: (identifier) @definition.method)
(property_declaration
name: (identifier) @definition)
(type_parameter
(identifier) @definition.type)
(class_declaration
name: (identifier) @definition)
;; References
(identifier) @reference
;; Scope
(block) @scope

View File

@ -0,0 +1 @@
(source (list_lit) @fold)

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