mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-01 19:17:02 -04:00
Compare commits
No commits in common. "main" and "v0.8.3" have entirely different histories.
1896 changed files with 30718 additions and 81337 deletions
31
.emmyrc.json
31
.emmyrc.json
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json",
|
||||
"format": {
|
||||
"externalTool": {
|
||||
"program": "stylua",
|
||||
"args": [
|
||||
"-",
|
||||
"--stdin-filepath",
|
||||
"${file}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"diagnostics": {
|
||||
"disable": [
|
||||
"unnecessary-if",
|
||||
"incomplete-signature-doc"
|
||||
],
|
||||
"enables": [
|
||||
"iter-variable-reassign",
|
||||
"non-literal-expressions-in-assert",
|
||||
"missing-global-doc"
|
||||
]
|
||||
},
|
||||
"codeAction": {
|
||||
"insertSpace": true
|
||||
},
|
||||
"strict": {
|
||||
"typeCall": true,
|
||||
"arrayIndex": true
|
||||
}
|
||||
}
|
||||
4
.gitattributes
vendored
4
.gitattributes
vendored
|
|
@ -1,4 +0,0 @@
|
|||
runtime/queries/**/*.scm linguist-language=tsq
|
||||
doc/*.txt linguist-documentation
|
||||
SUPPORTED_LANGUAGES.md linguist-generated
|
||||
lua/nvim-treesitter/async.lua linguist-vendored
|
||||
7
.github/CODEOWNERS
vendored
Normal file
7
.github/CODEOWNERS
vendored
Normal 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
|
||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
open_collective: "nvim-treesitter"
|
||||
github: "nvim-treesitter"
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
type: 'bug'
|
||||
labels: [ bug ]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
|
|||
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
7
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,9 +1,10 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
type: enhancement
|
||||
assignees: ""
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
|
|
|||
13
.github/ISSUE_TEMPLATE/highlighting_issue.yml
vendored
13
.github/ISSUE_TEMPLATE/highlighting_issue.yml
vendored
|
|
@ -1,7 +1,6 @@
|
|||
name: Highlighting issue
|
||||
description: Missing or incorrect highlights or you want to change the way something is highlighted
|
||||
type: 'bug'
|
||||
labels: [highlights]
|
||||
labels: [ highlights ]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
@ -12,10 +11,10 @@ body:
|
|||
- 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 `:InspectTree` and made sure
|
||||
- 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 `:Inspect` 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).
|
||||
- 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:
|
||||
|
|
@ -35,8 +34,8 @@ body:
|
|||
attributes:
|
||||
label: Tree-sitter parsing result
|
||||
description: |
|
||||
Please provide the output of `:InspectTree` (screenshot or plain text)
|
||||
with the following options enabled (pressing the key):
|
||||
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)
|
||||
|
|
@ -67,7 +66,7 @@ body:
|
|||
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
|
||||
`:Inspect`.
|
||||
`:TSHighlightCapturesUnderCursor`.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
|
|||
47
.github/PULL_REQUEST_TEMPLATE/new_language.md
vendored
47
.github/PULL_REQUEST_TEMPLATE/new_language.md
vendored
|
|
@ -1,47 +0,0 @@
|
|||
<!--
|
||||
Before proceeding, make sure you have read https://github.com/nvim-treesitter/nvim-treesitter/blob/main/CONTRIBUTING.md!
|
||||
Make sure to fill out all fields and read the checklist at the end.
|
||||
-->
|
||||
|
||||
# Name of language
|
||||
|
||||
<!-- Link to an official description of the language -->
|
||||
https://...
|
||||
|
||||
Language file extension, if applicable: (e.g. `.zu`)
|
||||
|
||||
<details>
|
||||
<summary>Representative code sample</summary>
|
||||
```
|
||||
max. 50 lines
|
||||
```
|
||||
</details>
|
||||
|
||||
## Parser repo
|
||||
|
||||
https://github.com/...
|
||||
|
||||
<details>
|
||||
<summary>Parsed tree for code sample</summary>
|
||||
```
|
||||
paste output of tree-sitter parse or :InspectTree here
|
||||
```
|
||||
</details>
|
||||
|
||||
## Queries
|
||||
|
||||
Source of queries: https://github.com/... (or "written from scratch")
|
||||
|
||||
<details>
|
||||
<summary>Screenshots of code sample</summary>
|
||||
<!-- paste screenshot of code sample using provided queries here -->
|
||||
</details>
|
||||
|
||||
<!--
|
||||
CHECKLIST: _Before_ submitting, make sure
|
||||
|
||||
* `./scripts/install-parsers.lua <language>` works without warnings
|
||||
* `./scripts/install-parsers.lua --generate <language>` works without warnings
|
||||
* `make query` works without warning
|
||||
* `make docs` is run
|
||||
-->
|
||||
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
|
|
@ -1,16 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
cooldown:
|
||||
default-days: 3
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
labels:
|
||||
- "CI"
|
||||
groups:
|
||||
actions:
|
||||
patterns: ["*"]
|
||||
|
||||
37
.github/mergify.yml
vendored
Normal file
37
.github/mergify.yml
vendored
Normal 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
|
||||
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
|
|
@ -1,5 +0,0 @@
|
|||
<!--
|
||||
Before proceeding, make sure you have read https://github.com/nvim-treesitter/nvim-treesitter/blob/main/CONTRIBUTING.md!
|
||||
If you are adding a new parser, use this link instead:
|
||||
<https://github.com/nvim-treesitter/nvim-treesitter/compare/main...my-branch?quick_pull=1&template=new_language.md>
|
||||
-->
|
||||
62
.github/workflows/downstream.yml
vendored
62
.github/workflows/downstream.yml
vendored
|
|
@ -1,62 +0,0 @@
|
|||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- "lua/nvim-treesitter/parsers.lua"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
test-downstream:
|
||||
name: Check downstream queries
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NVIM: "nvim"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: tree-sitter/setup-action/cli@v2
|
||||
|
||||
- name: Install and prepare Neovim
|
||||
env:
|
||||
NVIM_TAG: "nightly"
|
||||
run: |
|
||||
bash ./scripts/ci-install.sh
|
||||
|
||||
- name: Compile parsers
|
||||
run: $NVIM -l ./scripts/install-parsers.lua --max-jobs=10
|
||||
|
||||
- name: Set up ts_query_ls
|
||||
run: curl -fL https://github.com/ribru17/ts_query_ls/releases/latest/download/ts_query_ls-x86_64-unknown-linux-gnu.tar.gz | tar -xz
|
||||
|
||||
- name: Clone textobjects
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: nvim-treesitter/nvim-treesitter-textobjects
|
||||
ref: main
|
||||
path: .tests/nvim-treesitter-textobjects
|
||||
sparse-checkout: queries
|
||||
|
||||
- name: Check textobjects
|
||||
working-directory: .tests/nvim-treesitter-textobjects/
|
||||
run: ../../ts_query_ls check queries/
|
||||
|
||||
- name: Clone context
|
||||
if: always()
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: nvim-treesitter/nvim-treesitter-context
|
||||
ref: master
|
||||
path: .tests/nvim-treesitter-context
|
||||
sparse-checkout: queries
|
||||
|
||||
- name: Check context
|
||||
if: always()
|
||||
working-directory: .tests/nvim-treesitter-context/
|
||||
run: ../../ts_query_ls check queries/
|
||||
|
||||
62
.github/workflows/lint.yml
vendored
62
.github/workflows/lint.yml
vendored
|
|
@ -1,50 +1,34 @@
|
|||
name: Lint
|
||||
name: Linting and style checking
|
||||
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
workflow_dispatch:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
lua:
|
||||
name: Lint Lua files
|
||||
runs-on: ubuntu-slim
|
||||
luacheck:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Format
|
||||
- name: Prepare
|
||||
run: |
|
||||
make formatlua
|
||||
git diff --exit-code
|
||||
sudo apt-get update
|
||||
sudo apt-get install luarocks -y
|
||||
sudo luarocks install luacheck
|
||||
|
||||
- name: Lint
|
||||
run: make checklua
|
||||
- name: Run Luacheck
|
||||
run: luacheck .
|
||||
|
||||
queries:
|
||||
name: Lint query files
|
||||
runs-on: ubuntu-slim
|
||||
stylua:
|
||||
name: StyLua
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Format
|
||||
run: |
|
||||
make formatquery
|
||||
git diff --exit-code
|
||||
|
||||
- name: Lint
|
||||
run: make lintquery
|
||||
|
||||
readme:
|
||||
name: Lint docs
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Check SUPPORTED_LANGUAGES
|
||||
run: |
|
||||
make docs
|
||||
git diff --exit-code
|
||||
- uses: actions/checkout@v3
|
||||
- name: Lint with stylua
|
||||
uses: JohnnyMorganz/stylua-action@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
args: --check .
|
||||
|
|
|
|||
58
.github/workflows/test-core.yml
vendored
58
.github/workflows/test-core.yml
vendored
|
|
@ -1,58 +0,0 @@
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
type:
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
check_compilation:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
nvim_tag: [stable, nightly]
|
||||
name: ${{matrix.nvim_tag}} / ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
NVIM: ${{ matrix.os == 'windows-latest' && 'nvim-win64\\bin\\nvim.exe' || 'nvim' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: tree-sitter/setup-action/cli@v2
|
||||
|
||||
- name: Install and prepare Neovim
|
||||
env:
|
||||
NVIM_TAG: ${{ matrix.nvim_tag }}
|
||||
run: |
|
||||
bash ./scripts/ci-install.sh
|
||||
|
||||
- if: inputs.type == 'build'
|
||||
name: Compile parsers
|
||||
run: $NVIM -l ./scripts/install-parsers.lua --max-jobs=10
|
||||
|
||||
- if: inputs.type == 'generate'
|
||||
name: Generate and compile parsers
|
||||
run: $NVIM -l ./scripts/install-parsers.lua --generate --max-jobs=2
|
||||
|
||||
- name: Check parsers
|
||||
run: $NVIM -l ./scripts/check-parsers.lua
|
||||
|
||||
- name: Check queries (nvim)
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: $NVIM -l ./scripts/check-queries.lua
|
||||
|
||||
- name: Check queries (tsqueryls)
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: make checkquery
|
||||
|
||||
- name: Run highlight tests
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: make tests TESTS=query NVIM_BIN=$NVIM
|
||||
|
||||
- name: Run indents tests
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: make tests TESTS=indent NVIM_BIN=$NVIM
|
||||
20
.github/workflows/test-generate.yml
vendored
20
.github/workflows/test-generate.yml
vendored
|
|
@ -1,20 +0,0 @@
|
|||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [unlabeled, labeled, opened, synchronize, reopened]
|
||||
branches:
|
||||
- "main"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-generate-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check_compilation:
|
||||
name: Generate
|
||||
if: contains(github.event.pull_request.labels.*.name, 'ci:generate') || github.event_name == 'workflow_dispatch'
|
||||
uses: ./.github/workflows/test-core.yml
|
||||
with:
|
||||
type: "generate"
|
||||
85
.github/workflows/test-queries.yml
vendored
85
.github/workflows/test-queries.yml
vendored
|
|
@ -1,21 +1,86 @@
|
|||
name: Tests
|
||||
name: Test queries
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- 'master'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- "main"
|
||||
workflow_dispatch:
|
||||
- 'master'
|
||||
|
||||
# Cancel any in-progress CI runs for a PR if it is updated
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-build-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
check_compilation:
|
||||
name: Build
|
||||
uses: ./.github/workflows/test-core.yml
|
||||
with:
|
||||
type: "build"
|
||||
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' && 'nvim-win64\\bin\\nvim.exe' || 'nvim' }}
|
||||
ALLOWED_INSTALLATION_FAILURES: ${{ matrix.os == 'windows-2022' && 'rnoweb' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
- name: Install and prepare Neovim
|
||||
env:
|
||||
NVIM_TAG: ${{ matrix.nvim_tag }}
|
||||
TREE_SITTER_CLI_TAG: v0.20.6
|
||||
run: |
|
||||
bash ./scripts/ci-install-${{ matrix.os }}.sh
|
||||
|
||||
- name: Setup Parsers Cache
|
||||
id: parsers-cache
|
||||
uses: actions/cache@v3
|
||||
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_command_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
|
||||
|
||||
- name: Check query files
|
||||
run: $NVIM --headless -c "luafile ./scripts/check-queries.lua" -c "q"
|
||||
|
|
|
|||
63
.github/workflows/tests.yml
vendored
Normal file
63
.github/workflows/tests.yml
vendored
Normal 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@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
- 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.6/highlight-assertions_v0.1.6_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.6
|
||||
run: |
|
||||
bash ./scripts/ci-install-${{ matrix.os }}.sh
|
||||
|
||||
- name: Setup Parsers Cache
|
||||
id: parsers-cache
|
||||
uses: actions/cache@v3
|
||||
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
|
||||
56
.github/workflows/update-lockfile.yml
vendored
Normal file
56
.github/workflows/update-lockfile.yml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
name: Update lockfile
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 6 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-lockfile:
|
||||
name: Update lockfile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Prepare
|
||||
env:
|
||||
NVIM_TAG: stable
|
||||
run: |
|
||||
wget https://github.com/josephburnett/jd/releases/download/v1.6.1/jd-amd64-linux
|
||||
mv ./jd-amd64-linux /tmp/jd
|
||||
chmod +x /tmp/jd
|
||||
sudo apt install libfuse2
|
||||
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: |
|
||||
cp lockfile.json /tmp/old_lockfile.json
|
||||
./nvim.appimage --headless -c "luafile ./scripts/write-lockfile.lua" -c "q"
|
||||
# Pretty print
|
||||
cp lockfile.json /tmp/lockfile.json
|
||||
cat /tmp/lockfile.json | 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
|
||||
UPDATED_PARSERS=$(/tmp/jd -f merge /tmp/old_lockfile.json lockfile.json | jq -r 'keys | join(", ")')
|
||||
echo "UPDATED_PARSERS=$UPDATED_PARSERS" >> $GITHUB_ENV
|
||||
git commit -m "Update parsers: $UPDATED_PARSERS" || echo 'No commit necessary!'
|
||||
git clean -xf
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
title: "Update lockfile.json: ${{ env.UPDATED_PARSERS }}"
|
||||
branch: update-lockfile-pr
|
||||
base: ${{ github.head_ref }}
|
||||
draft: true
|
||||
60
.github/workflows/update-parsers.yml
vendored
60
.github/workflows/update-parsers.yml
vendored
|
|
@ -1,60 +0,0 @@
|
|||
name: Update parsers
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 6 * * 6"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BIN_DIR: ${{ github.workspace }}/bin
|
||||
|
||||
jobs:
|
||||
update-parsers:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tier: [1, 2]
|
||||
name: Update parsers tier ${{ matrix.tier }}
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- uses: actions/create-github-app-token@v3
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.TOKEN_ID }}
|
||||
private-key: ${{ secrets.TOKEN_PRIVATE_KEY }}
|
||||
|
||||
- name: Add $BIN_DIR to PATH
|
||||
run: echo "$BIN_DIR" >> $GITHUB_PATH
|
||||
|
||||
- name: Prepare
|
||||
env:
|
||||
NVIM_TAG: nightly
|
||||
run: |
|
||||
bash scripts/ci-install.sh
|
||||
wget --directory-prefix="$BIN_DIR" https://github.com/JohnnyMorganz/StyLua/releases/latest/download/stylua-linux-x86_64.zip
|
||||
(cd "$BIN_DIR"; unzip stylua*.zip)
|
||||
|
||||
- name: Update parsers
|
||||
run: ./scripts/update-parsers.lua --tier=${{ matrix.tier }}
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
add-paths: lua/nvim-treesitter/parsers.lua
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
sign-commits: true
|
||||
commit-message: "bot(parsers): update ${{ env.UPDATED_PARSERS }}"
|
||||
title: "Update parsers (tier ${{ matrix.tier }}): ${{ env.UPDATED_PARSERS }}"
|
||||
body: "[beep boop](https://github.com/peter-evans/create-pull-request)"
|
||||
branch: update-parsers-tier-${{ matrix.tier }}
|
||||
base: ${{ github.head_ref }}
|
||||
|
||||
- name: Enable Pull Request Automerge
|
||||
if: ${{ matrix.tier == 2 }}
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: gh pr merge --rebase --auto update-parsers-tier-2
|
||||
42
.github/workflows/update-readme.yml
vendored
Normal file
42
.github/workflows/update-readme.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
name: Update README
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-readme:
|
||||
name: Update README
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
env:
|
||||
NVIM_TAG: stable
|
||||
run: |
|
||||
sudo apt install libfuse2
|
||||
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@v4
|
||||
with:
|
||||
commit-message: Update README
|
||||
title: Update README
|
||||
branch: update-readme-pr
|
||||
base: ${{ github.head_ref }}
|
||||
draft: true
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,8 +1,4 @@
|
|||
.test-deps
|
||||
doc/tags
|
||||
.luacheckcache
|
||||
/tags
|
||||
nvim.appimage
|
||||
nvim-linux-x86_64*
|
||||
nvim-macos*
|
||||
nvim-win64*
|
||||
|
|
|
|||
21
.luacheckrc
Normal file
21
.luacheckrc
Normal 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",
|
||||
}
|
||||
28
.luarc.json
28
.luarc.json
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"runtime": {
|
||||
"version": "LuaJIT"
|
||||
},
|
||||
"workspace": {
|
||||
"library": [
|
||||
"$VIMRUNTIME",
|
||||
"${3rd}/busted/library"
|
||||
],
|
||||
"ignoreDir": [
|
||||
".test-deps",
|
||||
"tests"
|
||||
],
|
||||
"checkThirdParty": "Disable"
|
||||
},
|
||||
"diagnostics": {
|
||||
"groupFileStatus": {
|
||||
"strict": "Opened",
|
||||
"strong": "Opened"
|
||||
},
|
||||
"groupSeverity": {
|
||||
"strong": "Warning",
|
||||
"strict": "Warning"
|
||||
},
|
||||
"unusedLocalExclude": [ "_*" ]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
column_width = 100
|
||||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferSingle"
|
||||
call_parentheses = "Always"
|
||||
quote_style = "AutoPreferDouble"
|
||||
call_parentheses = "None"
|
||||
|
|
|
|||
405
.tsqueryrc.json
405
.tsqueryrc.json
|
|
@ -1,405 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/ribru17/ts_query_ls/refs/heads/master/schemas/config.json",
|
||||
"parser_install_directories": ["${HOME}/.local/share/nvim/site/parser"],
|
||||
"supported_abi_versions": {
|
||||
"start": 13,
|
||||
"end": 15
|
||||
},
|
||||
"parser_aliases": {
|
||||
"html_tags": "html",
|
||||
"ecma": "javascript",
|
||||
"jsx": "javascript"
|
||||
},
|
||||
"valid_captures": {
|
||||
"highlights": {
|
||||
"variable": "various variable names",
|
||||
"variable.builtin": "built-in variable names (e.g. `this`)",
|
||||
"variable.parameter": "parameters of a function",
|
||||
"variable.parameter.builtin": "special parameters (e.g. `_`, `it`)",
|
||||
"variable.member": "object and struct fields",
|
||||
|
||||
"constant": "constant identifiers",
|
||||
"constant.builtin": "built-in constant values",
|
||||
"constant.macro": "constants defined by the preprocessor",
|
||||
|
||||
"module": "modules or namespaces",
|
||||
"module.builtin": "built-in modules or namespaces",
|
||||
"label": "GOTO and other labels (e.g. `label:` in C), including heredoc labels",
|
||||
|
||||
"string": "string literals",
|
||||
"string.documentation": "string documenting code (e.g. Python docstrings)",
|
||||
"string.regexp": "regular expressions",
|
||||
"string.escape": "escape sequences",
|
||||
"string.special": "other special strings (e.g. dates)",
|
||||
"string.special.symbol": "symbols or atoms",
|
||||
"string.special.url": "URIs (e.g. hyperlinks)",
|
||||
"string.special.path": "filenames",
|
||||
|
||||
"character": "character literals",
|
||||
"character.special": "special characters (e.g. wildcards)",
|
||||
|
||||
"boolean": "boolean literals",
|
||||
"number": "numeric literals",
|
||||
"number.float": "floating-point number literals",
|
||||
|
||||
"type": "type or class definitions and annotations",
|
||||
"type.builtin": "built-in types",
|
||||
"type.definition": "identifiers in type definitions (e.g. `typedef <type> <identifier>` in C)",
|
||||
|
||||
"attribute": "attribute annotations (e.g. Python decorators, Rust lifetimes)",
|
||||
"attribute.builtin": "builtin annotations (e.g. `@property` in Python)",
|
||||
"property": "the key in key/value pairs",
|
||||
|
||||
"function": "function definitions",
|
||||
"function.builtin": "built-in functions",
|
||||
"function.call": "function calls",
|
||||
"function.macro": "preprocessor macros",
|
||||
|
||||
"function.method": "method definitions",
|
||||
"function.method.call": "method calls",
|
||||
|
||||
"constructor": "constructor calls and definitions",
|
||||
"operator": "symbolic operators (e.g. `+` / `*`)",
|
||||
|
||||
"keyword": "keywords not fitting into specific categories",
|
||||
"keyword.coroutine": "keywords related to coroutines (e.g. `go` in Go, `async/await` in Python)",
|
||||
"keyword.function": "keywords that define a function (e.g. `func` in Go, `def` in Python)",
|
||||
"keyword.operator": "operators that are English words (e.g. `and` / `or`)",
|
||||
"keyword.import": "keywords for including or exporting modules (e.g. `import` / `from` in Python)",
|
||||
"keyword.type": "keywords describing namespaces and composite types (e.g. `struct`, `enum`)",
|
||||
"keyword.modifier": "keywords modifying other constructs (e.g. `const`, `static`, `public`)",
|
||||
"keyword.repeat": "keywords related to loops (e.g. `for` / `while`)",
|
||||
"keyword.return": "keywords like `return` and `yield`",
|
||||
"keyword.debug": "keywords related to debugging",
|
||||
"keyword.exception": "keywords related to exceptions (e.g. `throw` / `catch`)",
|
||||
"keyword.conditional": "keywords related to conditionals (e.g. `if` / `else`)",
|
||||
"keyword.conditional.ternary": "ternary operator (e.g. `?` / `:`)",
|
||||
"keyword.directive": "various preprocessor directives & shebangs",
|
||||
"keyword.directive.define": "preprocessor definition directives",
|
||||
|
||||
"punctuation.delimiter": "delimiters (e.g. `;` / `.` / `,`)",
|
||||
"punctuation.bracket": "brackets (e.g. `()` / `{}` / `[]`)",
|
||||
"punctuation.special": "special symbols (e.g. `{}` in string interpolation)",
|
||||
|
||||
"comment": "line and block comments",
|
||||
"comment.documentation": "comments documenting code",
|
||||
"comment.error": "error-type comments (e.g. `ERROR`, `FIXME`, `DEPRECATED`)",
|
||||
"comment.warning": "warning-type comments (e.g. `WARNING`, `FIX`, `HACK`)",
|
||||
"comment.todo": "todo-type comments (e.g. `TODO`, `WIP`)",
|
||||
"comment.note": "note-type comments (e.g. `NOTE`, `INFO`, `XXX`)",
|
||||
|
||||
"markup.strong": "bold text",
|
||||
"markup.italic": "italic text",
|
||||
"markup.strikethrough": "struck-through text",
|
||||
"markup.underline": "underlined text (only for literal underline markup!)",
|
||||
"markup.heading": "headings, titles (including markers)",
|
||||
"markup.heading.1": "top-level heading",
|
||||
"markup.heading.2": "section heading",
|
||||
"markup.heading.3": "subsection heading",
|
||||
"markup.heading.4": "and so on",
|
||||
"markup.heading.5": "and so forth",
|
||||
"markup.heading.6": "six levels ought to be enough for anybody",
|
||||
"markup.quote": "block quotes",
|
||||
"markup.math": "math environments (e.g. `$ ... $` in LaTeX)",
|
||||
"markup.link": "text references, footnotes, citations, etc.",
|
||||
"markup.link.label": "link, reference descriptions",
|
||||
"markup.link.url": "URL-style links",
|
||||
"markup.raw": "literal or verbatim text (e.g. inline code)",
|
||||
"markup.raw.block": "literal or verbatim text as a stand-alone block ; (use priority 90 for blocks with injections)",
|
||||
"markup.list": "list markers",
|
||||
"markup.list.checked": "checked todo-style list markers",
|
||||
"markup.list.unchecked": "unchecked todo-style list markers",
|
||||
|
||||
"diff.plus": "added text (for diff files)",
|
||||
"diff.minus": "deleted text (for diff files)",
|
||||
"diff.delta": "changed text (for diff files)",
|
||||
|
||||
"tag": "XML-style tag names (and similar)",
|
||||
"tag.builtin": "builtin tag names (e.g. HTML5 tags)",
|
||||
"tag.attribute": "XML-style tag attributes",
|
||||
"tag.delimiter": "XML-style tag delimiters",
|
||||
|
||||
"conceal": "captures that are only meant to be concealed",
|
||||
"spell": "for defining regions to be spellchecked",
|
||||
"nospell": "for defining regions that should NOT be spellchecked",
|
||||
"none": "completely disable the highlight"
|
||||
},
|
||||
"injections": {
|
||||
"injection.content": "indicates that the captured node should have its contents re-parsed using another language",
|
||||
"injection.language": "indicates that the captured node’s text may contain the name of a language that should be used to re-parse the `@injection.content`",
|
||||
"injection.filename": "indicates that the captured node’s text may contain a filename; the corresponding filetype is then looked-up up via `vim.filetype.match()` and treated as the name of a language that should be used to re-parse the `@injection.content`"
|
||||
},
|
||||
"folds": {
|
||||
"fold": "fold this node"
|
||||
},
|
||||
"indents": {
|
||||
"indent.begin": "Specifies that the next line should be indented. Multiple indents on the same line get collapsed. Indent can also have `indent.immediate` set using a `#set!` directive, which permits the next line to indent even when the block intended to be indented has no content yet, improving interactive typing.",
|
||||
"indent.end": "Used to specify that the indented region ends and any text subsequent to the capture should be dedented.",
|
||||
"indent.align": "Specifies aligned indent blocks (like python aligned/hanging indent). Specify the delimiters with `indent.open_delimiter` and `indent.close_delimiter` metadata. For some languages, the last line of an `indent.align` block must not be the same indent as the natural next line, which can be controlled by setting `indent.avoid_last_matching_next`.",
|
||||
"indent.dedent": "Specifies dedenting starting on the next line.",
|
||||
"indent.branch": "Used to specify that a dedented region starts at the line including the captured nodes.",
|
||||
"indent.ignore": "Specifies that indentation should be ignored for this node.",
|
||||
"indent.auto": "Behaves like 'autoindent' buffer option.",
|
||||
"indent.zero": "Sets indentation for this node to zero (no indentation)."
|
||||
},
|
||||
"locals": {
|
||||
"local.definition": "various definitions",
|
||||
"local.definition.constant": "constants",
|
||||
"local.definition.function": "functions",
|
||||
"local.definition.method": "methods",
|
||||
"local.definition.var": "variables",
|
||||
"local.definition.parameter": "parameters",
|
||||
"local.definition.macro": "preprocessor macros",
|
||||
"local.definition.type": "types or classes",
|
||||
"local.definition.field": "fields or properties",
|
||||
"local.definition.enum": "enumerations",
|
||||
"local.definition.namespace": "modules or namespaces",
|
||||
"local.definition.import": "imported names",
|
||||
"local.definition.associated": "the associated type of a variable",
|
||||
"local.scope": "scope block",
|
||||
"local.reference": "identifier reference"
|
||||
}
|
||||
},
|
||||
"valid_predicates": {
|
||||
"eq": {
|
||||
"any": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"arity": "required"
|
||||
}
|
||||
],
|
||||
"description": "checks for equality between two nodes, or a node and a string"
|
||||
},
|
||||
"any-of": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "variadic"
|
||||
}
|
||||
],
|
||||
"description": "match any of the given strings against the text corresponding to a node"
|
||||
},
|
||||
"contains": {
|
||||
"any": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "variadic"
|
||||
}
|
||||
],
|
||||
"description": "match a string against parts of the text corresponding to a node"
|
||||
},
|
||||
"match": {
|
||||
"any": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
}
|
||||
],
|
||||
"description": "Match a regexp against the text corresponding to a node"
|
||||
},
|
||||
"lua-match": {
|
||||
"any": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
}
|
||||
],
|
||||
"description": "match a Lua pattern against the text corresponding to a node"
|
||||
},
|
||||
"has-ancestor": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "named_node"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "variadic",
|
||||
"constraint": "named_node"
|
||||
}
|
||||
],
|
||||
"description": "match any of the given node types against all ancestors of a node"
|
||||
},
|
||||
"has-parent": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "named_node"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "variadic",
|
||||
"constraint": "named_node"
|
||||
}
|
||||
],
|
||||
"description": "match any of the given node types against the direct ancestor of a node"
|
||||
},
|
||||
"kind-eq": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "named_node"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "variadic",
|
||||
"constraint": "named_node"
|
||||
}
|
||||
],
|
||||
"description": "checks whether a capture corresponds to a given set of nodes"
|
||||
}
|
||||
},
|
||||
"valid_directives": {
|
||||
"set": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "any",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"arity": "optional"
|
||||
},
|
||||
{
|
||||
"type": "any",
|
||||
"arity": "optional"
|
||||
}
|
||||
],
|
||||
"description": "sets key/value metadata for a specific match or capture"
|
||||
},
|
||||
"offset": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required",
|
||||
"constraint": "integer"
|
||||
}
|
||||
],
|
||||
"description": "Takes the range of the captured node and applies an offset. This will set a new range in the form of a list like { {start_row}, {start_col}, {end_row}, {end_col} } for the captured node with `capture_id` as `metadata[capture_id].range`."
|
||||
},
|
||||
"gsub": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "required"
|
||||
}
|
||||
],
|
||||
"description": "Transforms the content of the node using a Lua pattern. This will set a new `metadata[capture_id].text`."
|
||||
},
|
||||
"trim": {
|
||||
"parameters": [
|
||||
{
|
||||
"type": "capture",
|
||||
"arity": "required"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "optional",
|
||||
"constraint": {
|
||||
"enum": ["0", "1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "optional",
|
||||
"constraint": {
|
||||
"enum": ["0", "1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "optional",
|
||||
"constraint": {
|
||||
"enum": ["0", "1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"arity": "optional",
|
||||
"constraint": {
|
||||
"enum": ["0", "1"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Trims whitespace from the node. Sets a new `metadata[capture_id].range`. Takes a capture ID and, optionally, four integers to customize trimming behavior (`1` meaning trim, `0` meaning don't trim). When only given a capture ID, trims blank lines (lines that contain only whitespace, or are empty) from the end of the node (for backwards compatibility). Can trim all whitespace from both sides of the node if parameters are given."
|
||||
}
|
||||
}
|
||||
}
|
||||
676
CONTRIBUTING.md
676
CONTRIBUTING.md
|
|
@ -1,103 +1,77 @@
|
|||
# Contributing to `nvim-treesitter`
|
||||
|
||||
The main parts of `nvim-treesitter` are
|
||||
* a curated list of [parsers](#Parsers);
|
||||
* a collection of [queries](#Queries).
|
||||
First of all, thank you very much for contributing to `nvim-treesitter`.
|
||||
|
||||
Before describing these in detail, some general advice:
|
||||
* Some basic knowledge of how tree-sitter works is assumed; we recommend reading
|
||||
- the [upstream documentation](https://tree-sitter.github.io/tree-sitter/);
|
||||
- [Neovim's documentation](https://neovim.io/doc/user/treesitter.html#treesitter).
|
||||
* There are dedicated Matrix channels for questions and general help:
|
||||
- [#nvim-treesitter](https://matrix.to/#/#nvim-treesitter:matrix.org) for questions specific to Neovim's implementation and the queries here;
|
||||
- [#tree-sitter](https://matrix.to/#/#tree-sitter-chat:matrix.org) for general questions regarding treesitter queries and the `tree-sitter` CLI.
|
||||
If you haven't already, you should really come and reach out to us on our
|
||||
[Matrix channel], so we can help you with any question you might have!
|
||||
|
||||
## Parsers
|
||||
As you know, `nvim-treesitter` is roughly split in two parts:
|
||||
|
||||
>[!IMPORTANT]
|
||||
> To qualify for inclusion, a parser must meet the following criteria:
|
||||
> * correspond to a filetype detected by Neovim (nightly)
|
||||
> * feature complete, tested by users, and actively maintained (according to maintainer discretion)
|
||||
> * hosted or mirrored on Github (other codeforges are not reliable enough for CI)
|
||||
> * covered by CI using [upstream workflows](https://github.com/tree-sitter/workflows)
|
||||
> * provide reference queries covered by a [`ts_query_ls` workflow](https://github.com/tree-sitter-grammars/template/blob/9c46d09d688d27c7aef31c2b32f50260de4e7906/.github/workflows/ci.yml#L69-L86)
|
||||
> * if the repo contains a `src/parser.c`, it must support the latest ABI
|
||||
> * if the repo does _not_ contain a `src/parser.c`, it must contain an up-to-date `src/grammar.json`
|
||||
> * if the repo contains an external scanner, it must be written in C99
|
||||
>
|
||||
> Tier 1 parsers (preferred) in addition need to
|
||||
> * make regular releases following semver (_patch_ for fixes not affecting queries; _minor_ for changes introducing new nodes or patterns; _major_ for changes removing nodes or previously valid patterns)
|
||||
> * provide WASM release artifacts
|
||||
- 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
|
||||
|
||||
To add a new parser, edit the following files:
|
||||
Depending on which part of the plugin you want to contribute to, please read the appropriate section.
|
||||
|
||||
1. In `lua/parsers.lua`, add an entry to the returned table of the following form:
|
||||
## Style Checks and Tests
|
||||
|
||||
```lua
|
||||
zimbu = {
|
||||
install_info = {
|
||||
url = 'https://github.com/zimbulang/tree-sitter-zimbu', -- git repo; use `path` for local path
|
||||
revision = 'v2.1', -- tag or commit hash
|
||||
-- optional entries:
|
||||
branch = 'develop', -- only needed if different from default branch
|
||||
location = 'parser', -- only needed if the parser is in subdirectory of a "monorepo"
|
||||
generate = true, -- only needed if repo does not contain pre-generated src/parser.c
|
||||
},
|
||||
maintainers = { '@me' }, -- the _query_ maintainers
|
||||
tier = 1, -- stable: track versioned releases instead of latest commit
|
||||
-- optional entries:
|
||||
requires = { 'vim' }, -- if the queries inherit from another language
|
||||
readme_note = "an example language",
|
||||
}
|
||||
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
|
||||
```
|
||||
|
||||
>[!IMPORTANT]
|
||||
> The "maintainers" here refers to the person maintaining the **queries** in `nvim-treesitter`, not the parser maintainers (who likely don't use Neovim). The maintainers' duty is to review issues and PRs related to the query and to keep them updated with respect to parser changes.
|
||||
## Adding new modules
|
||||
|
||||
2. If the parser name is not the same as the Vim filetype, add an entry to the `filetypes` table in `plugin/filetypes.lua`:
|
||||
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:
|
||||
|
||||
```lua
|
||||
zimbu = { 'zu' },
|
||||
```
|
||||
- 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`.
|
||||
|
||||
3. Update the list of [supported languages] by running `make docs` (or `./scripts/update-readme.lua` if on Windows).
|
||||
In any case, you can build your own module! To help you started in the process, we have a template
|
||||
repository designed to build new modules [here](https://github.com/nvim-treesitter/module-template).
|
||||
Feel free to use it, and contact us over on our
|
||||
on the "Neovim tree-sitter" [Matrix channel].
|
||||
|
||||
4. Test if both `:TSInstall zimbu` and `:TSInstallFromGrammar zimbu` work without errors (`:checkhealth treesitter` or `./scripts/check-parsers.lua zimbu`).
|
||||
## Parser configurations
|
||||
|
||||
>[!IMPORTANT]
|
||||
> You also need to add queries in order for the parser to actually be useful!
|
||||
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).
|
||||
|
||||
When you're done, open a Pull Request using the [provided template](.github/PULL_REQUEST_TEMPLATE/new_language.md), e.g. using `gh pr create -B main -T new_language`.
|
||||
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`:
|
||||
|
||||
## Queries
|
||||
- `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.
|
||||
|
||||
To add (or edit existing) queries, create a corresponding `runtime/queries/zimbu/*.scm` file:
|
||||
For these types there is a *norm* you will have to follow so that features work fine.
|
||||
Here are some global advices:
|
||||
|
||||
- `highlights.scm` used for syntax highlighting,
|
||||
- `injections.scm` used to specify nodes whose content should be parsed as a different language;
|
||||
- `folds.scm`; used to define folds;
|
||||
- `locals.scm`: used to extract keyword definitions, scopes, references, etc. (not used in this plugin).
|
||||
- `indents.scm`; used to control indentation.
|
||||
|
||||
See [tree-sitter queries] for a basic description of the query language. The following tools can be helpful when writing or editing queries:
|
||||
* [ts_query_ls] is a language server for treesitter queries, which can validate, autocomplete, and format. This tool can also be used as an offline linter and formatter (accessible through `make lintquery`, `make checkquery`, `make formatquery` targets).
|
||||
* Neovim's `:InspectTree` will show the parsed tree for a buffer and highlight the text corresponding to any given node (and vice versa).
|
||||
* `:EditQuery` opens a "playground" where you can write query patterns and see which parts of the buffer are captured by each capture.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> The valid captures that can be used in queries is different for each editor, so you cannot just copy them, e.g., from Helix or the parser repositories. For Neovim, all valid captures are listed below. You can verify that your changes adhere to this by running `make lintquery`.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> Since grammars can change constantly, it is important to make sure that the patterns in a query are actually valid for the parser specified in nvim-treesitter's manifest. This can be verified using `make checkquery` (which requires the parser to be installed in the default directory(!) through `nvim-treesitter`). Opening the query in Neovim with the parser installed will also show all invalid patterns, either via [ts_query_ls] or Neovim's builtin query-linter.
|
||||
|
||||
>[!TIP]
|
||||
> Before opening a PR, run `make query` to format, lint, and check all queries.
|
||||
|
||||
#### Inheriting languages
|
||||
- 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_:
|
||||
line of your file_.
|
||||
|
||||
```query
|
||||
; inherits: lang1,(optionallang)
|
||||
|
|
@ -106,405 +80,184 @@ line of your file_:
|
|||
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).
|
||||
|
||||
#### Formatting
|
||||
|
||||
All queries are expected to follow a standard format, with every node on a single line and indented by two spaces for each level of nesting. You can automatically format the bundled queries by running `make formatquery`.
|
||||
|
||||
Should you need to preserve a specific format for a node, you can exempt it (and all contained nodes) by placing before it
|
||||
```query
|
||||
; format-ignore
|
||||
```
|
||||
|
||||
### Highlights
|
||||
|
||||
Syntax highlighting is specified in a `highlights.scm` query, which assigns treesitter nodes to captures that can be assigned a highlight group. This feature is implemented in Neovim and documented at [`:h treesitter-highlight`](https://neovim.io/doc/user/treesitter.html#treesitter-highlight).
|
||||
Note that your color scheme needs to define (or link) these captures as highlight groups. You can use Neovim's built-in `:Inspect` function to see exactly which highlight groups are applied at a given position.
|
||||
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.
|
||||
|
||||
The valid captures are listed below.
|
||||
#### Misc
|
||||
|
||||
#### Identifiers
|
||||
|
||||
```query
|
||||
@variable ; various variable names
|
||||
@variable.builtin ; built-in variable names (e.g. `this`)
|
||||
@variable.parameter ; parameters of a function
|
||||
@variable.parameter.builtin ; special parameters (e.g. `_`, `it`)
|
||||
@variable.member ; object and struct fields
|
||||
|
||||
@constant ; constant identifiers
|
||||
@constant.builtin ; built-in constant values
|
||||
@constant.macro ; constants defined by the preprocessor
|
||||
|
||||
@module ; modules or namespaces
|
||||
@module.builtin ; built-in modules or namespaces
|
||||
@label ; GOTO and other labels (e.g. `label:` in C), including heredoc labels
|
||||
```
|
||||
|
||||
#### Literals
|
||||
|
||||
```query
|
||||
@string ; string literals
|
||||
@string.documentation ; string documenting code (e.g. Python docstrings)
|
||||
@string.regexp ; regular expressions
|
||||
@string.escape ; escape sequences
|
||||
@string.special ; other special strings (e.g. dates)
|
||||
@string.special.symbol ; symbols or atoms
|
||||
@string.special.url ; URIs (e.g. hyperlinks)
|
||||
@string.special.path ; filenames
|
||||
|
||||
@character ; character literals
|
||||
@character.special ; special characters (e.g. wildcards)
|
||||
|
||||
@boolean ; boolean literals
|
||||
@number ; numeric literals
|
||||
@number.float ; floating-point number literals
|
||||
```
|
||||
|
||||
#### Types
|
||||
|
||||
```query
|
||||
@type ; type or class definitions and annotations
|
||||
@type.builtin ; built-in types
|
||||
@type.definition ; identifiers in type definitions (e.g. `typedef <type> <identifier>` in C)
|
||||
|
||||
@attribute ; attribute annotations (e.g. Python decorators, Rust lifetimes)
|
||||
@attribute.builtin ; builtin annotations (e.g. `@property` in Python)
|
||||
@property ; the key in key/value pairs
|
||||
```
|
||||
|
||||
#### Functions
|
||||
|
||||
```query
|
||||
@function ; function definitions
|
||||
@function.builtin ; built-in functions
|
||||
@function.call ; function calls
|
||||
@function.macro ; preprocessor macros
|
||||
|
||||
@function.method ; method definitions
|
||||
@function.method.call ; method calls
|
||||
|
||||
@constructor ; constructor calls and definitions
|
||||
@operator ; symbolic operators (e.g. `+` / `*`)
|
||||
```
|
||||
|
||||
#### Keywords
|
||||
|
||||
```query
|
||||
@keyword ; keywords not fitting into specific categories
|
||||
@keyword.coroutine ; keywords related to coroutines (e.g. `go` in Go, `async/await` in Python)
|
||||
@keyword.function ; keywords that define a function (e.g. `func` in Go, `def` in Python)
|
||||
@keyword.operator ; operators that are English words (e.g. `and` / `or`)
|
||||
@keyword.import ; keywords for including or exporting modules (e.g. `import` / `from` in Python)
|
||||
@keyword.type ; keywords describing namespaces and composite types (e.g. `struct`, `enum`)
|
||||
@keyword.modifier ; keywords modifying other constructs (e.g. `const`, `static`, `public`)
|
||||
@keyword.repeat ; keywords related to loops (e.g. `for` / `while`)
|
||||
@keyword.return ; keywords like `return` and `yield`
|
||||
@keyword.debug ; keywords related to debugging
|
||||
@keyword.exception ; keywords related to exceptions (e.g. `throw` / `catch`)
|
||||
|
||||
@keyword.conditional ; keywords related to conditionals (e.g. `if` / `else`)
|
||||
@keyword.conditional.ternary ; ternary operator (e.g. `?` / `:`)
|
||||
|
||||
@keyword.directive ; various preprocessor directives & shebangs
|
||||
@keyword.directive.define ; preprocessor definition directives
|
||||
```scheme
|
||||
@comment ; line and block comments
|
||||
@error ; syntax/parser errors
|
||||
@none ; completely disable the highlight
|
||||
@preproc ; various preprocessor directives & shebangs
|
||||
@define ; preprocessor definition directives
|
||||
@operator ; symbolic operators (e.g. `+` / `*`)
|
||||
```
|
||||
|
||||
#### Punctuation
|
||||
|
||||
```query
|
||||
```scheme
|
||||
@punctuation.delimiter ; delimiters (e.g. `;` / `.` / `,`)
|
||||
@punctuation.bracket ; brackets (e.g. `()` / `{}` / `[]`)
|
||||
@punctuation.special ; special symbols (e.g. `{}` in string interpolation)
|
||||
```
|
||||
|
||||
#### Comments
|
||||
#### Literals
|
||||
|
||||
```query
|
||||
@comment ; line and block comments
|
||||
@comment.documentation ; comments documenting code
|
||||
```scheme
|
||||
@string ; string literals
|
||||
@string.regex ; regular expressions
|
||||
@string.escape ; escape sequences
|
||||
@string.special ; other special strings (e.g. dates)
|
||||
|
||||
@comment.error ; error-type comments (e.g. `ERROR`, `FIXME`, `DEPRECATED`)
|
||||
@comment.warning ; warning-type comments (e.g. `WARNING`, `FIX`, `HACK`)
|
||||
@comment.todo ; todo-type comments (e.g. `TODO`, `WIP`)
|
||||
@comment.note ; note-type comments (e.g. `NOTE`, `INFO`, `XXX`)
|
||||
@character ; character literals
|
||||
@character.special ; special characters (e.g. wildcards)
|
||||
|
||||
@boolean ; boolean literals
|
||||
@number ; numeric literals
|
||||
@float ; floating-point number literals
|
||||
```
|
||||
|
||||
#### Markup
|
||||
#### Functions
|
||||
|
||||
```scheme
|
||||
@function ; function definitions
|
||||
@function.builtin ; built-in functions
|
||||
@function.call ; function calls
|
||||
@function.macro ; preprocessor macros
|
||||
|
||||
@method ; method definitions
|
||||
@method.call ; method calls
|
||||
|
||||
@constructor ; constructor calls and definitions
|
||||
@parameter ; parameters of a function
|
||||
```
|
||||
|
||||
#### Keywords
|
||||
|
||||
```scheme
|
||||
@keyword ; various keywords
|
||||
@keyword.function ; keywords that define a function (e.g. `func` in Go, `def` in Python)
|
||||
@keyword.operator ; operators that are English words (e.g. `and` / `or`)
|
||||
@keyword.return ; keywords like `return` and `yield`
|
||||
|
||||
@conditional ; keywords related to conditionals (e.g. `if` / `else`)
|
||||
@conditional.ternary ; ternary operator (e.g. `?` / `:`)
|
||||
|
||||
@repeat ; keywords related to loops (e.g. `for` / `while`)
|
||||
@debug ; keywords related to debugging
|
||||
@label ; GOTO and other labels (e.g. `label:` in C)
|
||||
@include ; keywords for including modules (e.g. `import` / `from` in Python)
|
||||
@exception ; keywords related to exceptions (e.g. `throw` / `catch`)
|
||||
```
|
||||
|
||||
#### Types
|
||||
|
||||
```scheme
|
||||
@type ; type or class definitions and annotations
|
||||
@type.builtin ; built-in types
|
||||
@type.definition ; type definitions (e.g. `typedef` in C)
|
||||
@type.qualifier ; type qualifiers (e.g. `const`)
|
||||
|
||||
@storageclass ; modifiers that affect storage in memory or life-time
|
||||
@attribute ; attribute annotations (e.g. Python decorators)
|
||||
@field ; object and struct fields
|
||||
@property ; similar to `@field`
|
||||
```
|
||||
|
||||
#### Identifiers
|
||||
|
||||
```scheme
|
||||
@variable ; various variable names
|
||||
@variable.builtin ; built-in variable names (e.g. `this`)
|
||||
|
||||
@constant ; constant identifiers
|
||||
@constant.builtin ; built-in constant values
|
||||
@constant.macro ; constants defined by the preprocessor
|
||||
|
||||
@namespace ; modules or namespaces
|
||||
@symbol ; symbols or atoms
|
||||
```
|
||||
|
||||
#### Text
|
||||
|
||||
Mainly for markup languages.
|
||||
|
||||
```query
|
||||
@markup.strong ; bold text
|
||||
@markup.italic ; italic text
|
||||
@markup.strikethrough ; struck-through text
|
||||
@markup.underline ; underlined text (only for literal underline markup!)
|
||||
```scheme
|
||||
@text ; non-structured text
|
||||
@text.strong ; bold text
|
||||
@text.emphasis ; text with emphasis
|
||||
@text.underline ; underlined text
|
||||
@text.strike ; strikethrough text
|
||||
@text.title ; text that is part of a title
|
||||
@text.literal ; literal or verbatim text (e.g., inline code)
|
||||
@text.quote ; text quotations
|
||||
@text.uri ; URIs (e.g. hyperlinks)
|
||||
@text.math ; math environments (e.g. `$ ... $` in LaTeX)
|
||||
@text.environment ; text environments of markup languages
|
||||
@text.environment.name ; text indicating the type of an environment
|
||||
@text.reference ; text references, footnotes, citations, etc.
|
||||
|
||||
@markup.heading ; headings, titles (including markers)
|
||||
@markup.heading.1 ; top-level heading
|
||||
@markup.heading.2 ; section heading
|
||||
@markup.heading.3 ; subsection heading
|
||||
@markup.heading.4 ; and so on
|
||||
@markup.heading.5 ; and so forth
|
||||
@markup.heading.6 ; six levels ought to be enough for anybody
|
||||
@text.todo ; todo notes
|
||||
@text.note ; info notes
|
||||
@text.warning ; warning notes
|
||||
@text.danger ; danger/error notes
|
||||
|
||||
@markup.quote ; block quotes
|
||||
@markup.math ; math environments (e.g. `$ ... $` in LaTeX)
|
||||
|
||||
@markup.link ; text references, footnotes, citations, etc.
|
||||
@markup.link.label ; link, reference descriptions
|
||||
@markup.link.url ; URL-style links
|
||||
|
||||
@markup.raw ; literal or verbatim text (e.g. inline code)
|
||||
@markup.raw.block ; literal or verbatim text as a stand-alone block
|
||||
; (use priority 90 for blocks with injections)
|
||||
|
||||
@markup.list ; list markers
|
||||
@markup.list.checked ; checked todo-style list markers
|
||||
@markup.list.unchecked ; unchecked todo-style list markers
|
||||
@text.diff.add ; added text (for diff files)
|
||||
@text.diff.delete ; deleted text (for diff files)
|
||||
```
|
||||
|
||||
```query
|
||||
@diff.plus ; added text (for diff files)
|
||||
@diff.minus ; deleted text (for diff files)
|
||||
@diff.delta ; changed text (for diff files)
|
||||
#### Tags
|
||||
|
||||
Used for XML-like tags.
|
||||
|
||||
```scheme
|
||||
@tag ; XML tag names
|
||||
@tag.attribute ; XML tag attributes
|
||||
@tag.delimiter ; XML tag delimiters
|
||||
```
|
||||
|
||||
```query
|
||||
@tag ; XML-style tag names (and similar)
|
||||
@tag.builtin ; builtin tag names (e.g. HTML5 tags)
|
||||
@tag.attribute ; XML-style tag attributes
|
||||
@tag.delimiter ; XML-style tag delimiters
|
||||
#### Conceal
|
||||
|
||||
|
||||
```scheme
|
||||
@conceal ; for captures that are only used for concealing
|
||||
```
|
||||
|
||||
#### Non-highlighting captures
|
||||
`@conceal` must be followed by `(#set! conceal "")`.
|
||||
|
||||
```query
|
||||
@conceal ; captures that are only meant to be concealed
|
||||
```
|
||||
#### Spell
|
||||
|
||||
>[!TIP]
|
||||
> * See [`:h tree-sitter-highlight-conceal`](https://neovim.io/doc/user/treesitter.html#treesitter-highlight-conceal).
|
||||
> * The capture should be meaningful to allow proper highlighting when `set conceallevel=0`.
|
||||
> * A conceal can be restricted to part of the capture via the [`#offset!` directive](https://neovim.io/doc/user/treesitter.html#treesitter-directive-offset%21).
|
||||
|
||||
```query
|
||||
```scheme
|
||||
@spell ; for defining regions to be spellchecked
|
||||
@nospell ; for defining regions that should NOT be spellchecked
|
||||
```
|
||||
|
||||
>[!TIP]
|
||||
> The main types of nodes that should be spell checked are
|
||||
> - comments
|
||||
> - strings; where it makes sense. Strings that have interpolation or are typically used for non text purposes are not spell checked (e.g. bash).
|
||||
|
||||
#### Predicates
|
||||
|
||||
Captures can be restricted according to node contents using [predicates](https://neovim.io/doc/user/treesitter.html#treesitter-predicates).
|
||||
|
||||
>[!IMPORTANT]
|
||||
> For performance reasons, prefer earlier predicates in this list:
|
||||
>
|
||||
> 1. `#eq?` (literal match)
|
||||
> 2. `#any-of?` (one of several literal matches)
|
||||
> 3. `#lua-match?` (match against a [Lua pattern](https://neovim.io/doc/user/luaref.html#lua-pattern))
|
||||
> 4. `#match?`/`#vim-match?` (match against a [Vim regular expression](https://neovim.io/doc/user/pattern.html#regexp)
|
||||
|
||||
Besides those provided by Neovim, nvim-treesitter also implements
|
||||
|
||||
```query
|
||||
#kind-eq? ; checks whether a capture corresponds to a given set of nodes
|
||||
#any-kind-eq? ; checks whether any of a list of captures corresponds to a given set of nodes
|
||||
```
|
||||
|
||||
#### Directives
|
||||
|
||||
Nodes contain metadata that can be modified via [directives](https://neovim.io/doc/user/treesitter.html#treesitter-directives).
|
||||
|
||||
#### Priority
|
||||
|
||||
Captures can be assigned a priority to control precedence of highlights via the
|
||||
`#set! priority <number>` directive (see [`:h treesitter-highlight-priority`](https://neovim.io/doc/user/treesitter.html#treesitter-highlight-priority)). This is useful for controlling conflicts with injected languages or when inheriting queries from other languages.
|
||||
|
||||
>[!NOTE]
|
||||
> The default priority for treesitter highlights is `100`; queries should only
|
||||
set priorities between `90` and `120`, to avoid conflict with other sources of highlighting (such as diagnostics or LSP semantic tokens).
|
||||
|
||||
>[!TIP]
|
||||
> Precedence is also influenced by pattern order in a query file. If possible, try to achieve the correct result by reordering patterns before resorting to explicit priorities.
|
||||
|
||||
### Injections
|
||||
|
||||
Language injections are controlled by `injections.scm` queries, which specify nodes that should be parsed as a different language. This feature is implemented in Neovim and documented at
|
||||
[`:h treesitter-language-injections](https://neovim.io/doc/user/treesitter.html#treesitter-language-injections).
|
||||
|
||||
The valid captures are:
|
||||
|
||||
```query
|
||||
@injection.language ; dynamic detection of the injection language (i.e. the text of the captured node describes the language)
|
||||
@injection.content ; region for the dynamically detected language
|
||||
@injection.filename ; indicates that the captured node’s text may contain a filename; the corresponding filetype is then looked-up up via vim.filetype.match() and treated as the name of a language that should be used to re-parse the `@injection.content`
|
||||
```
|
||||
|
||||
>[!TIP]
|
||||
> When writing injection queries, try to ensure that each captured node is only matched by a single pattern.
|
||||
|
||||
### Folds
|
||||
|
||||
You can define folds for a given language by adding a `folds.scm` query. This is implemented in Neovim. The only valid capture is `@fold`:
|
||||
|
||||
```query
|
||||
(function_definition) @fold ; fold this node
|
||||
```
|
||||
|
||||
Folds should be given to nodes with defined start and end delimiters/patterns, or to consecutive nodes which are part of the same conceptual "grouping", such as consecutive line comments or import statements. The following items are valid fold candidates:
|
||||
|
||||
- Function/method definitions
|
||||
- Class/interface/trait definitions
|
||||
- Switch/match statements, and individual match arms
|
||||
- Execution blocks (such as those found in conditional statements or loops)
|
||||
- Parameter/argument lists
|
||||
- Array/object/string expressions
|
||||
- Consecutive import statements, consecutive line comments
|
||||
|
||||
The following items would *not* be valid fold candidates:
|
||||
|
||||
- Multiline assignment statements
|
||||
- Multiline property access expressions
|
||||
|
||||
As a rule of thumb, these highlight captures usually reside in or around objects which should be folded:
|
||||
|
||||
- `@function`, `@function.method`
|
||||
- `@keyword.import`, `@keyword.conditional`, `@keyword.repeat`
|
||||
- `@comment`, `@comment.documentation`
|
||||
- `@string`, `@string.documentation`
|
||||
- `@markup.heading.x`, `@markup.list`
|
||||
|
||||
### Indents
|
||||
|
||||
>[!WARNING]
|
||||
> Treesitter-based indentation is still experimental and likely to have breaking changes in the future.
|
||||
|
||||
Indentation for a language is controlled by `indents.scm` queries. The following captures can be used to set the indentation for nodes, either relative or absolute
|
||||
|
||||
* `@indent.begin` specifies that the next line should be indented. Multiple
|
||||
indents on the same line get collapsed, e.g.,
|
||||
```query
|
||||
(
|
||||
(if_statement)
|
||||
(ERROR "else") @indent.begin
|
||||
)
|
||||
```
|
||||
You can also `#set! indent.immediate` to permit the next line to indent even when the block intended to be indented has no content yet. (This can improve interactive typing.)
|
||||
For example for Python,
|
||||
```query
|
||||
((if_statement) @indent.begin
|
||||
(#set! indent.immediate 1))
|
||||
```
|
||||
will allow
|
||||
```python
|
||||
if True:<CR>
|
||||
# Auto indent to here
|
||||
```
|
||||
|
||||
* `@indent.end` is used to specify that the indented region ends and any text subsequent to the capture should be dedented.
|
||||
|
||||
* `@indent.branch` is used to specify that a dedented region starts at the line _including_ the captured nodes.
|
||||
|
||||
* `@indent.dedent` specifies dedenting starting on the _next_ line.
|
||||
|
||||
* `@indent.auto` behaves like Vim's [`autoindent`](https://neovim.io/doc/user/options.html#'autoindent') buffer option (copy whatever the indentation of previous line is when opening a new line after it).
|
||||
|
||||
* `@indent.ignore` specifies that no indent should be added to this node.
|
||||
|
||||
* `@indent.zero` sets the indentation of this node to 0 (i.e., removes _all_ indentation).
|
||||
|
||||
* `@indent.align` can be used to specify blocks that should have the same indentation.
|
||||
This allows
|
||||
```
|
||||
foo(a,
|
||||
b,
|
||||
c)
|
||||
```
|
||||
as well as
|
||||
```
|
||||
foo(
|
||||
a,
|
||||
b,
|
||||
c)
|
||||
```
|
||||
and
|
||||
```
|
||||
foo(
|
||||
a,
|
||||
b,
|
||||
c
|
||||
)
|
||||
```
|
||||
To specify the delimiters to align at, `#set! indent.open_delimiter` and
|
||||
`indent.close_delimiter`, e.g.,
|
||||
```query
|
||||
((argument_list) @indent.align
|
||||
(#set! indent.open_delimiter "(")
|
||||
(#set! indent.close_delimiter ")"))
|
||||
```
|
||||
For some languages, the last line of an `indent.align` block must not be
|
||||
the same indent as the natural next line.
|
||||
For example in Python,
|
||||
|
||||
```python
|
||||
if (a > b and
|
||||
c < d):
|
||||
pass
|
||||
```
|
||||
is not correct, whereas
|
||||
```python
|
||||
if (a > b and
|
||||
c < d):
|
||||
pass
|
||||
```
|
||||
would be correctly indented. This behavior may be selected by setting
|
||||
`indent.avoid_last_matching_next`. For example,
|
||||
```query
|
||||
(if_statement
|
||||
condition: (parenthesized_expression) @indent.align
|
||||
(#set! indent.open_delimiter "(")
|
||||
(#set! indent.close_delimiter ")")
|
||||
(#set! indent.avoid_last_matching_next 1)
|
||||
)
|
||||
```
|
||||
specifies that the last line of an `@indent.align` capture
|
||||
should be additionally indented to avoid clashing with the indent of the first
|
||||
line of the block inside an `if`.
|
||||
|
||||
### Locals
|
||||
|
||||
Locals are used to keep track of definitions and references in local or global
|
||||
scopes, see [upstream
|
||||
documentation](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables).
|
||||
Note that nvim-treesitter uses more specific subcaptures for definitions and
|
||||
**does not use locals** (for highlighting or any other purpose). These queries
|
||||
are only provided for limited backwards compatibility.
|
||||
```scheme
|
||||
@definition ; various definitions
|
||||
@definition.constant ; constants
|
||||
@definition.function ; functions
|
||||
@definition.method ; methods
|
||||
@definition.var ; variables
|
||||
@definition.parameter ; parameters
|
||||
@definition.macro ; preprocessor macros
|
||||
@definition.type ; types or classes
|
||||
@definition.field ; fields or properties
|
||||
@definition.enum ; enumerations
|
||||
@definition.namespace ; modules or namespaces
|
||||
@definition.import ; imported names
|
||||
@definition.associated ; the associated type of a variable
|
||||
|
||||
```query
|
||||
@local.definition ; various definitions
|
||||
@local.definition.constant ; constants
|
||||
@local.definition.function ; functions
|
||||
@local.definition.method ; methods
|
||||
@local.definition.var ; variables
|
||||
@local.definition.parameter ; parameters
|
||||
@local.definition.macro ; preprocessor macros
|
||||
@local.definition.type ; types or classes
|
||||
@local.definition.field ; fields or properties
|
||||
@local.definition.enum ; enumerations
|
||||
@local.definition.namespace ; modules or namespaces
|
||||
@local.definition.import ; imported names
|
||||
@local.definition.associated ; the associated type of a variable
|
||||
|
||||
@local.scope ; scope block
|
||||
@local.reference ; identifier reference
|
||||
@scope ; scope block
|
||||
@reference ; identifier reference
|
||||
```
|
||||
|
||||
#### Definition scope
|
||||
#### Definition Scope
|
||||
|
||||
You can set the scope of a definition by setting the `scope` property on the definition.
|
||||
|
||||
|
|
@ -520,8 +273,8 @@ doSomething(); // Should point to the declaration as the definition
|
|||
|
||||
```query
|
||||
(function_declaration
|
||||
((identifier) @local.definition.var)
|
||||
(#set! definition.var.scope "parent"))
|
||||
((identifier) @definition.var)
|
||||
(#set! "definition.var.scope" "parent"))
|
||||
```
|
||||
|
||||
Possible scope values are:
|
||||
|
|
@ -530,7 +283,44 @@ Possible scope values are:
|
|||
- `global`: The definition is valid in the root scope
|
||||
- `local`: The definition is valid in the containing scope. This is the default behavior
|
||||
|
||||
### Folds
|
||||
|
||||
[supported languages]: https://github.com/nvim-treesitter/nvim-treesitter/SUPPORTED_LANGUAGES.md
|
||||
[tree-sitter queries]: https://tree-sitter.github.io/tree-sitter/using-parsers/queries/index.html
|
||||
[ts_query_ls]: https://github.com/ribru17/ts_query_ls
|
||||
You can define folds for a given language by adding a `folds.scm` query :
|
||||
|
||||
```scheme
|
||||
@fold ; fold this node
|
||||
```
|
||||
|
||||
If the `folds.scm` query is not present, this will fall back 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.
|
||||
|
||||
```scheme
|
||||
@{lang} ; 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 ; combine all matches of a pattern as one single block of content
|
||||
```
|
||||
|
||||
### Indents
|
||||
|
||||
```scheme
|
||||
@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)
|
||||
```
|
||||
|
||||
[Matrix channel]: https://matrix.to/#/#nvim-treesitter:matrix.org
|
||||
|
|
|
|||
139
Makefile
139
Makefile
|
|
@ -1,139 +0,0 @@
|
|||
NVIM_VERSION ?= nightly
|
||||
|
||||
DEPDIR ?= .test-deps
|
||||
CURL ?= curl -sL --create-dirs
|
||||
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
NVIM_ARCH ?= macos-arm64
|
||||
LUALS_ARCH ?= darwin-arm64
|
||||
STYLUA_ARCH ?= macos-aarch64
|
||||
RUST_ARCH ?= aarch64-apple-darwin
|
||||
else
|
||||
NVIM_ARCH ?= linux-x86_64
|
||||
LUALS_ARCH ?= linux-x64
|
||||
STYLUA_ARCH ?= linux-x86_64
|
||||
RUST_ARCH ?= x86_64-unknown-linux-gnu
|
||||
endif
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
# download test dependencies
|
||||
|
||||
NVIM := $(DEPDIR)/nvim-$(NVIM_ARCH)
|
||||
NVIM_TARBALL := $(NVIM).tar.gz
|
||||
NVIM_URL := https://github.com/neovim/neovim/releases/download/$(NVIM_VERSION)/$(notdir $(NVIM_TARBALL))
|
||||
NVIM_BIN := $(NVIM)/nvim-$(NVIM_ARCH)/bin/nvim
|
||||
NVIM_RUNTIME=$(NVIM)/nvim-$(NVIM_ARCH)/share/nvim/runtime
|
||||
|
||||
.PHONY: nvim
|
||||
nvim: $(NVIM)
|
||||
|
||||
$(NVIM):
|
||||
$(CURL) $(NVIM_URL) -o $(NVIM_TARBALL)
|
||||
mkdir $@
|
||||
tar -xf $(NVIM_TARBALL) -C $@
|
||||
rm -rf $(NVIM_TARBALL)
|
||||
|
||||
EMMYLUALS := $(DEPDIR)/emmylua_check-$(LUALS_ARCH)
|
||||
EMMYLUALS_TARBALL := $(EMMYLUALS).tar.gz
|
||||
EMMYLUALS_URL := https://github.com/emmyluals/emmylua-analyzer-rust/releases/latest/download/$(notdir $(EMMYLUALS_TARBALL))
|
||||
|
||||
.PHONY: emmyluals
|
||||
emmyluals: $(EMMYLUALS)
|
||||
|
||||
$(EMMYLUALS):
|
||||
$(CURL) $(EMMYLUALS_URL) -o $(EMMYLUALS_TARBALL)
|
||||
mkdir $@
|
||||
tar -xf $(EMMYLUALS_TARBALL) -C $@
|
||||
rm -rf $(EMMYLUALS_TARBALL)
|
||||
|
||||
STYLUA := $(DEPDIR)/stylua-$(STYLUA_ARCH)
|
||||
STYLUA_TARBALL := $(STYLUA).zip
|
||||
STYLUA_URL := https://github.com/JohnnyMorganz/StyLua/releases/latest/download/$(notdir $(STYLUA_TARBALL))
|
||||
|
||||
.PHONY: stylua
|
||||
stylua: $(STYLUA)
|
||||
|
||||
$(STYLUA):
|
||||
$(CURL) $(STYLUA_URL) -o $(STYLUA_TARBALL)
|
||||
unzip $(STYLUA_TARBALL) -d $(STYLUA)
|
||||
rm -rf $(STYLUA_TARBALL)
|
||||
|
||||
TSQUERYLS := $(DEPDIR)/ts_query_ls-$(RUST_ARCH)
|
||||
TSQUERYLS_TARBALL := $(TSQUERYLS).tar.gz
|
||||
TSQUERYLS_URL := https://github.com/ribru17/ts_query_ls/releases/latest/download/$(notdir $(TSQUERYLS_TARBALL))
|
||||
|
||||
.PHONY: tsqueryls
|
||||
tsqueryls: $(TSQUERYLS)
|
||||
|
||||
$(TSQUERYLS):
|
||||
$(CURL) $(TSQUERYLS_URL) -o $(TSQUERYLS_TARBALL)
|
||||
mkdir $@
|
||||
tar -xf $(TSQUERYLS_TARBALL) -C $@
|
||||
rm -rf $(TSQUERYLS_TARBALL)
|
||||
|
||||
HLASSERT := $(DEPDIR)/highlight-assertions-$(RUST_ARCH)
|
||||
HLASSERT_TARBALL := $(HLASSERT).tar.gz
|
||||
HLASSERT_URL := https://github.com/nvim-treesitter/highlight-assertions/releases/latest/download/$(notdir $(HLASSERT_TARBALL))
|
||||
|
||||
.PHONY: hlassert
|
||||
hlassert: $(HLASSERT)
|
||||
|
||||
$(HLASSERT):
|
||||
$(CURL) $(HLASSERT_URL) -o $(HLASSERT_TARBALL)
|
||||
mkdir $@
|
||||
tar -xf $(HLASSERT_TARBALL) -C $@
|
||||
rm -rf $(HLASSERT_TARBALL)
|
||||
|
||||
PLENTEST := $(DEPDIR)/plentest.nvim
|
||||
|
||||
.PHONY: plentest
|
||||
plentest: $(PLENTEST)
|
||||
|
||||
$(PLENTEST):
|
||||
git clone --filter=blob:none https://github.com/nvim-treesitter/plentest.nvim $(PLENTEST)
|
||||
|
||||
# actual test targets
|
||||
|
||||
.PHONY: lua
|
||||
lua: formatlua checklua
|
||||
|
||||
.PHONY: formatlua
|
||||
formatlua: $(STYLUA)
|
||||
$(STYLUA)/stylua .
|
||||
|
||||
.PHONY: checklua
|
||||
checklua: $(EMMYLUALS) $(NVIM)
|
||||
VIMRUNTIME=$(NVIM_RUNTIME) $(EMMYLUALS)/emmylua_check --warnings-as-errors .
|
||||
|
||||
.PHONY: query
|
||||
query: formatquery lintquery checkquery
|
||||
|
||||
.PHONY: lintquery
|
||||
lintquery: $(TSQUERYLS)
|
||||
$(TSQUERYLS)/ts_query_ls lint runtime/queries
|
||||
|
||||
.PHONY: formatquery
|
||||
formatquery: $(TSQUERYLS)
|
||||
$(TSQUERYLS)/ts_query_ls format runtime/queries
|
||||
|
||||
.PHONY: checkquery
|
||||
checkquery: $(TSQUERYLS)
|
||||
$(TSQUERYLS)/ts_query_ls check runtime/queries
|
||||
|
||||
.PHONY: docs
|
||||
docs: $(NVIM)
|
||||
$(NVIM_BIN) -l scripts/update-readme.lua
|
||||
|
||||
.PHONY: tests
|
||||
tests: $(NVIM) $(HLASSERT) $(PLENTEST)
|
||||
HLASSERT=$(HLASSERT)/highlight-assertions PLENTEST=$(PLENTEST) \
|
||||
$(NVIM_BIN) --headless --clean -u scripts/minimal_init.lua \
|
||||
-c "lua require('plentest').test_directory('tests/$(TESTS)', { minimal_init = './scripts/minimal_init.lua' })"
|
||||
|
||||
.PHONY: all
|
||||
all: lua query docs tests
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(DEPDIR)
|
||||
726
README.md
726
README.md
|
|
@ -1,187 +1,673 @@
|
|||
<h1 align="center">
|
||||
<img src="https://github.com/nvim-treesitter/nvim-treesitter/assets/2361214/0513b223-c902-4f12-92ee-8ac4d8d6f41f" alt="nvim-treesitter">
|
||||
</h1>
|
||||
<div align="center">
|
||||
<h1>nvim-treesitter</h1>
|
||||
<p>
|
||||
<a href="https://matrix.to/#/#nvim-treesitter:matrix.org">
|
||||
<img alt="Matrix Chat" src="https://img.shields.io/matrix/nvim-treesitter:matrix.org" />
|
||||
</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>
|
||||
|
||||
The `nvim-treesitter` plugin provides
|
||||
1. functions for installing, updating, and removing [**tree-sitter parsers**](SUPPORTED_LANGUAGES.md);
|
||||
2. a collection of **queries** for enabling tree-sitter features built into Neovim for these languages;
|
||||
3. a staging ground for [treesitter-based features](#Supported-features) considered for upstreaming to Neovim.
|
||||
<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>
|
||||
|
||||
For details on these and how to help improving them, see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
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:
|
||||
|
||||
>[!CAUTION]
|
||||
> This is a full, incompatible, rewrite: Treat this as a different plugin you need to set up from scratch following the instructions below. If you can't or don't want to update, specify the [`master` branch](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/README.md) (which is locked but will remain available for backward compatibility with Nvim 0.11).
|
||||

|
||||
|
||||
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 0.12.0 or later (nightly)
|
||||
- `tar` and `curl` in your path
|
||||
- [`tree-sitter-cli`](https://github.com/tree-sitter/tree-sitter/blob/master/crates/cli/README.md) (0.26.1 or later, installed via your package manager, **not npm**)
|
||||
- a C compiler in your path (see <https://docs.rs/cc/latest/cc/#compile-time-requirements>)
|
||||
|
||||
>[!IMPORTANT]
|
||||
> The current **support policy** for Neovim is
|
||||
> * the _latest_ [stable release](https://github.com/neovim/neovim/releases/tag/stable),
|
||||
> * the _latest_ [nightly prerelease](https://github.com/neovim/neovim/releases/tag/nightly).
|
||||
> Other versions may work but are neither tested nor considered for fixes.
|
||||
- **Neovim 0.8.0 or later** built with **tree-sitter 0.20.3+** (latest [nightly](https://github.com/neovim/neovim#install-from-source) recommended)
|
||||
- `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`).
|
||||
|
||||
This plugin is only guaranteed to work with specific versions of language parsers** (as specified in the `parser.lua` table). **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., using the following spec with [lazy.nvim](https://github.com/folke/lazy.nvim):
|
||||
**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
|
||||
{
|
||||
'nvim-treesitter/nvim-treesitter',
|
||||
lazy = false,
|
||||
build = ':TSUpdate'
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
-- A list of parser names, or "all" (the four listed parsers should always be installed)
|
||||
ensure_installed = { "c", "lua", "vim", "help" },
|
||||
|
||||
-- Install parsers synchronously (only applied to `ensure_installed`)
|
||||
sync_install = false,
|
||||
|
||||
-- Automatically install missing parsers when entering buffer
|
||||
-- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
|
||||
auto_install = true,
|
||||
|
||||
-- List of parsers to ignore installing (for "all")
|
||||
ignore_install = { "javascript" },
|
||||
|
||||
---- If you need to change the installation directory of the parsers (see -> Advanced Setup)
|
||||
-- parser_install_dir = "/some/path/to/store/parsers", -- Remember to run vim.opt.runtimepath:append("/some/path/to/store/parsers")!
|
||||
|
||||
highlight = {
|
||||
-- `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" },
|
||||
-- Or use a function for more flexibility, e.g. to disable slow treesitter highlight for large files
|
||||
disable = function(lang, buf)
|
||||
local max_filesize = 100 * 1024 -- 100 KB
|
||||
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
|
||||
if ok and stats and stats.size > max_filesize then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
|
||||
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
|
||||
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
|
||||
-- Using this option may slow down your editor, and you may see some duplicate highlights.
|
||||
-- Instead of true it can also be a list of languages
|
||||
additional_vim_regex_highlighting = false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
>[!IMPORTANT]
|
||||
> This plugin does not support lazy-loading.
|
||||
Each module can also be enabled or disabled interactively through the following commands:
|
||||
|
||||
## Setup
|
||||
|
||||
`nvim-treesitter` can be configured by calling `setup`. **You do not need to call `setup` for `nvim-treesitter` to work using default values.**
|
||||
|
||||
```lua
|
||||
require('nvim-treesitter').setup {
|
||||
-- Directory to install parsers and queries to (prepended to `runtimepath` to have priority)
|
||||
install_dir = vim.fn.stdpath('data') .. '/site'
|
||||
}
|
||||
```
|
||||
|
||||
Parsers and queries can then be installed with
|
||||
|
||||
```lua
|
||||
require('nvim-treesitter').install { 'rust', 'javascript', 'zig' }
|
||||
```
|
||||
|
||||
(This is a no-op if the parsers are already installed.) Note that this function runs asynchronously; for synchronous installation in a script context ("bootstrapping"), you need to `wait()` for it to finish:
|
||||
|
||||
```lua
|
||||
require('nvim-treesitter').install({ 'rust', 'javascript', 'zig' }):wait(300000) -- wait max. 5 minutes
|
||||
```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.
|
||||
|
||||
A list of the currently supported languages can be found [on this page](SUPPORTED_LANGUAGES.md). If you wish to add a new language or improve the queries for an existing one, please see our [contributing guide](CONTRIBUTING.md).
|
||||
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.
|
||||
|
||||
# Supported features
|
||||
Experimental parsers are parsers that have a maintainer but are not stable enough for
|
||||
daily use yet.
|
||||
|
||||
`nvim-treesitter` provides queries for the following features. **These are not automatically enabled.**
|
||||
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.
|
||||
|
||||
## Highlighting
|
||||
<!--This section of the README is automatically updated by a CI job-->
|
||||
<!--parserinfo-->
|
||||
- [x] [ada](https://github.com/briot/tree-sitter-ada) (maintained by @briot)
|
||||
- [x] [agda](https://github.com/AusCyberman/tree-sitter-agda) (maintained by @Decodetalkers)
|
||||
- [x] [arduino](https://github.com/ObserverOfTime/tree-sitter-arduino) (maintained by @ObserverOfTime)
|
||||
- [x] [astro](https://github.com/virchau13/tree-sitter-astro) (maintained by @virchau13)
|
||||
- [ ] [awk](https://github.com/Beaglefoot/tree-sitter-awk)
|
||||
- [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] [blueprint](https://gitlab.com/gabmus/tree-sitter-blueprint.git) (experimental, maintained by @gabmus)
|
||||
- [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] [capnp](https://github.com/amaanq/tree-sitter-capnp) (maintained by @amaanq)
|
||||
- [x] [chatito](https://github.com/ObserverOfTime/tree-sitter-chatito) (maintained by @ObserverOfTime)
|
||||
- [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 @akinsho)
|
||||
- [x] [devicetree](https://github.com/joelspadin/tree-sitter-devicetree) (maintained by @jedrzejboczar)
|
||||
- [x] [diff](https://github.com/the-mikedavis/tree-sitter-diff) (maintained by @gbprod)
|
||||
- [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] [ebnf](https://github.com/RubixDev/ebnf.git) (experimental, maintained by @RubixDev)
|
||||
- [x] [eex](https://github.com/connorlay/tree-sitter-eex) (maintained by @connorlay)
|
||||
- [x] [elixir](https://github.com/elixir-lang/tree-sitter-elixir) (maintained by @connorlay)
|
||||
- [x] [elm](https://github.com/elm-tooling/tree-sitter-elm) (maintained by @zweimach)
|
||||
- [x] [elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) (maintained by @glapa-grossklag, @amaanq)
|
||||
- [x] [elvish](https://github.com/ckafi/tree-sitter-elvish) (maintained by @ckafi)
|
||||
- [ ] [embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template)
|
||||
- [x] [erlang](https://github.com/WhatsApp/tree-sitter-erlang) (maintained by @filmor)
|
||||
- [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] [fsh](https://github.com/mgramigna/tree-sitter-fsh) (maintained by @mgramigna)
|
||||
- [x] [func](https://github.com/amaanq/tree-sitter-func) (maintained by @amaanq)
|
||||
- [x] [fusion](https://gitlab.com/jirgn/tree-sitter-fusion.git) (maintained by @jirgn)
|
||||
- [x] [Godot (gdscript)](https://github.com/PrestonKnopp/tree-sitter-gdscript) (maintained by @Shatur)
|
||||
- [x] [git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) (maintained by @gbprod)
|
||||
- [x] [gitattributes](https://github.com/ObserverOfTime/tree-sitter-gitattributes) (maintained by @ObserverOfTime)
|
||||
- [x] [gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) (maintained by @gbprod)
|
||||
- [x] [gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) (maintained by @theHamsta)
|
||||
- [x] [gleam](https://github.com/J3RN/tree-sitter-gleam) (maintained by @connorlay)
|
||||
- [x] [Glimmer and Ember](https://github.com/alexlafroscia/tree-sitter-glimmer) (maintained by @NullVoxPopuli)
|
||||
- [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] [gosum](https://github.com/amaanq/tree-sitter-go-sum) (maintained by @amaanq)
|
||||
- [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/neovim/tree-sitter-vimdoc) (maintained by @vigoux)
|
||||
- [x] [hjson](https://github.com/winston0410/tree-sitter-hjson) (maintained by @winston0410)
|
||||
- [x] [hlsl](https://github.com/theHamsta/tree-sitter-hlsl) (maintained by @theHamsta)
|
||||
- [x] [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] [htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) (experimental, maintained by @ObserverOfTime)
|
||||
- [x] [http](https://github.com/rest-nvim/tree-sitter-http) (maintained by @NTBBloodbath)
|
||||
- [x] [ini](https://github.com/justinmk/tree-sitter-ini) (experimental, maintained by @theHamsta)
|
||||
- [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] [jq](https://github.com/flurie/tree-sitter-jq) (maintained by @ObserverOfTime)
|
||||
- [x] [jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) (maintained by @steelsojka)
|
||||
- [x] [json](https://github.com/tree-sitter/tree-sitter-json) (maintained by @steelsojka)
|
||||
- [x] [json5](https://github.com/Joakker/tree-sitter-json5) (maintained by @Joakker)
|
||||
- [x] [JSON with comments](https://gitlab.com/WhyNotHugo/tree-sitter-jsonc.git) (maintained by @WhyNotHugo)
|
||||
- [x] [jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) (maintained by @nawordar)
|
||||
- [x] [julia](https://github.com/tree-sitter/tree-sitter-julia) (maintained by @theHamsta)
|
||||
- [x] [kdl](https://github.com/amaanq/tree-sitter-kdl) (maintained by @amaanq)
|
||||
- [x] [kotlin](https://github.com/fwcd/tree-sitter-kotlin) (maintained by @SalBakraa)
|
||||
- [x] [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] [m68k](https://github.com/grahambates/tree-sitter-m68k) (maintained by @grahambates)
|
||||
- [x] [make](https://github.com/alemuller/tree-sitter-make) (maintained by @lewis6991)
|
||||
- [x] [markdown](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml)
|
||||
- [x] [markdown_inline](https://github.com/MDeiml/tree-sitter-markdown) (experimental, maintained by @MDeiml)
|
||||
- [x] [menhir](https://github.com/Kerl13/tree-sitter-menhir) (maintained by @Kerl13)
|
||||
- [ ] [mermaid](https://github.com/monaqa/tree-sitter-mermaid) (experimental)
|
||||
- [x] [meson](https://github.com/Decodetalkers/tree-sitter-meson) (maintained by @Decodetalkers)
|
||||
- [ ] [nickel](https://github.com/nickel-lang/tree-sitter-nickel)
|
||||
- [x] [ninja](https://github.com/alemuller/tree-sitter-ninja) (maintained by @alemuller)
|
||||
- [x] [nix](https://github.com/cstrahan/tree-sitter-nix) (maintained by @leo60228)
|
||||
- [x] [norg](https://github.com/nvim-neorg/tree-sitter-norg) (maintained by @JoeyGrajciar, @vhyrro)
|
||||
- [x] [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)
|
||||
- [ ] [org](https://github.com/milisims/tree-sitter-org)
|
||||
- [x] [pascal](https://github.com/Isopod/tree-sitter-pascal.git) (maintained by @Isopod)
|
||||
- [x] [perl](https://github.com/ganezdragon/tree-sitter-perl) (maintained by @lcrownover)
|
||||
- [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] [Path of Exile item filter](https://github.com/ObserverOfTime/tree-sitter-poe-filter) (experimental, maintained by @ObserverOfTime)
|
||||
- [x] [prisma](https://github.com/victorhqc/tree-sitter-prisma) (maintained by @elianiva)
|
||||
- [x] [proto](https://github.com/mitchellh/tree-sitter-proto) (maintained by @fsouza)
|
||||
- [x] [pug](https://github.com/zealot128/tree-sitter-pug) (experimental, 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] [qmljs](https://github.com/yuja/tree-sitter-qmljs) (maintained by @Decodetalkers)
|
||||
- [x] [Tree-sitter query language](https://github.com/nvim-treesitter/tree-sitter-query) (maintained by @steelsojka)
|
||||
- [x] [r](https://github.com/r-lib/tree-sitter-r) (maintained by @echasnovski)
|
||||
- [x] [racket](https://github.com/6cdh/tree-sitter-racket) (maintained by @6cdh)
|
||||
- [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] [rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) (maintained by @bamonroe)
|
||||
- [x] [ron](https://github.com/amaanq/tree-sitter-ron) (maintained by @amaanq)
|
||||
- [x] [rst](https://github.com/stsewd/tree-sitter-rst) (maintained by @stsewd)
|
||||
- [x] [ruby](https://github.com/tree-sitter/tree-sitter-ruby) (maintained by @TravonteD)
|
||||
- [x] [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] [smali](https://github.com/amaanq/tree-sitter-smali) (experimental, maintained by @amaanq)
|
||||
- [x] [smithy](https://github.com/indoorvivants/tree-sitter-smithy) (maintained by @amaanq, @keynmol)
|
||||
- [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] [sql](https://github.com/derekstride/tree-sitter-sql) (maintained by @derekstride)
|
||||
- [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)
|
||||
- [x] [swift](https://github.com/alex-pinkus/tree-sitter-swift) (maintained by @alex-pinkus)
|
||||
- [x] [sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) (maintained by @RaafatTurki)
|
||||
- [x] [t32](https://codeberg.org/xasc/tree-sitter-t32) (maintained by @xasc)
|
||||
- [x] [teal](https://github.com/euclidianAce/tree-sitter-teal) (maintained by @euclidianAce)
|
||||
- [x] [terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) (maintained by @MichaHoffmann)
|
||||
- [x] [thrift](https://github.com/duskmoon314/tree-sitter-thrift) (maintained by @amaanq, @duskmoon314)
|
||||
- [x] [tiger](https://github.com/ambroisie/tree-sitter-tiger) (maintained by @ambroisie)
|
||||
- [x] [tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) (maintained by @ahelwer, @susliko)
|
||||
- [x] [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] [twig](https://github.com/gbprod/tree-sitter-twig) (maintained by @gbprod)
|
||||
- [x] [typescript](https://github.com/tree-sitter/tree-sitter-typescript) (maintained by @steelsojka)
|
||||
- [x] [v](https://github.com/vlang/vls) (maintained by @kkharji)
|
||||
- [x] [vala](https://github.com/vala-lang/tree-sitter-vala) (maintained by @Prince781)
|
||||
- [x] [verilog](https://github.com/tree-sitter/tree-sitter-verilog) (maintained by @zegervdv)
|
||||
- [x] [vhs](https://github.com/charmbracelet/tree-sitter-vhs) (maintained by @caarlos0)
|
||||
- [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] [wgsl_bevy](https://github.com/theHamsta/tree-sitter-wgsl-bevy) (maintained by @theHamsta)
|
||||
- [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-->
|
||||
|
||||
Treesitter highlighting is provided by Neovim, see `:h treesitter-highlight`. To enable it for a filetype, put `vim.treesitter.start()` in a `ftplugin/<filetype>.lua` in your config directory, or place the following in your `init.lua`:
|
||||
For related information on the supported languages, including related plugins, see [this wiki page](https://github.com/nvim-treesitter/nvim-treesitter/wiki/Supported-Languages-Information).
|
||||
|
||||
# Available modules
|
||||
|
||||
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
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = { '<filetype>' },
|
||||
callback = function() vim.treesitter.start() end,
|
||||
})
|
||||
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,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Folds
|
||||
|
||||
Treesitter-based folding is provided by Neovim. To enable it, put the following in your `ftplugin` or `FileType` autocommand:
|
||||
To customize the syntax highlighting of a capture, simply define or link a highlight group of the same name:
|
||||
|
||||
```lua
|
||||
vim.wo[0][0].foldexpr = 'v:lua.vim.treesitter.foldexpr()'
|
||||
vim.wo[0][0].foldmethod = 'expr'
|
||||
-- Highlight the @foo.bar capture group with the "Identifier" highlight group
|
||||
vim.api.nvim_set_hl(0, "@foo.bar", { link = "Identifier" })
|
||||
```
|
||||
|
||||
## Indentation
|
||||
|
||||
Treesitter-based indentation is provided by this plugin but considered **experimental**. To enable it, put the following in your `ftplugin` or `FileType` autocommand:
|
||||
For a language-specific highlight, append the name of the language:
|
||||
|
||||
```lua
|
||||
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||
-- Highlight @foo.bar as "Identifier" only in Lua files
|
||||
vim.api.nvim_set_hl(0, "@foo.bar.lua", { link = "Identifier" })
|
||||
```
|
||||
|
||||
(Note the specific quotes used.)
|
||||
See `:h treesitter-highlight-groups` for details.
|
||||
|
||||
## Injections
|
||||
#### Incremental selection
|
||||
|
||||
Injections are used for multi-language documents, see `:h treesitter-language-injections`. No setup is needed.
|
||||
Incremental selection based on the named nodes from the grammar.
|
||||
|
||||
## Locals
|
||||
```lua
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
incremental_selection = {
|
||||
enable = true,
|
||||
keymaps = {
|
||||
init_selection = "gnn", -- set to `false` to disable one of the mappings
|
||||
node_incremental = "grn",
|
||||
scope_incremental = "grc",
|
||||
node_decremental = "grm",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
These queries can be used to look up definitions and references to identifiers in a given scope. They are not used in this plugin and are provided for (limited) backward compatibility.
|
||||
#### 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()
|
||||
set nofoldenable " Disable folding at startup.
|
||||
```
|
||||
|
||||
This will respect your `foldminlines` and `foldnestmax` settings.
|
||||
|
||||
# Advanced setup
|
||||
|
||||
## Adding custom languages
|
||||
## Changing the parser install directory
|
||||
|
||||
If you want to install the parsers to a custom directory you can specify this
|
||||
directory with `parser_install_dir` option in that is passed to `setup`.
|
||||
`nvim-treesitter` will then install the parser files into this directory.
|
||||
|
||||
This directory must be writeable and must be explicitly added to the
|
||||
`runtimepath`. For example:
|
||||
|
||||
``` lua
|
||||
vim.opt.runtimepath:append("/some/path/to/store/parsers")
|
||||
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
parser_install_dir = "/some/path/to/store/parsers",
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
If this option is not included in the setup options, or is explicitly set to
|
||||
`nil` then the default install directories will be used. If this value is set
|
||||
the default directories will be ignored.
|
||||
|
||||
Bear in mind that any parser installed into a parser folder on the runtime path
|
||||
will still be considered installed. (For example if
|
||||
"~/.local/share/nvim/site/parser/c.so" exists then the "c" parser will be
|
||||
considered installed, even though it is not in `parser_install_dir`)
|
||||
|
||||
The default paths are:
|
||||
1. first the package folder. Where `nvim-treesitter` is installed.
|
||||
2. second the site directory. This is the "site" subdirectory of `stdpath("data")`.
|
||||
|
||||
## 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. Add the following snippet in a `User TSUpdate` autocommand:
|
||||
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
|
||||
vim.api.nvim_create_autocmd('User', { pattern = 'TSUpdate',
|
||||
callback = function()
|
||||
require('nvim-treesitter.parsers').zimbu = {
|
||||
install_info = {
|
||||
url = 'https://github.com/zimbulang/tree-sitter-zimbu',
|
||||
revision = <sha>, -- commit hash for revision to check out; HEAD if missing
|
||||
-- optional entries:
|
||||
branch = 'develop', -- only needed if different from default branch
|
||||
location = 'parser', -- only needed if the parser is in subdirectory of a "monorepo"
|
||||
generate = true, -- only needed if repo does not contain pre-generated `src/parser.c`
|
||||
generate_from_json = false, -- only needed if repo does not contain `src/grammar.json` either
|
||||
queries = 'queries/neovim', -- also install queries from given directory
|
||||
},
|
||||
}
|
||||
end})
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, if you have a local checkout, you can instead use
|
||||
If you wish to set a specific parser for a filetype, you should extend the `filetype_to_parsername` table:
|
||||
|
||||
```lua
|
||||
install_info = {
|
||||
path = '~/parsers/tree-sitter-zimbu',
|
||||
-- optional entries
|
||||
location = 'parser',
|
||||
generate = true,
|
||||
generate_from_json = false,
|
||||
queries = 'queries/neovim', -- symlink queries from given directory
|
||||
},
|
||||
```
|
||||
This will always use the state of the directory as-is (i.e., `branch` and `revision` will be ignored).
|
||||
|
||||
2. If the parser name differs from the filetype(s) used by Neovim, you need to register the parser via
|
||||
|
||||
```lua
|
||||
vim.treesitter.language.register('zimbu', { 'zu' })
|
||||
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.
|
||||
```
|
||||
|
||||
If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`](<https://neovim.io/doc/user/lua.html#vim.filetype.add()>) to add a custom detection rule.
|
||||
4. Start `nvim` and `:TSInstall zimbu`.
|
||||
|
||||
3. 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`.
|
||||
|
||||
>[!IMPORTANT]
|
||||
> If the parser requires an external scanner, this must be written in C.
|
||||
Note that neither `:TSInstall` nor `:TSInstallFromGrammar` copy query files from the grammar repository.
|
||||
If you want your installed grammar to be useful, you must manually [add query files](#adding-queries) to your local nvim-treesitter installation.
|
||||
Note also that module functionality is only triggered if your language's filetype is correctly identified.
|
||||
If Neovim does not detect your language's filetype by default, you can use [Neovim's `vim.filetype.add()`](https://neovim.io/doc/user/lua.html#vim.filetype.add()) to add a custom detection rule.
|
||||
|
||||
### Modifying parsers
|
||||
|
||||
You can use the same approach for overriding parser information. E.g., if you always want to generate the `lua` parser from grammar, add
|
||||
|
||||
```lua
|
||||
vim.api.nvim_create_autocmd('User', { pattern = 'TSUpdate',
|
||||
callback = function()
|
||||
require('nvim-treesitter.parsers').lua.install_info.generate = true
|
||||
end})
|
||||
```
|
||||
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 can be placed anywhere in your `runtimepath` under `queries/<language>`, with earlier directories taking precedence unless the queries are marked with `; extends`; see [`:h treesitter-query-modelines`](https://neovim.io/doc/user/treesitter.html#treesitter-query-modeline).
|
||||
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 {
|
||||
--
|
||||
--
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,365 +0,0 @@
|
|||
# Supported languages
|
||||
|
||||
The following is a list of languages for which a parser can be installed through `:TSInstall`.
|
||||
|
||||
Legend:
|
||||
- **Tier:** _stable_ (updates follow semver releases), _unstable_ (updates follow HEAD), _unmaintained_ (no automatic updates), or _unsupported_ (known to be broken, cannot be installed)
|
||||
- **Queries** available for **H**ighlights, **F**olds, **I**ndents, In**J**ections, **L**ocals
|
||||
- **Maintainer** of queries in nvim-treesitter (may be different from parser maintainer!)
|
||||
|
||||
<!--This section of the README is automatically updated by a CI job-->
|
||||
<!--parserinfo-->
|
||||
Language | Tier | Queries | Maintainer
|
||||
-------- |:----:|:-------:| ----------
|
||||
[ada](https://github.com/briot/tree-sitter-ada) | unstable | `HF JL` | @briot
|
||||
[agda](https://github.com/tree-sitter/tree-sitter-agda) | unstable | `HF J ` | @Decodetalkers
|
||||
[angular](https://github.com/dlvandenberg/tree-sitter-angular) | unstable | `HFIJL` | @dlvandenberg
|
||||
[apex](https://github.com/aheber/tree-sitter-sfapex) | unstable | `HF JL` | @aheber, @xixiafinland
|
||||
[arduino](https://github.com/tree-sitter-grammars/tree-sitter-arduino) | unstable | `HFIJL` | @ObserverOfTime
|
||||
[asm](https://github.com/RubixDev/tree-sitter-asm) | unstable | `H J ` | @RubixDev
|
||||
[astro](https://github.com/virchau13/tree-sitter-astro) | unstable | `HFIJL` | @virchau13
|
||||
[authzed](https://github.com/mleonidas/tree-sitter-authzed) | unstable | `H J ` | @mattpolzin
|
||||
[awk](https://github.com/Beaglefoot/tree-sitter-awk) | unstable | `H J ` |
|
||||
[bash](https://github.com/tree-sitter/tree-sitter-bash) | unstable | `HFIJL` | @TravonteD
|
||||
[bass](https://github.com/vito/tree-sitter-bass) | unstable | `HFIJL` | @amaanq
|
||||
[beancount](https://github.com/polarmutex/tree-sitter-beancount) | unstable | `HF J ` | @polarmutex
|
||||
[bibtex](https://github.com/latex-lsp/tree-sitter-bibtex) | unstable | `HFIJ ` | @theHamsta, @clason
|
||||
[bicep](https://github.com/tree-sitter-grammars/tree-sitter-bicep) | unstable | `HFIJL` | @amaanq
|
||||
[bitbake](https://github.com/tree-sitter-grammars/tree-sitter-bitbake) | unstable | `HFIJL` | @amaanq
|
||||
[blade](https://github.com/EmranMR/tree-sitter-blade) | unstable | `HFIJ ` | @calebdw
|
||||
[bp](https://github.com/ambroisie/tree-sitter-bp)[^bp] | unstable | `HFIJL` | @ambroisie
|
||||
[bpftrace](https://github.com/sgruszka/tree-sitter-bpftrace) | unstable | `H J ` | @sgruszka
|
||||
[brightscript](https://github.com/ajdelcimmuto/tree-sitter-brightscript) | unstable | `HFIJ ` | @ajdelcimmuto
|
||||
[c](https://github.com/tree-sitter/tree-sitter-c) | unstable | `HFIJL` | @amaanq
|
||||
[c3](https://github.com/c3lang/tree-sitter-c3) | unstable | `HFIJ ` | @cbuttner
|
||||
[c_sharp](https://github.com/tree-sitter/tree-sitter-c-sharp) | unstable | `HF JL` | @amaanq
|
||||
[caddy](https://github.com/opa-oz/tree-sitter-caddy) | unmaintained | `HFIJ ` |
|
||||
[cairo](https://github.com/tree-sitter-grammars/tree-sitter-cairo) | unstable | `HFIJL` | @amaanq
|
||||
[capnp](https://github.com/tree-sitter-grammars/tree-sitter-capnp) | unstable | `HFIJL` | @amaanq
|
||||
[chatito](https://github.com/tree-sitter-grammars/tree-sitter-chatito) | unstable | `HFIJL` | @ObserverOfTime
|
||||
[circom](https://github.com/Decurity/tree-sitter-circom) | unstable | `HF JL` | @alexandr-martirosyan
|
||||
[clojure](https://github.com/sogaiu/tree-sitter-clojure) | unstable | `HF JL` | @NoahTheDuke
|
||||
[cmake](https://github.com/uyha/tree-sitter-cmake) | unstable | `HFIJ ` | @uyha
|
||||
[comment](https://github.com/stsewd/tree-sitter-comment) | unstable | `H ` | @stsewd
|
||||
[commonlisp](https://github.com/tree-sitter-grammars/tree-sitter-commonlisp) | unstable | `HF JL` | @theHamsta
|
||||
[cooklang](https://github.com/addcninblue/tree-sitter-cooklang) | unstable | `H J ` | @addcninblue
|
||||
[corn](https://github.com/jakestanger/tree-sitter-corn) | unstable | `HFIJL` | @jakestanger
|
||||
[cpon](https://github.com/tree-sitter-grammars/tree-sitter-cpon) | unstable | `HFIJL` | @amaanq
|
||||
[cpp](https://github.com/tree-sitter/tree-sitter-cpp) | unstable | `HFIJL` | @theHamsta
|
||||
[css](https://github.com/tree-sitter/tree-sitter-css) | unstable | `HFIJ ` | @TravonteD
|
||||
[csv](https://github.com/tree-sitter-grammars/tree-sitter-csv) | unstable | `H ` | @amaanq
|
||||
[cuda](https://github.com/tree-sitter-grammars/tree-sitter-cuda) | unstable | `HFIJL` | @theHamsta
|
||||
[cue](https://github.com/eonpatapon/tree-sitter-cue) | unstable | `HFIJL` | @amaanq
|
||||
[cylc](https://github.com/elliotfontaine/tree-sitter-cylc) | unstable | `HFIJ ` | @elliotfontaine
|
||||
[d](https://github.com/gdamore/tree-sitter-d) | unstable | `HFIJL` | @amaanq
|
||||
[dart](https://github.com/UserNobody14/tree-sitter-dart) | unstable | `HFIJL` | @akinsho
|
||||
[desktop](https://github.com/ValdezFOmar/tree-sitter-desktop) | stable | `HF J ` | @ValdezFOmar
|
||||
[devicetree](https://github.com/joelspadin/tree-sitter-devicetree) | unstable | `HFIJL` | @jedrzejboczar
|
||||
[dhall](https://github.com/jbellerb/tree-sitter-dhall) | unstable | `HF J ` | @amaanq
|
||||
[diff](https://github.com/tree-sitter-grammars/tree-sitter-diff) | unstable | `HF J ` | @gbprod
|
||||
[disassembly](https://github.com/ColinKennedy/tree-sitter-disassembly) | unstable | `H J ` | @ColinKennedy
|
||||
[djot](https://github.com/treeman/tree-sitter-djot) | unstable | `HFIJL` | @NoahTheDuke
|
||||
[dockerfile](https://github.com/camdencheek/tree-sitter-dockerfile) | unstable | `H J ` | @camdencheek
|
||||
[dot](https://github.com/rydesun/tree-sitter-dot) | unstable | `HFIJ ` | @rydesun
|
||||
[doxygen](https://github.com/tree-sitter-grammars/tree-sitter-doxygen) | unstable | `H IJ ` | @amaanq
|
||||
[dtd](https://github.com/tree-sitter-grammars/tree-sitter-xml) | unstable | `HF JL` | @ObserverOfTime
|
||||
[earthfile](https://github.com/glehmann/tree-sitter-earthfile) | unstable | `H J ` | @glehmann
|
||||
[ebnf](https://github.com/RubixDev/ebnf) | unstable | `H J ` | @RubixDev
|
||||
ecma (queries only)[^ecma] | unstable | `HFIJL` | @steelsojka
|
||||
[editorconfig](https://github.com/ValdezFOmar/tree-sitter-editorconfig) | stable | `HF J ` | @ValdezFOmar
|
||||
[eds](https://github.com/uyha/tree-sitter-eds) | unstable | `HF ` | @uyha
|
||||
[eex](https://github.com/connorlay/tree-sitter-eex) | unstable | `H J ` | @connorlay
|
||||
[elixir](https://github.com/elixir-lang/tree-sitter-elixir) | unstable | `HFIJL` | @connorlay
|
||||
[elm](https://github.com/elm-tooling/tree-sitter-elm) | unstable | `HF J ` | @zweimach
|
||||
[elsa](https://github.com/glapa-grossklag/tree-sitter-elsa) | unstable | `HFIJL` | @glapa-grossklag, @amaanq
|
||||
[elvish](https://github.com/elves/tree-sitter-elvish) | unstable | `H J ` | @elves
|
||||
[embedded_template](https://github.com/tree-sitter/tree-sitter-embedded-template) | unstable | `H J ` |
|
||||
[enforce](https://github.com/simonvic/tree-sitter-enforce) | unstable | `HFIJL` | @simonvic
|
||||
[erlang](https://github.com/WhatsApp/tree-sitter-erlang) | unstable | `HF J ` | @filmor
|
||||
[facility](https://github.com/FacilityApi/tree-sitter-facility) | unstable | `HFIJ ` | @bryankenote
|
||||
[faust](https://github.com/khiner/tree-sitter-faust) | unstable | `H J ` | @khiner
|
||||
[fennel](https://github.com/alexmozaidze/tree-sitter-fennel) | unstable | `HF JL` | @alexmozaidze
|
||||
[fidl](https://github.com/google/tree-sitter-fidl) | unstable | `HF J ` | @chaopeng
|
||||
[firrtl](https://github.com/tree-sitter-grammars/tree-sitter-firrtl) | unstable | `HFIJL` | @amaanq
|
||||
[fish](https://github.com/ram02z/tree-sitter-fish) | unstable | `HFIJL` | @ram02z
|
||||
[foam](https://github.com/FoamScience/tree-sitter-foam) | unstable | `HFIJL` | @FoamScience
|
||||
[forth](https://github.com/AlexanderBrevig/tree-sitter-forth) | unstable | `HFIJL` | @amaanq
|
||||
[fortran](https://github.com/stadelmanma/tree-sitter-fortran) | unstable | `HFIJ ` | @amaanq
|
||||
[fsh](https://github.com/mgramigna/tree-sitter-fsh) | unstable | `H J ` | @mgramigna
|
||||
[fsharp](https://github.com/ionide/tree-sitter-fsharp) | unstable | `H J ` | @nsidorenco
|
||||
[func](https://github.com/tree-sitter-grammars/tree-sitter-func) | unstable | `H J ` | @amaanq
|
||||
[gap](https://github.com/gap-system/tree-sitter-gap)[^gap] | unstable | `HF JL` | @reiniscirpons
|
||||
[gaptst](https://github.com/gap-system/tree-sitter-gaptst)[^gaptst] | unstable | `HF J ` | @reiniscirpons
|
||||
[gdscript](https://github.com/PrestonKnopp/tree-sitter-gdscript)[^gdscript] | unmaintained | `HFIJL` |
|
||||
[gdshader](https://github.com/airblast-dev/tree-sitter-gdshader) | unstable | `H J ` | @airblast-dev
|
||||
[git_config](https://github.com/the-mikedavis/tree-sitter-git-config) | unstable | `HF J ` | @amaanq
|
||||
[git_rebase](https://github.com/the-mikedavis/tree-sitter-git-rebase) | unstable | `H J ` | @gbprod
|
||||
[gitattributes](https://github.com/tree-sitter-grammars/tree-sitter-gitattributes) | unstable | `H JL` | @ObserverOfTime
|
||||
[gitcommit](https://github.com/gbprod/tree-sitter-gitcommit) | unstable | `H J ` | @gbprod
|
||||
[gitignore](https://github.com/shunsambongi/tree-sitter-gitignore) | unstable | `H J ` | @theHamsta
|
||||
[gleam](https://github.com/gleam-lang/tree-sitter-gleam) | unstable | `HFIJL` | @amaanq
|
||||
[glimmer](https://github.com/ember-tooling/tree-sitter-glimmer)[^glimmer] | unstable | `HFIJL` | @NullVoxPopuli
|
||||
[glimmer_javascript](https://github.com/NullVoxPopuli/tree-sitter-glimmer-javascript) | unstable | `HFIJL` | @NullVoxPopuli
|
||||
[glimmer_typescript](https://github.com/NullVoxPopuli/tree-sitter-glimmer-typescript) | unstable | `HFIJ ` | @NullVoxPopuli
|
||||
[glsl](https://github.com/tree-sitter-grammars/tree-sitter-glsl) | unstable | `HFIJL` | @theHamsta
|
||||
[gn](https://github.com/tree-sitter-grammars/tree-sitter-gn) | unstable | `HFIJL` | @amaanq
|
||||
[gnuplot](https://github.com/dpezto/tree-sitter-gnuplot) | unstable | `H J ` | @dpezto
|
||||
[go](https://github.com/tree-sitter/tree-sitter-go) | unstable | `HFIJL` | @theHamsta, @WinWisely268
|
||||
[goctl](https://github.com/chaozwn/tree-sitter-goctl) | unstable | `HFIJ ` | @chaozwn
|
||||
[godot_resource](https://github.com/PrestonKnopp/tree-sitter-godot-resource)[^godot_resource] | unstable | `HF JL` | @pierpo
|
||||
[gomod](https://github.com/camdencheek/tree-sitter-go-mod) | unstable | `H J ` | @camdencheek
|
||||
[gosum](https://github.com/tree-sitter-grammars/tree-sitter-go-sum) | unstable | `H ` | @amaanq
|
||||
[gotmpl](https://github.com/ngalaiko/tree-sitter-go-template) | unstable | `HF JL` | @qvalentin
|
||||
[gowork](https://github.com/omertuc/tree-sitter-go-work) | unstable | `H J ` | @omertuc
|
||||
[gpg](https://github.com/tree-sitter-grammars/tree-sitter-gpg-config) | unstable | `H J ` | @ObserverOfTime
|
||||
[graphql](https://github.com/bkegley/tree-sitter-graphql) | unstable | `H IJ ` | @bkegley
|
||||
[gren](https://github.com/MaeBrooks/tree-sitter-gren) | unstable | `H J ` | @MaeBrooks
|
||||
[groovy](https://github.com/murtaza64/tree-sitter-groovy) | unstable | `HFIJL` | @murtaza64
|
||||
[groq](https://github.com/ajrussellaudio/tree-sitter-groq) | unstable | `HFIJ ` | @ajrussellaudio
|
||||
[gstlaunch](https://github.com/tree-sitter-grammars/tree-sitter-gstlaunch) | unstable | `H ` | @theHamsta
|
||||
[hack](https://github.com/slackhq/tree-sitter-hack) | unstable | `H J ` |
|
||||
[hare](https://github.com/tree-sitter-grammars/tree-sitter-hare) | unstable | `HFIJL` | @amaanq
|
||||
[haskell](https://github.com/tree-sitter-grammars/tree-sitter-haskell) | unstable | `HF JL` | @mrcjkb
|
||||
[haskell_persistent](https://github.com/MercuryTechnologies/tree-sitter-haskell-persistent) | unstable | `HF ` | @lykahb
|
||||
[hcl](https://github.com/tree-sitter-grammars/tree-sitter-hcl) | unstable | `HFIJ ` | @MichaHoffmann
|
||||
[heex](https://github.com/connorlay/tree-sitter-heex) | unstable | `HFIJL` | @connorlay
|
||||
[helm](https://github.com/ngalaiko/tree-sitter-go-template) | unstable | `HF JL` | @qvalentin
|
||||
[hjson](https://github.com/winston0410/tree-sitter-hjson) | unstable | `HFIJL` | @winston0410
|
||||
[hlsl](https://github.com/tree-sitter-grammars/tree-sitter-hlsl) | unstable | `HFIJL` | @theHamsta
|
||||
[hlsplaylist](https://github.com/Freed-Wu/tree-sitter-hlsplaylist) | unstable | `H J ` | @Freed-Wu
|
||||
[hocon](https://github.com/antosha417/tree-sitter-hocon) | unstable | `HF J ` | @antosha417
|
||||
[hoon](https://github.com/urbit-pilled/tree-sitter-hoon) | unstable | `HF JL` | @urbit-pilled
|
||||
[html](https://github.com/tree-sitter/tree-sitter-html) | unstable | `HFIJL` | @TravonteD
|
||||
html_tags (queries only)[^html_tags] | unstable | `H IJ ` | @TravonteD
|
||||
[htmldjango](https://github.com/interdependence/tree-sitter-htmldjango) | unstable | `HFIJ ` | @ObserverOfTime
|
||||
[http](https://github.com/rest-nvim/tree-sitter-http) | unstable | `HF J ` | @amaanq, @NTBBloodbath
|
||||
[hurl](https://github.com/pfeiferj/tree-sitter-hurl) | unstable | `HFIJ ` | @pfeiferj
|
||||
[hyprlang](https://github.com/tree-sitter-grammars/tree-sitter-hyprlang) | unstable | `HFIJ ` | @luckasRanarison
|
||||
[idl](https://github.com/cathaysia/tree-sitter-idl) | unstable | `H IJ ` | @cathaysia
|
||||
[idris](https://github.com/kayhide/tree-sitter-idris) | unstable | `HF JL` |
|
||||
[ini](https://github.com/justinmk/tree-sitter-ini) | unstable | `HF J ` | @theHamsta
|
||||
[inko](https://github.com/inko-lang/tree-sitter-inko) | stable | `HFIJL` | @yorickpeterse
|
||||
[ispc](https://github.com/tree-sitter-grammars/tree-sitter-ispc) | unstable | `HFIJL` | @fab4100
|
||||
[janet_simple](https://github.com/sogaiu/tree-sitter-janet-simple) | unstable | `HF JL` | @sogaiu
|
||||
[java](https://github.com/tree-sitter/tree-sitter-java) | unstable | `HFIJL` | @p00f
|
||||
[javadoc](https://github.com/rmuir/tree-sitter-javadoc) | unstable | `H IJ ` | @rmuir
|
||||
[javascript](https://github.com/tree-sitter/tree-sitter-javascript) | unstable | `HFIJL` | @steelsojka
|
||||
[jinja](https://github.com/cathaysia/tree-sitter-jinja)[^jinja] | unstable | `H J ` | @cathaysia
|
||||
[jinja_inline](https://github.com/cathaysia/tree-sitter-jinja)[^jinja_inline] | unstable | `H J ` | @cathaysia
|
||||
[jjdescription](https://github.com/ribru17/tree-sitter-jjdescription) | stable | `H J ` | @ribru17
|
||||
[jq](https://github.com/flurie/tree-sitter-jq) | unstable | `H JL` | @ObserverOfTime
|
||||
[jsdoc](https://github.com/tree-sitter/tree-sitter-jsdoc) | unstable | `H ` | @steelsojka
|
||||
[json](https://github.com/tree-sitter/tree-sitter-json) | unstable | `HFIJL` | @steelsojka
|
||||
[json5](https://github.com/Joakker/tree-sitter-json5) | unstable | `H J ` | @Joakker
|
||||
[jsonnet](https://github.com/sourcegraph/tree-sitter-jsonnet) | unstable | `HF JL` | @nawordar
|
||||
jsx (queries only)[^jsx] | unstable | `HFIJ ` | @steelsojka
|
||||
[julia](https://github.com/tree-sitter-grammars/tree-sitter-julia) | unstable | `HFIJL` | @clason
|
||||
[just](https://github.com/IndianBoy42/tree-sitter-just) | unstable | `HFIJL` | @Hubro
|
||||
[kcl](https://github.com/kcl-lang/tree-sitter-kcl) | unstable | `HF J ` | @bertbaron
|
||||
[kconfig](https://github.com/tree-sitter-grammars/tree-sitter-kconfig) | unstable | `HFIJL` | @amaanq
|
||||
[kdl](https://github.com/tree-sitter-grammars/tree-sitter-kdl) | unstable | `HFIJL` | @amaanq
|
||||
[kitty](https://github.com/OXY2DEV/tree-sitter-kitty) | unstable | `H J ` | @OXY2DEV
|
||||
[kos](https://github.com/kos-lang/tree-sitter-kos) | unstable | `HF JL` | @cdragan
|
||||
[kotlin](https://github.com/fwcd/tree-sitter-kotlin) | unstable | `HF JL` |
|
||||
[koto](https://github.com/koto-lang/tree-sitter-koto) | unstable | `HF JL` | @irh
|
||||
[kusto](https://github.com/Willem-J-an/tree-sitter-kusto) | unstable | `H J ` | @Willem-J-an
|
||||
[lalrpop](https://github.com/traxys/tree-sitter-lalrpop) | unstable | `HF JL` | @traxys
|
||||
[latex](https://github.com/latex-lsp/tree-sitter-latex) | unstable | `HF J ` | @theHamsta, @clason
|
||||
[ledger](https://github.com/cbarrete/tree-sitter-ledger) | unstable | `HFIJ ` | @cbarrete
|
||||
[leo](https://github.com/r001/tree-sitter-leo) | unstable | `H IJ ` | @r001
|
||||
[linkerscript](https://github.com/tree-sitter-grammars/tree-sitter-linkerscript) | unstable | `HFIJL` | @amaanq
|
||||
[liquid](https://github.com/hankthetank27/tree-sitter-liquid) | unstable | `H J ` | @hankthetank27
|
||||
[liquidsoap](https://github.com/savonet/tree-sitter-liquidsoap) | unstable | `HFIJL` | @toots
|
||||
[llvm](https://github.com/benwilliamgraham/tree-sitter-llvm) | unstable | `H J ` | @benwilliamgraham
|
||||
[lua](https://github.com/tree-sitter-grammars/tree-sitter-lua) | unstable | `HFIJL` | @muniftanjim
|
||||
[luadoc](https://github.com/tree-sitter-grammars/tree-sitter-luadoc) | unstable | `H ` | @amaanq
|
||||
[luap](https://github.com/tree-sitter-grammars/tree-sitter-luap)[^luap] | unstable | `H ` | @amaanq
|
||||
[luau](https://github.com/tree-sitter-grammars/tree-sitter-luau) | unstable | `HFIJL` | @amaanq
|
||||
[m68k](https://github.com/grahambates/tree-sitter-m68k) | unstable | `HF JL` | @grahambates
|
||||
[make](https://github.com/tree-sitter-grammars/tree-sitter-make) | unstable | `HF J ` | @lewis6991
|
||||
[markdown](https://github.com/tree-sitter-grammars/tree-sitter-markdown)[^markdown] | unstable | `HFIJ ` | @MDeiml
|
||||
[markdown_inline](https://github.com/tree-sitter-grammars/tree-sitter-markdown)[^markdown_inline] | unstable | `H J ` | @MDeiml
|
||||
[matlab](https://github.com/acristoffers/tree-sitter-matlab) | unstable | `HFIJL` | @acristoffers
|
||||
[menhir](https://github.com/Kerl13/tree-sitter-menhir) | unstable | `H J ` | @Kerl13
|
||||
[mermaid](https://github.com/monaqa/tree-sitter-mermaid) | unstable | `HFIJ ` |
|
||||
[meson](https://github.com/tree-sitter-grammars/tree-sitter-meson) | unstable | `HFIJ ` | @Decodetalkers
|
||||
[mlir](https://github.com/artagnon/tree-sitter-mlir) | unstable | `H JL` | @artagnon
|
||||
[muttrc](https://github.com/neomutt/tree-sitter-muttrc) | unstable | `H J ` | @Freed-Wu
|
||||
[nasm](https://github.com/naclsn/tree-sitter-nasm) | unstable | `H J ` | @ObserverOfTime
|
||||
[nginx](https://github.com/opa-oz/tree-sitter-nginx) | unstable | `HF J ` | @opa-oz
|
||||
[nickel](https://github.com/nickel-lang/tree-sitter-nickel) | unstable | `H IJ ` |
|
||||
[nim](https://github.com/alaviss/tree-sitter-nim) | unstable | `HF JL` | @aMOPel
|
||||
[nim_format_string](https://github.com/aMOPel/tree-sitter-nim-format-string) | unstable | `H J ` | @aMOPel
|
||||
[ninja](https://github.com/alemuller/tree-sitter-ninja) | unstable | `HFIJ ` | @alemuller
|
||||
[nix](https://github.com/nix-community/tree-sitter-nix) | unstable | `HFIJL` | @leo60228, @mrcjkb, @zimbatm
|
||||
[nqc](https://github.com/tree-sitter-grammars/tree-sitter-nqc) | unstable | `HFIJL` | @amaanq
|
||||
[nu](https://github.com/nushell/tree-sitter-nu) | unstable | `HFIJ ` | @abhisheksingh0x558
|
||||
[objc](https://github.com/tree-sitter-grammars/tree-sitter-objc) | unstable | `HFIJL` | @amaanq
|
||||
[objdump](https://github.com/ColinKennedy/tree-sitter-objdump) | unstable | `H J ` | @ColinKennedy
|
||||
[ocaml](https://github.com/tree-sitter/tree-sitter-ocaml) | unstable | `HFIJL` | @undu
|
||||
[ocaml_interface](https://github.com/tree-sitter/tree-sitter-ocaml) | unstable | `HFIJL` | @undu
|
||||
[ocamllex](https://github.com/atom-ocaml/tree-sitter-ocamllex) | unstable | `H J ` | @undu
|
||||
[odin](https://github.com/tree-sitter-grammars/tree-sitter-odin) | unstable | `HFIJL` | @amaanq
|
||||
[pascal](https://github.com/Isopod/tree-sitter-pascal) | unstable | `HFIJL` | @Isopod
|
||||
[passwd](https://github.com/ath3/tree-sitter-passwd) | unstable | `H ` | @amaanq
|
||||
[pem](https://github.com/tree-sitter-grammars/tree-sitter-pem) | unstable | `HF J ` | @ObserverOfTime
|
||||
[perl](https://github.com/tree-sitter-perl/tree-sitter-perl) | unstable | `HF J ` | @RabbiVeesh, @LeoNerd
|
||||
[php](https://github.com/tree-sitter/tree-sitter-php)[^php] | unstable | `HFIJL` | @tk-shirasaka, @calebdw
|
||||
[php_only](https://github.com/tree-sitter/tree-sitter-php)[^php_only] | unstable | `HFIJL` | @tk-shirasaka, @calebdw
|
||||
[phpdoc](https://github.com/claytonrcarter/tree-sitter-phpdoc) | unstable | `H ` | @mikehaertl
|
||||
[pioasm](https://github.com/leo60228/tree-sitter-pioasm) | unstable | `H J ` | @leo60228
|
||||
[pkl](https://github.com/apple/tree-sitter-pkl) | unstable | `HF J ` | @ribru17
|
||||
[po](https://github.com/tree-sitter-grammars/tree-sitter-po) | unstable | `HF J ` | @amaanq
|
||||
[pod](https://github.com/tree-sitter-perl/tree-sitter-pod) | unstable | `H ` | @RabbiVeesh, @LeoNerd
|
||||
[poe_filter](https://github.com/tree-sitter-grammars/tree-sitter-poe-filter)[^poe_filter] | unstable | `HFIJ ` | @ObserverOfTime
|
||||
[pony](https://github.com/tree-sitter-grammars/tree-sitter-pony) | unstable | `HFIJL` | @amaanq, @mfelsche
|
||||
[powershell](https://github.com/airbus-cert/tree-sitter-powershell) | unstable | `HFIJL` | @L2jLiga
|
||||
[printf](https://github.com/tree-sitter-grammars/tree-sitter-printf) | unstable | `H ` | @ObserverOfTime
|
||||
[prisma](https://github.com/victorhqc/tree-sitter-prisma) | unstable | `HF J ` | @elianiva
|
||||
[problog](https://github.com/foxyseta/tree-sitter-prolog) | unstable | `HFIJ ` | @foxyseta
|
||||
[prolog](https://github.com/foxyseta/tree-sitter-prolog) | unstable | `HFIJ ` | @foxyseta
|
||||
[promql](https://github.com/MichaHoffmann/tree-sitter-promql) | unstable | `H J ` | @MichaHoffmann
|
||||
[properties](https://github.com/tree-sitter-grammars/tree-sitter-properties)[^properties] | unstable | `H JL` | @ObserverOfTime
|
||||
[proto](https://github.com/coder3101/tree-sitter-proto) | unstable | `HFIJ ` | @stefanvanburen
|
||||
[prql](https://github.com/PRQL/tree-sitter-prql) | unstable | `H J ` | @matthias-Q
|
||||
[psv](https://github.com/tree-sitter-grammars/tree-sitter-csv) | unstable | `H ` | @amaanq
|
||||
[pug](https://github.com/zealot128/tree-sitter-pug) | unstable | `H J ` | @zealot128
|
||||
[puppet](https://github.com/tree-sitter-grammars/tree-sitter-puppet) | unstable | `HFIJL` | @amaanq
|
||||
[purescript](https://github.com/postsolar/tree-sitter-purescript) | unstable | `H JL` | @postsolar
|
||||
[pymanifest](https://github.com/tree-sitter-grammars/tree-sitter-pymanifest) | unstable | `H J ` | @ObserverOfTime
|
||||
[python](https://github.com/tree-sitter/tree-sitter-python) | stable | `HFIJL` | @stsewd, @theHamsta
|
||||
[ql](https://github.com/tree-sitter/tree-sitter-ql) | unstable | `HFIJL` | @pwntester
|
||||
[qmldir](https://github.com/tree-sitter-grammars/tree-sitter-qmldir) | unstable | `H J ` | @amaanq
|
||||
[qmljs](https://github.com/yuja/tree-sitter-qmljs) | unstable | `HF J ` | @Decodetalkers
|
||||
[query](https://github.com/tree-sitter-grammars/tree-sitter-query)[^query] | unstable | `HFIJL` | @steelsojka
|
||||
[r](https://github.com/r-lib/tree-sitter-r) | unstable | `H IJL` | @ribru17
|
||||
[racket](https://github.com/6cdh/tree-sitter-racket) | unstable | `HF J ` |
|
||||
[ralph](https://github.com/alephium/tree-sitter-ralph) | unstable | `H J ` | @tdroxler
|
||||
[rasi](https://github.com/Fymyte/tree-sitter-rasi) | unstable | `HFIJL` | @Fymyte
|
||||
[razor](https://github.com/tris203/tree-sitter-razor) | unstable | `HF J ` | @tris203
|
||||
[rbs](https://github.com/joker1007/tree-sitter-rbs) | unstable | `HFIJ ` | @joker1007
|
||||
[re2c](https://github.com/tree-sitter-grammars/tree-sitter-re2c) | unstable | `HFIJL` | @amaanq
|
||||
[readline](https://github.com/tree-sitter-grammars/tree-sitter-readline) | unstable | `HFIJ ` | @ribru17
|
||||
[regex](https://github.com/tree-sitter/tree-sitter-regex) | unstable | `H ` | @theHamsta
|
||||
[rego](https://github.com/FallenAngel97/tree-sitter-rego) | unstable | `H J ` | @FallenAngel97
|
||||
[requirements](https://github.com/tree-sitter-grammars/tree-sitter-requirements) | unstable | `H J ` | @ObserverOfTime
|
||||
[rescript](https://github.com/rescript-lang/tree-sitter-rescript) | unstable | `HFIJL` | @ribru17
|
||||
[rifleconf](https://github.com/purarue/tree-sitter-rifleconf) | unstable | `H J ` | @purarue
|
||||
[rnoweb](https://github.com/bamonroe/tree-sitter-rnoweb) | unstable | `HF J ` | @bamonroe
|
||||
[robot](https://github.com/Hubro/tree-sitter-robot) | unmaintained | `HFIJ ` |
|
||||
[robots_txt](https://github.com/opa-oz/tree-sitter-robots-txt) | unstable | `H J ` | @opa-oz
|
||||
[roc](https://github.com/faldor20/tree-sitter-roc) | unmaintained | `H IJL` |
|
||||
[ron](https://github.com/tree-sitter-grammars/tree-sitter-ron) | unstable | `HFIJL` | @amaanq
|
||||
[rst](https://github.com/stsewd/tree-sitter-rst) | unstable | `H JL` | @stsewd
|
||||
[ruby](https://github.com/tree-sitter/tree-sitter-ruby) | unstable | `HFIJL` | @TravonteD
|
||||
[runescript](https://github.com/2004Scape/tree-sitter-runescript) | unstable | `H J ` | @2004Scape
|
||||
[rust](https://github.com/tree-sitter/tree-sitter-rust) | unstable | `HFIJL` | @amaanq
|
||||
[scala](https://github.com/tree-sitter/tree-sitter-scala) | unstable | `HF JL` | @stevanmilic
|
||||
[scfg](https://github.com/rockorager/tree-sitter-scfg) | unstable | `H J ` | @WhyNotHugo
|
||||
[scheme](https://github.com/6cdh/tree-sitter-scheme) | unstable | `HF J ` |
|
||||
[scss](https://github.com/serenadeai/tree-sitter-scss) | unstable | `HFIJ ` | @elianiva
|
||||
[sflog](https://github.com/aheber/tree-sitter-sfapex)[^sflog] | unstable | `H ` | @aheber, @xixiaofinland
|
||||
[slang](https://github.com/tree-sitter-grammars/tree-sitter-slang)[^slang] | unstable | `HFIJL` | @theHamsta
|
||||
[slim](https://github.com/theoo/tree-sitter-slim) | unstable | `HFIJL` | @theoo
|
||||
[slint](https://github.com/slint-ui/tree-sitter-slint) | unstable | `HFIJL` | @hunger
|
||||
[smali](https://github.com/tree-sitter-grammars/tree-sitter-smali) | unstable | `HFIJL` | @amaanq
|
||||
[smithy](https://github.com/indoorvivants/tree-sitter-smithy) | unstable | `H J ` | @amaanq, @keynmol
|
||||
[snakemake](https://github.com/osthomas/tree-sitter-snakemake) | unstable | `HFIJL` | @osthomas
|
||||
[snl](https://github.com/minijackson/tree-sitter-snl)[^snl] | unstable | `HFIJL` | @minijackson
|
||||
[solidity](https://github.com/JoranHonig/tree-sitter-solidity) | unstable | `HF J ` | @amaanq
|
||||
[soql](https://github.com/aheber/tree-sitter-sfapex) | unstable | `H ` | @aheber, @xixiafinland
|
||||
[sosl](https://github.com/aheber/tree-sitter-sfapex) | unstable | `H ` | @aheber, @xixiafinland
|
||||
[sourcepawn](https://github.com/nilshelmig/tree-sitter-sourcepawn) | unstable | `H JL` | @Sarrus1
|
||||
[sparql](https://github.com/GordianDziwis/tree-sitter-sparql) | unstable | `HFIJL` | @GordianDziwis
|
||||
[sproto](https://github.com/hanxi/tree-sitter-sproto) | unstable | `HFIJ ` | @hanxi
|
||||
[sql](https://github.com/derekstride/tree-sitter-sql) | unstable | `HFIJ ` | @derekstride
|
||||
[squirrel](https://github.com/tree-sitter-grammars/tree-sitter-squirrel) | unstable | `HFIJL` | @amaanq
|
||||
[ssh_config](https://github.com/tree-sitter-grammars/tree-sitter-ssh-config) | unstable | `HFIJL` | @ObserverOfTime
|
||||
[starlark](https://github.com/tree-sitter-grammars/tree-sitter-starlark) | unstable | `HFIJL` | @amaanq
|
||||
[strace](https://github.com/sigmaSd/tree-sitter-strace) | unstable | `H J ` | @amaanq
|
||||
[styled](https://github.com/mskelton/tree-sitter-styled) | unstable | `HFIJ ` | @mskelton
|
||||
[supercollider](https://github.com/madskjeldgaard/tree-sitter-supercollider) | unstable | `HFIJL` | @madskjeldgaard, @elgiano
|
||||
[superhtml](https://github.com/kristoff-it/superhtml) | unstable | `H J ` | @rockorager
|
||||
[surface](https://github.com/connorlay/tree-sitter-surface) | unstable | `HFIJ ` | @connorlay
|
||||
[svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) | unstable | `HFIJL` | @amaanq
|
||||
[sway](https://github.com/FuelLabs/tree-sitter-sway.git) | unstable | `HFIJL` | @ribru17
|
||||
[swift](https://github.com/alex-pinkus/tree-sitter-swift) | unstable | `HFIJL` | @alex-pinkus
|
||||
[sxhkdrc](https://github.com/RaafatTurki/tree-sitter-sxhkdrc) | unstable | `HF J ` | @RaafatTurki
|
||||
[systemtap](https://github.com/ok-ryoko/tree-sitter-systemtap) | unstable | `HF JL` | @ok-ryoko
|
||||
[systemverilog](https://github.com/gmlarumbe/tree-sitter-systemverilog) | unstable | `HF J ` | @zhangwwpeng
|
||||
[t32](https://github.com/xasc/tree-sitter-t32) | unstable | `HFIJL` | @xasc
|
||||
[tablegen](https://github.com/tree-sitter-grammars/tree-sitter-tablegen) | unstable | `HFIJL` | @amaanq
|
||||
[tact](https://github.com/tact-lang/tree-sitter-tact) | unstable | `HFIJL` | @novusnota
|
||||
[tcl](https://github.com/tree-sitter-grammars/tree-sitter-tcl) | unstable | `HFIJ ` | @lewis6991
|
||||
[teal](https://github.com/euclidianAce/tree-sitter-teal) | unstable | `HFIJL` | @euclidianAce
|
||||
[templ](https://github.com/vrischmann/tree-sitter-templ) | unstable | `HF J ` | @vrischmann
|
||||
[tera](https://github.com/uncenter/tree-sitter-tera) | unstable | `H J ` | @uncenter
|
||||
[terraform](https://github.com/MichaHoffmann/tree-sitter-hcl) | unstable | `HFIJ ` | @MichaHoffmann
|
||||
[textproto](https://github.com/PorterAtGoogle/tree-sitter-textproto) | unstable | `HFIJ ` | @Porter
|
||||
[thrift](https://github.com/tree-sitter-grammars/tree-sitter-thrift) | unstable | `HFIJL` | @amaanq, @duskmoon314
|
||||
[tiger](https://github.com/ambroisie/tree-sitter-tiger) | unstable | `HFIJL` | @ambroisie
|
||||
[tlaplus](https://github.com/tlaplus-community/tree-sitter-tlaplus) | unstable | `HF JL` | @ahelwer, @susliko
|
||||
[tmux](https://github.com/Freed-Wu/tree-sitter-tmux) | unstable | `H J ` | @Freed-Wu, @stevenxxiu
|
||||
[todotxt](https://github.com/arnarg/tree-sitter-todotxt) | unstable | `H ` | @arnarg
|
||||
[toml](https://github.com/tree-sitter-grammars/tree-sitter-toml) | unstable | `HFIJL` | @tk-shirasaka
|
||||
[tsv](https://github.com/tree-sitter-grammars/tree-sitter-csv) | unstable | `H ` | @amaanq
|
||||
[tsx](https://github.com/tree-sitter/tree-sitter-typescript) | unstable | `HFIJL` | @steelsojka
|
||||
[turtle](https://github.com/GordianDziwis/tree-sitter-turtle) | unstable | `HFIJL` | @GordianDziwis
|
||||
[twig](https://github.com/gbprod/tree-sitter-twig) | unstable | `H J ` | @gbprod
|
||||
[typescript](https://github.com/tree-sitter/tree-sitter-typescript) | unstable | `HFIJL` | @steelsojka
|
||||
[typespec](https://github.com/happenslol/tree-sitter-typespec) | unstable | `H IJ ` | @happenslol
|
||||
[typoscript](https://github.com/Teddytrombone/tree-sitter-typoscript) | unstable | `HFIJ ` | @Teddytrombone
|
||||
[typst](https://github.com/uben0/tree-sitter-typst) | unstable | `HFIJ ` | @uben0, @RaafatTurki
|
||||
[udev](https://github.com/tree-sitter-grammars/tree-sitter-udev) | unstable | `H JL` | @ObserverOfTime
|
||||
[ungrammar](https://github.com/tree-sitter-grammars/tree-sitter-ungrammar) | unstable | `HFIJL` | @Philipp-M, @amaanq
|
||||
[unison](https://github.com/kylegoetz/tree-sitter-unison) | unstable | `HF J ` | @tapegram
|
||||
[usd](https://github.com/ColinKennedy/tree-sitter-usd) | unstable | `HFIJL` | @ColinKennedy
|
||||
[uxntal](https://github.com/tree-sitter-grammars/tree-sitter-uxntal) | unstable | `HFIJL` | @amaanq
|
||||
[v](https://github.com/vlang/v-analyzer) | unstable | `HFIJL` | @kkharji, @amaanq
|
||||
[vala](https://github.com/vala-lang/tree-sitter-vala) | unstable | `HF J ` | @Prince781
|
||||
[vento](https://github.com/ventojs/tree-sitter-vento) | unmaintained | `H J ` |
|
||||
[vhdl](https://github.com/jpt13653903/tree-sitter-vhdl) | unstable | `HF J ` | @jpt13653903
|
||||
[vhs](https://github.com/charmbracelet/tree-sitter-vhs) | unstable | `H J ` | @caarlos0
|
||||
[vim](https://github.com/tree-sitter-grammars/tree-sitter-vim) | unstable | `HF JL` | @clason
|
||||
[vimdoc](https://github.com/neovim/tree-sitter-vimdoc) | unstable | `H J ` | @clason
|
||||
[vrl](https://github.com/belltoy/tree-sitter-vrl) | unstable | `HFIJL` | @belltoy
|
||||
[vue](https://github.com/tree-sitter-grammars/tree-sitter-vue) | unstable | `HFIJ ` | @WhyNotHugo, @lucario387
|
||||
[wgsl](https://github.com/szebniok/tree-sitter-wgsl) | unstable | `HFIJ ` | @szebniok
|
||||
[wgsl_bevy](https://github.com/tree-sitter-grammars/tree-sitter-wgsl-bevy) | unstable | `HFI ` | @theHamsta
|
||||
[wing](https://github.com/winglang/tree-sitter-wing) | unstable | `HF JL` | @gshpychka, @MarkMcCulloh
|
||||
[wit](https://github.com/bytecodealliance/tree-sitter-wit) | stable | `HF J ` | @mkatychev
|
||||
[wxml](https://github.com/BlockLune/tree-sitter-wxml) | unstable | `HFIJ ` | @BlockLune
|
||||
[xcompose](https://github.com/tree-sitter-grammars/tree-sitter-xcompose) | unstable | `H JL` | @ObserverOfTime
|
||||
[xml](https://github.com/tree-sitter-grammars/tree-sitter-xml) | unstable | `HFIJL` | @ObserverOfTime
|
||||
[xresources](https://github.com/ValdezFOmar/tree-sitter-xresources) | stable | `HF JL` | @ValdezFOmar
|
||||
[yaml](https://github.com/tree-sitter-grammars/tree-sitter-yaml) | unstable | `HFIJL` | @amaanq
|
||||
[yang](https://github.com/Hubro/tree-sitter-yang) | unstable | `HFIJ ` | @Hubro
|
||||
[yuck](https://github.com/tree-sitter-grammars/tree-sitter-yuck) | unstable | `HFIJL` | @Philipp-M, @amaanq
|
||||
[zathurarc](https://github.com/Freed-Wu/tree-sitter-zathurarc) | unstable | `H J ` | @Freed-Wu
|
||||
[zig](https://github.com/tree-sitter-grammars/tree-sitter-zig) | unstable | `HFIJL` | @amaanq
|
||||
[ziggy](https://github.com/kristoff-it/ziggy) | unmaintained | `H I ` |
|
||||
[ziggy_schema](https://github.com/kristoff-it/ziggy) | unmaintained | `H I ` |
|
||||
[zsh](https://github.com/georgeharker/tree-sitter-zsh) | unstable | `HF JL` | @georgeharker
|
||||
[^bp]: Android Blueprint
|
||||
[^ecma]: queries required by javascript, typescript, tsx, qmljs
|
||||
[^gap]: GAP system
|
||||
[^gaptst]: GAP system test files
|
||||
[^gdscript]: Godot
|
||||
[^glimmer]: Glimmer and Ember
|
||||
[^godot_resource]: Godot Resources
|
||||
[^html_tags]: queries required by html, astro, vue, svelte
|
||||
[^jinja]: basic highlighting
|
||||
[^jinja_inline]: needed for full highlighting
|
||||
[^jsx]: queries required by javascript, tsx
|
||||
[^luap]: Lua patterns
|
||||
[^markdown]: basic highlighting
|
||||
[^markdown_inline]: needed for full highlighting
|
||||
[^php]: PHP with embedded HTML
|
||||
[^php_only]: PHP without embedded HTML
|
||||
[^poe_filter]: Path of Exile item filter
|
||||
[^properties]: Java properties files
|
||||
[^query]: Tree-sitter query language
|
||||
[^sflog]: Salesforce debug log
|
||||
[^slang]: Shader Slang
|
||||
[^snl]: EPICS Sequencer's SNL files
|
||||
<!--parserinfo-->
|
||||
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
27
autoload/nvim_treesitter.vim
Normal file
27
autoload/nvim_treesitter.vim
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
function! nvim_treesitter#statusline(...) abort
|
||||
return luaeval("require'nvim-treesitter.statusline'.statusline(_A)", get(a:, 1, {}))
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#foldexpr() abort
|
||||
return luaeval(printf('require"nvim-treesitter.fold".get_fold_indic(%d)', v:lnum))
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#installable_parsers(arglead, cmdline, cursorpos) abort
|
||||
return join(luaeval("require'nvim-treesitter.parsers'.available_parsers()") + ['all'], "\n")
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#installed_parsers(arglead, cmdline, cursorpos) abort
|
||||
return join(luaeval("require'nvim-treesitter.info'.installed_parsers()") + ['all'], "\n")
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#available_modules(arglead, cmdline, cursorpos) abort
|
||||
return join(luaeval("require'nvim-treesitter.configs'.available_modules()"), "\n")
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#available_query_groups(arglead, cmdline, cursorpos) abort
|
||||
return join(luaeval("require'nvim-treesitter.query'.available_query_groups()"), "\n")
|
||||
endfunction
|
||||
|
||||
function! nvim_treesitter#indent() abort
|
||||
return luaeval(printf('require"nvim-treesitter.indent".get_indent(%d)', v:lnum))
|
||||
endfunction
|
||||
32
contrib/nvim-treesitter-scm-1.rockspec
Normal file
32
contrib/nvim-treesitter-scm-1.rockspec
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
local MODREV, SPECREV = "scm", "-1"
|
||||
rockspec_format = "3.0"
|
||||
package = "nvim-treesitter"
|
||||
version = MODREV .. SPECREV
|
||||
|
||||
description = {
|
||||
summary = "Nvim Treesitter configurations and abstraction layer",
|
||||
labels = { "neovim"},
|
||||
homepage = "https://github.com/nvim-treesitter/nvim-treesitter",
|
||||
license = "Apache-2.0",
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.4",
|
||||
}
|
||||
|
||||
source = {
|
||||
url = "http://github.com/nvim-treesitter/nvim-treesitter/archive/v" .. MODREV .. ".zip",
|
||||
}
|
||||
|
||||
if MODREV == 'scm' then
|
||||
source = {
|
||||
url = 'git://github.com/nvim-treesitter/nvim-treesitter',
|
||||
}
|
||||
end
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
copy_directories = {
|
||||
'plugin'
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
*nvim-treesitter.txt* Treesitter parser and query installer for Neovim
|
||||
*nvim-treesitter* Treesitter configurations and abstraction layer for Neovim.
|
||||
|
||||
Minimum version of neovim: nightly
|
||||
|
||||
Authors:
|
||||
Kiyan Yazdani <yazdani.kiyan@protonmail.com>
|
||||
|
|
@ -11,179 +13,470 @@ Authors:
|
|||
Type |gO| to see the table of contents.
|
||||
|
||||
==============================================================================
|
||||
INTRODUCTION *nvim-treesitter-intro*
|
||||
INTRODUCTION *nvim-treesitter-intro*
|
||||
|
||||
Nvim-treesitter provides functionalities for managing treesitter parsers and
|
||||
compatible queries for core features (highlighting, injections, folds,
|
||||
indents).
|
||||
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*
|
||||
QUICK START *nvim-treesitter-quickstart*
|
||||
|
||||
To configure `nvim-treesitter`, put this in your `init.lua` file:
|
||||
>lua
|
||||
require'nvim-treesitter'.setup {
|
||||
-- A directory to install the parsers and queries to.
|
||||
-- Defaults to the `stdpath('data')/site` dir.
|
||||
install_dir = "/some/path/to/store/parsers",
|
||||
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 directory to install the parsers into.
|
||||
-- If this is excluded or nil parsers are installed
|
||||
-- to either the package dir, or the "site" dir.
|
||||
-- If a custom path is used (not nil) it must be added to the runtimepath.
|
||||
parser_install_dir = "/some/path/to/store/parsers",
|
||||
|
||||
-- A list of parser names, or "all"
|
||||
ensure_installed = { "c", "lua", "rust" },
|
||||
|
||||
-- Install parsers synchronously (only applied to `ensure_installed`)
|
||||
sync_install = false,
|
||||
|
||||
-- Automatically install missing parsers when entering buffer
|
||||
auto_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,
|
||||
},
|
||||
}
|
||||
|
||||
NOTE: You do not need to call `setup` to use this plugin with the default
|
||||
settings!
|
||||
|
||||
Parsers and queries can then be installed with >lua
|
||||
require'nvim-treesitter'.install { 'rust', 'javascript', 'zig' }
|
||||
vim.opt.runtimepath:append("/some/path/to/store/parsers")
|
||||
<
|
||||
To check installed parsers and queries, use `:checkhealth nvim-treesitter`.
|
||||
|
||||
Treesitter features for installed languages need to be enabled manually in a
|
||||
|FileType| autocommand or |ftplugin|, e.g. >lua
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = { 'rust', 'javascript', 'zig' },
|
||||
callback = function()
|
||||
-- syntax highlighting, provided by Neovim
|
||||
vim.treesitter.start()
|
||||
-- folds, provided by Neovim
|
||||
vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
|
||||
vim.wo.foldmethod = 'expr'
|
||||
-- indentation, provided by nvim-treesitter
|
||||
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||
end,
|
||||
})
|
||||
<
|
||||
==============================================================================
|
||||
COMMANDS *nvim-treesitter-commands*
|
||||
|
||||
:TSInstall {language} *:TSInstall*
|
||||
|
||||
Install one or more treesitter parsers. {language} can be one or multiple
|
||||
parsers or tiers (`stable`, `unstable`, or `all` (not recommended)). This is a
|
||||
no-op if the parser(s) are already installed. Installation is performed
|
||||
asynchronously. Use *:TSInstall!* to force installation even if a parser is
|
||||
already installed.
|
||||
|
||||
:TSInstallFromGrammar {language} *:TSInstallFromGrammar*
|
||||
|
||||
Like |:TSInstall| but also regenerates the `parser.c` from the original
|
||||
grammar. Useful for languages where the provided `parser.c` is outdated (e.g.,
|
||||
uses a no longer supported ABI).
|
||||
|
||||
:TSUpdate [{language}] *:TSUpdate*
|
||||
|
||||
Update parsers to the `revision` specified in the manifest if this is newer
|
||||
than the installed version. If {language} is specified, update the
|
||||
corresponding parser or tier; otherwise update all installed parsers. This is
|
||||
a no-op if all (specified) parsers are up to date.
|
||||
|
||||
Note: It is recommended to add this command as a build step in your plugin
|
||||
manager.
|
||||
|
||||
:TSUninstall {language} *:TSUninstall*
|
||||
|
||||
Deletes the parser for one or more {language}, or all parsers with `all`.
|
||||
|
||||
:TSLog *:TSLog*
|
||||
|
||||
Shows all messages from previous install, update, or uninstall operations.
|
||||
See |nvim-treesitter-modules| for a list of all available modules and its options.
|
||||
|
||||
==============================================================================
|
||||
API *nvim-treesitter-api*
|
||||
MODULES *nvim-treesitter-modules*
|
||||
|
||||
setup({opts}) *nvim-treesitter.setup()*
|
||||
|nvim-treesitter| provides several functionalities via modules (and submodules),
|
||||
each module makes use of the query files defined for each language,
|
||||
|
||||
Configure installation options. Needs to be specified before any
|
||||
installation operation.
|
||||
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.
|
||||
|
||||
Note: You only need to call `setup` if you want to set non-default
|
||||
options!
|
||||
|
||||
Parameters: ~
|
||||
• {opts} `(table?)` Optional parameters:
|
||||
• {install_dir} (`string?`, default `stdpath('data')/site/`)
|
||||
directory to install parsers and queries to. Note: will be
|
||||
prepended to |runtimepath|.
|
||||
|
||||
install({languages} [, {opts}]) *nvim-treesitter.install()*
|
||||
|
||||
Download, compile, and install the specified treesitter parsers and copy
|
||||
the corresponding queries to a directory on |runtimepath|, enabling their
|
||||
use in Neovim.
|
||||
|
||||
Note: This operation is performed asynchronously by default. For
|
||||
synchronous operation (e.g., in a bootstrapping script), you need to
|
||||
`wait()` for it: >lua
|
||||
require('nvim-treesitter').install({ 'rust', 'javascript', 'zig' })
|
||||
:wait(300000) -- max. 5 minutes
|
||||
>
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
-- Modules and its options go here
|
||||
highlight = { enable = true },
|
||||
incremental_selection = { enable = true },
|
||||
textobjects = { enable = true },
|
||||
}
|
||||
<
|
||||
Parameters: ~
|
||||
• {languages} `(string[]|string)` (List of) languages or tiers (`stable`,
|
||||
`unstable`) to install.
|
||||
• {opts} `(table?)` Optional parameters:
|
||||
• {force} (`boolean?`, default `false`) force installation
|
||||
of already installed parsers
|
||||
• {generate} (`boolean?`, default `false`) generate
|
||||
`parser.c` from `grammar.json` or `grammar.js` before
|
||||
compiling.
|
||||
• {max_jobs} (`integer?`) limit parallel tasks (useful in
|
||||
combination with {generate} on memory-limited systems).
|
||||
• {summary} (`boolean?`, default `false`) print summary of
|
||||
successful and total operations for multiple languages.
|
||||
|
||||
uninstall({languages} [, {opts}]) *nvim-treesitter.uninstall()*
|
||||
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.
|
||||
|
||||
Remove the parser and queries for the specified language(s).
|
||||
|
||||
Parameters: ~
|
||||
• {languages} `(string[]|string)` (List of) languages or tiers (`stable`,
|
||||
`unstable`) to update.
|
||||
• {opts} `(table?)` Optional parameters:
|
||||
• {summary} (`boolean?`, default `false`) print summary of
|
||||
successful and total operations for multiple languages.
|
||||
|
||||
update([{languages}, {opts}]) *nvim-treesitter.update()*
|
||||
|
||||
Update the parsers and queries if older than the revision specified in the
|
||||
manifest.
|
||||
|
||||
Note: This operation is performed asynchronously by default. For
|
||||
synchronous operation (e.g., in a bootstrapping script), you need to
|
||||
`wait()` for it: >lua
|
||||
require('nvim-treesitter').update():wait(300000) -- max. 5 minutes
|
||||
>
|
||||
require'nvim-treesitter.configs'.setup {
|
||||
highlight = {
|
||||
enable = true,
|
||||
disable = { "cpp", "lua" },
|
||||
},
|
||||
}
|
||||
<
|
||||
Parameters: ~
|
||||
• {languages} `(string[]|string)?` (List of) languages or tiers to update
|
||||
(default: all installed).
|
||||
• {opts} `(table?)` Optional parameters:
|
||||
• {max_jobs} (`integer?`) limit parallel tasks (useful in
|
||||
combination with {generate} on memory-limited systems).
|
||||
• {summary} (`boolean?`, default `false`) print summary of
|
||||
successful and total operations for multiple languages.
|
||||
|
||||
indentexpr() *nvim-treesitter.indentexpr()*
|
||||
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:
|
||||
|
||||
Used to enable treesitter indentation for a language via >lua
|
||||
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||
>
|
||||
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,
|
||||
},
|
||||
}
|
||||
<
|
||||
get_available([{tier}]) *nvim-treesitter.get_available()*
|
||||
|
||||
Return list of languages available for installation.
|
||||
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.
|
||||
|
||||
Parameters: ~
|
||||
• {tier} `(integer?)` Only return languages of specified {tier} (`1`:
|
||||
stable, `2`: unstable, `3`: unmaintained, `4`: unsupported)
|
||||
External plugins can provide their own modules with their own options,
|
||||
those can also be configured using the `nvim-treesitter.configs.setup`
|
||||
function.
|
||||
|
||||
get_installed([{type}]) *nvim-treesitter.get_installed()*
|
||||
------------------------------------------------------------------------------
|
||||
HIGHLIGHT *nvim-treesitter-highlight-mod*
|
||||
|
||||
Return list of languages installed via `nvim-treesitter`.
|
||||
Consistent syntax highlighting.
|
||||
|
||||
Note: This only searches `nvim-treesitter`'s (configured or default)
|
||||
installation directory; parsers and queries from other sources can be
|
||||
placed anywhere on 'runtimepath' and are not included. To list all, e.g.,
|
||||
parsers that are installed from any source, use >lua
|
||||
vim.api.nvim_get_runtime_file('parser/*', true)
|
||||
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,
|
||||
},
|
||||
}
|
||||
<
|
||||
Parameters: ~
|
||||
• {type} `('queries'|parsers'?)` If specified, only show languages with
|
||||
installed queries or parsers, respectively.
|
||||
|
||||
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.is_parent*
|
||||
is_parent(dest, source)~
|
||||
|
||||
Determines whether `dest` is a parent of `source`.
|
||||
Returns a boolean.
|
||||
|
||||
*ts_utils.get_named_children*
|
||||
get_named_children(node)~
|
||||
|
||||
Returns a table of named children of `node`.
|
||||
|
||||
*ts_utils.get_next_node*
|
||||
get_next_node(node, allow_switch_parent, allow_next_parent)~
|
||||
|
||||
Returns the next node within the same parent.
|
||||
If no node is found, returns `nil`.
|
||||
If `allow_switch_parent` is true, it will allow switching parent
|
||||
when the node is the last node.
|
||||
If `allow_next_parent` is true, it will allow next parent if
|
||||
the node is the last node and the next parent doesn't have children.
|
||||
|
||||
*ts_utils.get_previous_node*
|
||||
get_previous_node(node, allow_switch_parents, allow_prev_parent)~
|
||||
|
||||
Returns the previous node within the same parent.
|
||||
`allow_switch_parent` and `allow_prev_parent` follow the same rule
|
||||
as |ts_utils.get_next_node| but if the node is the first node.
|
||||
|
||||
*ts_utils.goto_node*
|
||||
goto_node(node, goto_end, avoid_set_jump)~
|
||||
|
||||
Sets cursor to the position of `node` in the current windows.
|
||||
If `goto_end` is truthy, the cursor is set to the end the node range.
|
||||
Setting `avoid_set_jump` to `true`, avoids setting the current cursor position
|
||||
to the jump list.
|
||||
|
||||
*ts_utils.swap_nodes*
|
||||
swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second)~
|
||||
|
||||
Swaps the nodes or ranges.
|
||||
set `cursor_to_second` to true to move the cursor to the second node
|
||||
|
||||
*ts_utils.memoize_by_buf_tick*
|
||||
memoize_by_buf_tick(fn, options)~
|
||||
|
||||
Caches the return value for a function and returns the cache value if the tick
|
||||
of the buffer has not changed from the previous.
|
||||
|
||||
`fn`: a function that takes any arguments
|
||||
and returns a value to store.
|
||||
`options?`: <table>
|
||||
- `bufnr`: a function/value that extracts the bufnr from the given arguments.
|
||||
- `key`: a function/value that extracts the cache key from the given arguments.
|
||||
`returns`: a function to call with bufnr as argument to
|
||||
retrieve the value from the cache
|
||||
|
||||
*ts_utils.node_to_lsp_range*
|
||||
node_to_lsp_range(node)~
|
||||
|
||||
Get an lsp formatted range from a node range
|
||||
|
||||
*ts_utils.node_length*
|
||||
node_length(node)~
|
||||
|
||||
Get the byte length of node range
|
||||
|
||||
*ts_utils.update_selection*
|
||||
update_selection(buf, node)~
|
||||
|
||||
Set the selection to the node range
|
||||
|
||||
*ts_utils.highlight_range*
|
||||
highlight_range(range, buf, hl_namespace, hl_group)~
|
||||
|
||||
Set a highlight that spans the given range
|
||||
|
||||
*ts_utils.highlight_node*
|
||||
highlight_node(node, buf, hl_namespace, hl_group)~
|
||||
|
||||
Set a highlight that spans the given node's range
|
||||
|
||||
==============================================================================
|
||||
FUNCTIONS *nvim-treesitter-functions*
|
||||
|
||||
*nvim_treesitter#statusline()*
|
||||
nvim_treesitter#statusline(opts)~
|
||||
|
||||
Returns a string describing the current position in the file. This
|
||||
could be used as a statusline indicator.
|
||||
Default options (lua syntax):
|
||||
>
|
||||
{
|
||||
indicator_size = 100,
|
||||
type_patterns = {'class', 'function', 'method'},
|
||||
transform_fn = function(line) 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.
|
||||
|
||||
==============================================================================
|
||||
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:
|
||||
|
|
|
|||
479
lockfile.json
Normal file
479
lockfile.json
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
{
|
||||
"ada": {
|
||||
"revision": "e9e2ec9d3b6302e9b455901bec00036e29d1c121"
|
||||
},
|
||||
"agda": {
|
||||
"revision": "80ea622cf952a0059e168e5c92a798b2f1925652"
|
||||
},
|
||||
"arduino": {
|
||||
"revision": "257efffa387da3283a37816b71dedfecf4af5222"
|
||||
},
|
||||
"astro": {
|
||||
"revision": "a1f66bf72ed68b87f779bce9a52e5c6521fc867e"
|
||||
},
|
||||
"awk": {
|
||||
"revision": "e559793754c60c2cdf00cbb0409842d75f0a41dc"
|
||||
},
|
||||
"bash": {
|
||||
"revision": "7f9506c34ab6a0f4e3e052b7a49cbeef91f71236"
|
||||
},
|
||||
"beancount": {
|
||||
"revision": "f3741a3a68ade59ec894ed84a64673494d2ba8f3"
|
||||
},
|
||||
"bibtex": {
|
||||
"revision": "ccfd77db0ed799b6c22c214fe9d2937f47bc8b34"
|
||||
},
|
||||
"blueprint": {
|
||||
"revision": "6ef91ca8270f0112b9c6d27ecb9966c741a5d103"
|
||||
},
|
||||
"c": {
|
||||
"revision": "7175a6dd5fc1cee660dce6fe23f6043d75af424a"
|
||||
},
|
||||
"c_sharp": {
|
||||
"revision": "18e434383a4582b4fd183a30e55022c2923764e1"
|
||||
},
|
||||
"capnp": {
|
||||
"revision": "cb85cddfdf398530110c807ba046822dbaee6afb"
|
||||
},
|
||||
"chatito": {
|
||||
"revision": "3baf22e7e507cedf15d1dbc03df8afa50a625586"
|
||||
},
|
||||
"clojure": {
|
||||
"revision": "262d6d60f39f0f77b3dd08da8ec895bd5a044416"
|
||||
},
|
||||
"cmake": {
|
||||
"revision": "399605a02bcd5daa309ce63a6459c600dce3473f"
|
||||
},
|
||||
"comment": {
|
||||
"revision": "a37ca370310ac6f89b6e0ebf2b86b2219780494e"
|
||||
},
|
||||
"commonlisp": {
|
||||
"revision": "c7e814975ab0d0d04333d1f32391c41180c58919"
|
||||
},
|
||||
"cooklang": {
|
||||
"revision": "5e113412aadb78955c27010daa4dbe1d202013cf"
|
||||
},
|
||||
"cpp": {
|
||||
"revision": "56cec4c2eb5d6af3d2942e69e35db15ae2433740"
|
||||
},
|
||||
"css": {
|
||||
"revision": "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51"
|
||||
},
|
||||
"cuda": {
|
||||
"revision": "a02c21408c592e6e6856eaabe4727faa97cf8d85"
|
||||
},
|
||||
"d": {
|
||||
"revision": "c2fbf21bd3aa45495fe13247e040ad5815250032"
|
||||
},
|
||||
"dart": {
|
||||
"revision": "53485a8f301254e19c518aa20c80f1bcf7cf5c62"
|
||||
},
|
||||
"devicetree": {
|
||||
"revision": "ea30a05d0f0446a96d8b096ad11828ad4f8ad849"
|
||||
},
|
||||
"diff": {
|
||||
"revision": "f69bde8e56f431863eba2fe4bab23e7d9692855f"
|
||||
},
|
||||
"dockerfile": {
|
||||
"revision": "09e316dba307b869831e9399b11a83bbf0f2a24b"
|
||||
},
|
||||
"dot": {
|
||||
"revision": "9ab85550c896d8b294d9b9ca1e30698736f08cea"
|
||||
},
|
||||
"ebnf": {
|
||||
"revision": "8e635b0b723c620774dfb8abf382a7f531894b40"
|
||||
},
|
||||
"eex": {
|
||||
"revision": "f742f2fe327463335e8671a87c0b9b396905d1d1"
|
||||
},
|
||||
"elixir": {
|
||||
"revision": "b20eaa75565243c50be5e35e253d8beb58f45d56"
|
||||
},
|
||||
"elm": {
|
||||
"revision": "28bb193640d916dfaf947912c1413cebb0484841"
|
||||
},
|
||||
"elsa": {
|
||||
"revision": "c83c21c1f8f6b38dfc5bd1392de03a7b05bb78f4"
|
||||
},
|
||||
"elvish": {
|
||||
"revision": "f32711e31e987fd5c2c002f3daba02f25c68672f"
|
||||
},
|
||||
"embedded_template": {
|
||||
"revision": "203f7bd3c1bbfbd98fc19add4b8fcb213c059205"
|
||||
},
|
||||
"erlang": {
|
||||
"revision": "2422bc9373094bfa97653ac540e08759f812523c"
|
||||
},
|
||||
"fennel": {
|
||||
"revision": "517195970428aacca60891b050aa53eabf4ba78d"
|
||||
},
|
||||
"fish": {
|
||||
"revision": "f9176908c9eb2e11eb684d79e1d00f3b29bd65c9"
|
||||
},
|
||||
"foam": {
|
||||
"revision": "c238f4af9a5723a212cf1a4c9b31dd5c1d5270a2"
|
||||
},
|
||||
"fortran": {
|
||||
"revision": "bfa6fd4c4aa0bb9b39ad33daa004ad4637a91d20"
|
||||
},
|
||||
"fsh": {
|
||||
"revision": "fa3347712f7a59ed02ccf508284554689c6cde28"
|
||||
},
|
||||
"func": {
|
||||
"revision": "f161cfe22452b386eeeab29ba0d2c14893f1a31f"
|
||||
},
|
||||
"fusion": {
|
||||
"revision": "19db2f47ba4c3a0f6238d4ae0e2abfca16e61dd6"
|
||||
},
|
||||
"gdscript": {
|
||||
"revision": "5d43d78c276570f76773685f08baf9e4ada09639"
|
||||
},
|
||||
"git_rebase": {
|
||||
"revision": "127f5b56c1ad3e8a449a7d6e0c7412ead7f7724c"
|
||||
},
|
||||
"gitattributes": {
|
||||
"revision": "577a075d46ea109905c5cb6179809df88da61ce9"
|
||||
},
|
||||
"gitcommit": {
|
||||
"revision": "f71b93f399c9c2b315825827c95466e7405ec622"
|
||||
},
|
||||
"gitignore": {
|
||||
"revision": "f4685bf11ac466dd278449bcfe5fd014e94aa504"
|
||||
},
|
||||
"gleam": {
|
||||
"revision": "cfcbca3f8f734773878e00d7bfcedea98eb10be2"
|
||||
},
|
||||
"glimmer": {
|
||||
"revision": "40cfb72a53654cbd666451ca04ffd500257c7b73"
|
||||
},
|
||||
"glsl": {
|
||||
"revision": "e2c2214045de2628b81089b1a739962f59654558"
|
||||
},
|
||||
"go": {
|
||||
"revision": "64457ea6b73ef5422ed1687178d4545c3e91334a"
|
||||
},
|
||||
"godot_resource": {
|
||||
"revision": "b6ef0768711086a86b3297056f9ffb5cc1d77b4a"
|
||||
},
|
||||
"gomod": {
|
||||
"revision": "4a65743dbc2bb3094114dd2b43da03c820aa5234"
|
||||
},
|
||||
"gosum": {
|
||||
"revision": "68974b63c19dc6e27214a5c76b6e26c0c40fe5b7"
|
||||
},
|
||||
"gowork": {
|
||||
"revision": "949a8a470559543857a62102c84700d291fc984c"
|
||||
},
|
||||
"graphql": {
|
||||
"revision": "5e66e961eee421786bdda8495ed1db045e06b5fe"
|
||||
},
|
||||
"hack": {
|
||||
"revision": "b7bd6928532ada34dddb1dece4a158ab62c6e783"
|
||||
},
|
||||
"haskell": {
|
||||
"revision": "3bdba07c7a8eec23f87fa59ce9eb2ea4823348b3"
|
||||
},
|
||||
"hcl": {
|
||||
"revision": "0ff887f2a60a147452d52db060de6b42f42f1441"
|
||||
},
|
||||
"heex": {
|
||||
"revision": "2e1348c3cf2c9323e87c2744796cf3f3868aa82a"
|
||||
},
|
||||
"help": {
|
||||
"revision": "8f75ef3ec86bc315d5fdb939899b397289389181"
|
||||
},
|
||||
"hjson": {
|
||||
"revision": "02fa3b79b3ff9a296066da6277adfc3f26cbc9e0"
|
||||
},
|
||||
"hlsl": {
|
||||
"revision": "8e2f0907e8d2e17a88a375025e70054bafdaa8b0"
|
||||
},
|
||||
"hocon": {
|
||||
"revision": "c390f10519ae69fdb03b3e5764f5592fb6924bcc"
|
||||
},
|
||||
"html": {
|
||||
"revision": "29f53d8f4f2335e61bf6418ab8958dac3282077a"
|
||||
},
|
||||
"htmldjango": {
|
||||
"revision": "b2dba02eddab66be669022320273d0dfe1ff923d"
|
||||
},
|
||||
"http": {
|
||||
"revision": "2c6c44574031263326cb1e51658bbc0c084326e7"
|
||||
},
|
||||
"ini": {
|
||||
"revision": "1a0ce072ebf3afac7d5603d9a95bb7c9a6709b44"
|
||||
},
|
||||
"java": {
|
||||
"revision": "dd597f13eb9bab0c1bccc9aec390e8e6ebf9e0a6"
|
||||
},
|
||||
"javascript": {
|
||||
"revision": "15e85e80b851983fab6b12dce5a535f5a0df0f9c"
|
||||
},
|
||||
"jq": {
|
||||
"revision": "13990f530e8e6709b7978503da9bc8701d366791"
|
||||
},
|
||||
"jsdoc": {
|
||||
"revision": "189a6a4829beb9cdbe837260653b4a3dfb0cc3db"
|
||||
},
|
||||
"json": {
|
||||
"revision": "73076754005a460947cafe8e03a8cf5fa4fa2938"
|
||||
},
|
||||
"json5": {
|
||||
"revision": "5dd5cdc418d9659682556b6adca2dd9ace0ac6d2"
|
||||
},
|
||||
"jsonc": {
|
||||
"revision": "02b01653c8a1c198ae7287d566efa86a135b30d5"
|
||||
},
|
||||
"jsonnet": {
|
||||
"revision": "fdc775714afa27fdef823adbaba6ab98f5ae66f2"
|
||||
},
|
||||
"julia": {
|
||||
"revision": "e2f449e2bcc95f1d07ceb62d67f986005f73a6be"
|
||||
},
|
||||
"kdl": {
|
||||
"revision": "b50d6c8b77d311639ecbf2b803ffb720c2b4cee2"
|
||||
},
|
||||
"kotlin": {
|
||||
"revision": "e4637037a5fe6f25fe66c305669faa0855f35692"
|
||||
},
|
||||
"lalrpop": {
|
||||
"revision": "7744b56f03ac1e5643fad23c9dd90837fe97291e"
|
||||
},
|
||||
"latex": {
|
||||
"revision": "6b7ea839307670e6bda011f888717d3a882ecc09"
|
||||
},
|
||||
"ledger": {
|
||||
"revision": "47b8971448ce5e9abac865f450c1b14fb3b6eee9"
|
||||
},
|
||||
"llvm": {
|
||||
"revision": "e9948edc41e9e5869af99dddb2b5ff5cc5581af6"
|
||||
},
|
||||
"lua": {
|
||||
"revision": "0fc89962b7ff5c7d676b8592c1cbce1ceaa806fd"
|
||||
},
|
||||
"m68k": {
|
||||
"revision": "d097b123f19c6eaba2bf181c05420d88b9fc489d"
|
||||
},
|
||||
"make": {
|
||||
"revision": "a4b9187417d6be349ee5fd4b6e77b4172c6827dd"
|
||||
},
|
||||
"markdown": {
|
||||
"revision": "7e7aa9a25ca9729db9fe22912f8f47bdb403a979"
|
||||
},
|
||||
"markdown_inline": {
|
||||
"revision": "7e7aa9a25ca9729db9fe22912f8f47bdb403a979"
|
||||
},
|
||||
"menhir": {
|
||||
"revision": "db7953acb0d5551f207373c81fa07a57d7b085cb"
|
||||
},
|
||||
"mermaid": {
|
||||
"revision": "d787c66276e7e95899230539f556e8b83ee16f6d"
|
||||
},
|
||||
"meson": {
|
||||
"revision": "5f3138d555aceef976ec9a1d4a3f78e13b31e45f"
|
||||
},
|
||||
"nickel": {
|
||||
"revision": "d6c7eeb751038f934b5b1aa7ff236376d0235c56"
|
||||
},
|
||||
"ninja": {
|
||||
"revision": "0a95cfdc0745b6ae82f60d3a339b37f19b7b9267"
|
||||
},
|
||||
"nix": {
|
||||
"revision": "6b71a810c0acd49b980c50fc79092561f7cee307"
|
||||
},
|
||||
"norg": {
|
||||
"revision": "1a305093569632de50f9a316ff843dcda25b4ef5"
|
||||
},
|
||||
"ocaml": {
|
||||
"revision": "f1106bf834703f1f2f795da1a3b5f8f40174ffcc"
|
||||
},
|
||||
"ocaml_interface": {
|
||||
"revision": "f1106bf834703f1f2f795da1a3b5f8f40174ffcc"
|
||||
},
|
||||
"ocamllex": {
|
||||
"revision": "ac1d5957e719d49bd6acd27439b79843e4daf8ed"
|
||||
},
|
||||
"org": {
|
||||
"revision": "081179c52b3e8175af62b9b91dc099d010c38770"
|
||||
},
|
||||
"pascal": {
|
||||
"revision": "9e995404ddff8319631d72d4b46552e737206912"
|
||||
},
|
||||
"perl": {
|
||||
"revision": "749d26fe13fb131b92e6515416096e572575b981"
|
||||
},
|
||||
"php": {
|
||||
"revision": "f860e598194f4a71747f91789bf536b393ad4a56"
|
||||
},
|
||||
"phpdoc": {
|
||||
"revision": "2f4d16c861b5a454b577d057f247f9902d7b47f5"
|
||||
},
|
||||
"pioasm": {
|
||||
"revision": "924aadaf5dea2a6074d72027b064f939acf32e20"
|
||||
},
|
||||
"poe_filter": {
|
||||
"revision": "80dc10195e26c72598ed1ab02cdf2d8e4c792e7b"
|
||||
},
|
||||
"prisma": {
|
||||
"revision": "eca2596a355b1a9952b4f80f8f9caed300a272b5"
|
||||
},
|
||||
"proto": {
|
||||
"revision": "42d82fa18f8afe59b5fc0b16c207ee4f84cb185f"
|
||||
},
|
||||
"pug": {
|
||||
"revision": "884e225b5ecca5d885ae627275f16ef648acd42e"
|
||||
},
|
||||
"python": {
|
||||
"revision": "9e53981ec31b789ee26162ea335de71f02186003"
|
||||
},
|
||||
"ql": {
|
||||
"revision": "bd087020f0d8c183080ca615d38de0ec827aeeaf"
|
||||
},
|
||||
"qmljs": {
|
||||
"revision": "ab75be9750e6f2f804638824d1790034286a830c"
|
||||
},
|
||||
"query": {
|
||||
"revision": "0717de07078a20a8608c98ad5f26c208949d0e15"
|
||||
},
|
||||
"r": {
|
||||
"revision": "80efda55672d1293aa738f956c7ae384ecdc31b4"
|
||||
},
|
||||
"racket": {
|
||||
"revision": "1a5df0206b25a05cb1b35a68d2105fc7493df39b"
|
||||
},
|
||||
"rasi": {
|
||||
"revision": "5f04634dd4e12de4574c4a3dc9d6d5d4da4a2a1b"
|
||||
},
|
||||
"regex": {
|
||||
"revision": "e1cfca3c79896ff79842f057ea13e529b66af636"
|
||||
},
|
||||
"rego": {
|
||||
"revision": "b2667c975f07b33be3ceb83bea5cfbad88095866"
|
||||
},
|
||||
"rnoweb": {
|
||||
"revision": "502c1126dc6777f09af5bef16e72a42f75bd081e"
|
||||
},
|
||||
"ron": {
|
||||
"revision": "049a3ef4e271089107dd08e4aeb195abd1f77103"
|
||||
},
|
||||
"rst": {
|
||||
"revision": "25e6328872ac3a764ba8b926aea12719741103f1"
|
||||
},
|
||||
"ruby": {
|
||||
"revision": "206c7077164372c596ffa8eaadb9435c28941364"
|
||||
},
|
||||
"rust": {
|
||||
"revision": "f7fb205c424b0962de59b26b931fe484e1262b35"
|
||||
},
|
||||
"scala": {
|
||||
"revision": "628e0aab6c2f7d31cf3b7d730f964d4fd9b340ee"
|
||||
},
|
||||
"scheme": {
|
||||
"revision": "67b90a365bebf4406af4e5a546d6336de787e135"
|
||||
},
|
||||
"scss": {
|
||||
"revision": "c478c6868648eff49eb04a4df90d703dc45b312a"
|
||||
},
|
||||
"slint": {
|
||||
"revision": "07547525cdf4627343dca5891f1743ae45e879bb"
|
||||
},
|
||||
"smali": {
|
||||
"revision": "d7f535e176c928d33b0e202dd808ac247cacf2ff"
|
||||
},
|
||||
"smithy": {
|
||||
"revision": "cf8c7eb9faf7c7049839585eac19c94af231e6a0"
|
||||
},
|
||||
"solidity": {
|
||||
"revision": "52ed0880c0126df2f2c7693f215fe6f38e4a2e0a"
|
||||
},
|
||||
"sparql": {
|
||||
"revision": "05f949d3c1c15e3261473a244d3ce87777374dec"
|
||||
},
|
||||
"sql": {
|
||||
"revision": "7be06f4d5eabace883dd45959c13dc740f1f1b98"
|
||||
},
|
||||
"supercollider": {
|
||||
"revision": "90c6d9f777d2b8c4ce497c48b5f270a44bcf3ea0"
|
||||
},
|
||||
"surface": {
|
||||
"revision": "f4586b35ac8548667a9aaa4eae44456c1f43d032"
|
||||
},
|
||||
"svelte": {
|
||||
"revision": "52e122ae68b316d3aa960a0a422d3645ba717f42"
|
||||
},
|
||||
"swift": {
|
||||
"revision": "0fe0de56b528cbf24a654c734ca181b48be3831d"
|
||||
},
|
||||
"sxhkdrc": {
|
||||
"revision": "440d5f913d9465c9c776a1bd92334d32febcf065"
|
||||
},
|
||||
"t32": {
|
||||
"revision": "f8106fcf5a27f905b3d9d55d9cd3e910bea70c60"
|
||||
},
|
||||
"teal": {
|
||||
"revision": "2158ecce11ea542f9b791baf2c7fb33798174ed2"
|
||||
},
|
||||
"terraform": {
|
||||
"revision": "0ff887f2a60a147452d52db060de6b42f42f1441"
|
||||
},
|
||||
"thrift": {
|
||||
"revision": "763ae3d760b7a7719b57568bdf9ffae2d896680f"
|
||||
},
|
||||
"tiger": {
|
||||
"revision": "a233ebe360a73a92c50978e5c4e9e471bc59ff42"
|
||||
},
|
||||
"tlaplus": {
|
||||
"revision": "6d2ec894aef843fc89312c904e20c5f555aec4e3"
|
||||
},
|
||||
"todotxt": {
|
||||
"revision": "0207f6a4ab6aeafc4b091914d31d8235049a2578"
|
||||
},
|
||||
"toml": {
|
||||
"revision": "8bd2056818b21860e3d756b5a58c4f6e05fb744e"
|
||||
},
|
||||
"tsx": {
|
||||
"revision": "5d20856f34315b068c41edaee2ac8a100081d259"
|
||||
},
|
||||
"turtle": {
|
||||
"revision": "085437f5cb117703b7f520dd92161140a684f092"
|
||||
},
|
||||
"twig": {
|
||||
"revision": "2457993b13a06dec2706e6a6c3d5b65bb23024b8"
|
||||
},
|
||||
"typescript": {
|
||||
"revision": "5d20856f34315b068c41edaee2ac8a100081d259"
|
||||
},
|
||||
"v": {
|
||||
"revision": "136f3a0ad91ab8a781c2d4eb419df0a981839f69"
|
||||
},
|
||||
"vala": {
|
||||
"revision": "8f690bfa639f2b83d1fb938ed3dd98a7ba453e8b"
|
||||
},
|
||||
"verilog": {
|
||||
"revision": "4457145e795b363f072463e697dfe2f6973c9a52"
|
||||
},
|
||||
"vhs": {
|
||||
"revision": "8a0df32b72a8cf8d3e3e84f16c19e9ba46d3dba5"
|
||||
},
|
||||
"vim": {
|
||||
"revision": "e39a7bbcfdcfc7900629962b785c7e14503ae590"
|
||||
},
|
||||
"vue": {
|
||||
"revision": "91fe2754796cd8fba5f229505a23fa08f3546c06"
|
||||
},
|
||||
"wgsl": {
|
||||
"revision": "40259f3c77ea856841a4e0c4c807705f3e4a2b65"
|
||||
},
|
||||
"wgsl_bevy": {
|
||||
"revision": "7cd38d6895060b023353e04f7af099ec64add5d1"
|
||||
},
|
||||
"yaml": {
|
||||
"revision": "0e36bed171768908f331ff7dff9d956bae016efb"
|
||||
},
|
||||
"yang": {
|
||||
"revision": "2c0e6be8dd4dcb961c345fa35c309ad4f5bd3502"
|
||||
},
|
||||
"zig": {
|
||||
"revision": "6b3f5788f38be900b45f5af5a753bf6a37d614b8"
|
||||
}
|
||||
}
|
||||
22
lua/nvim-treesitter.lua
Normal file
22
lua/nvim-treesitter.lua
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
local install = require "nvim-treesitter.install"
|
||||
local utils = require "nvim-treesitter.utils"
|
||||
local info = require "nvim-treesitter.info"
|
||||
local configs = require "nvim-treesitter.configs"
|
||||
local statusline = require "nvim-treesitter.statusline"
|
||||
|
||||
-- Registers all query predicates
|
||||
require "nvim-treesitter.query_predicates"
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.setup()
|
||||
utils.setup_commands("install", install.commands)
|
||||
utils.setup_commands("info", info.commands)
|
||||
utils.setup_commands("configs", configs.commands)
|
||||
configs.init()
|
||||
end
|
||||
|
||||
M.define_modules = configs.define_modules
|
||||
M.statusline = statusline.statusline
|
||||
|
||||
return M
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
---@meta
|
||||
error('Cannot require a meta file')
|
||||
|
||||
---@class InstallInfo
|
||||
---
|
||||
---URL of parser repo (Github)
|
||||
---@field url string
|
||||
---
|
||||
---Commit hash of parser to download (compatible with queries)
|
||||
---@field revision string
|
||||
---
|
||||
---Branch of parser repo to download (if not default branch)
|
||||
---@field branch? string
|
||||
---
|
||||
---Location of `grammar.js` in repo (if not at root, e.g., in a monorepo)
|
||||
---@field location? string
|
||||
---
|
||||
---Repo does not contain a `parser.c`; must be generated from grammar first
|
||||
---@field generate? boolean
|
||||
---
|
||||
---Generate parser from `grammar.json` instead of `grammar.js` (default true)
|
||||
---@field generate_from_json? boolean
|
||||
---
|
||||
---Parser repo is a local directory; overrides `url`, `revision`, and `branch`
|
||||
---@field path? string
|
||||
---
|
||||
---Directory with queries to be installed
|
||||
---@field queries? string
|
||||
|
||||
---@class ParserInfo
|
||||
---
|
||||
---Information necessary to build and install the parser (empty for query-only language)
|
||||
---@field install_info? InstallInfo
|
||||
---
|
||||
---List of Github users maintaining the queries for Neovim
|
||||
---@field maintainers? string[]
|
||||
---
|
||||
---List of other languages to install (e.g., if queries inherit from them)
|
||||
---@field requires? string[]
|
||||
---
|
||||
---Language support tier, maps to "stable", "unstable", "unmaintained", "unsupported"
|
||||
---@field tier integer
|
||||
---
|
||||
---Explanatory footnote text to add in SUPPORTED_LANGUAGES.md
|
||||
---@field readme_note? string
|
||||
|
||||
---@alias nvim-ts.parsers table<string,ParserInfo>
|
||||
|
|
@ -1,736 +0,0 @@
|
|||
---@meta nvim-treesitter.async vendored file, don't diagnose
|
||||
local pcall = copcall or pcall
|
||||
|
||||
--- @param ... any
|
||||
--- @return {[integer]: any, n: integer}
|
||||
local function pack_len(...)
|
||||
return { n = select('#', ...), ... }
|
||||
end
|
||||
|
||||
--- like unpack() but use the length set by F.pack_len if present
|
||||
--- @param t? { [integer]: any, n?: integer }
|
||||
--- @param first? integer
|
||||
--- @return ...any
|
||||
local function unpack_len(t, first)
|
||||
if t then
|
||||
return unpack(t, first or 1, t.n or table.maxn(t))
|
||||
end
|
||||
end
|
||||
|
||||
--- @class async
|
||||
local M = {}
|
||||
|
||||
--- Weak table to keep track of running tasks
|
||||
--- @type table<thread,async.Task?>
|
||||
local threads = setmetatable({}, { __mode = 'k' })
|
||||
|
||||
--- @return async.Task?
|
||||
local function running()
|
||||
local task = threads[coroutine.running()]
|
||||
if task and not (task:_completed() or task._closing) then
|
||||
return task
|
||||
end
|
||||
end
|
||||
|
||||
--- Base class for async tasks. Async functions should return a subclass of
|
||||
--- this. This is designed specifically to be a base class of uv_handle_t
|
||||
--- @class async.Handle
|
||||
--- @field close fun(self: async.Handle, callback?: fun())
|
||||
--- @field is_closing? fun(self: async.Handle): boolean
|
||||
|
||||
--- @alias async.CallbackFn fun(...: any): async.Handle?
|
||||
|
||||
--- @class async.Task : async.Handle
|
||||
--- @field package _callbacks table<integer,fun(err?: any, ...: any)>
|
||||
--- @field package _callback_pos integer
|
||||
--- @field private _thread thread
|
||||
---
|
||||
--- Tasks can call other async functions (task of callback functions)
|
||||
--- when we are waiting on a child, we store the handle to it here so we can
|
||||
--- cancel it.
|
||||
--- @field private _current_child? async.Handle
|
||||
---
|
||||
--- Error result of the task is an error occurs.
|
||||
--- Must use `await` to get the result.
|
||||
--- @field private _err? any
|
||||
---
|
||||
--- Result of the task.
|
||||
--- Must use `await` to get the result.
|
||||
--- @field private _result? any[]
|
||||
local Task = {}
|
||||
Task.__index = Task
|
||||
|
||||
--- @private
|
||||
--- @param func function
|
||||
--- @return async.Task
|
||||
function Task._new(func)
|
||||
local thread = coroutine.create(func)
|
||||
|
||||
local self = setmetatable({
|
||||
_closing = false,
|
||||
_thread = thread,
|
||||
_callbacks = {},
|
||||
_callback_pos = 1,
|
||||
}, Task)
|
||||
|
||||
threads[thread] = self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param callback fun(err?: any, ...: any)
|
||||
function Task:await(callback)
|
||||
if self._closing then
|
||||
callback('closing')
|
||||
elseif self:_completed() then -- TODO(lewis6991): test
|
||||
-- Already finished or closed
|
||||
callback(self._err, unpack_len(self._result))
|
||||
else
|
||||
self._callbacks[self._callback_pos] = callback
|
||||
self._callback_pos = self._callback_pos + 1
|
||||
end
|
||||
end
|
||||
|
||||
--- @package
|
||||
function Task:_completed()
|
||||
return (self._err or self._result) ~= nil
|
||||
end
|
||||
|
||||
-- Use max 32-bit signed int value to avoid overflow on 32-bit systems.
|
||||
-- Do not use `math.huge` as it is not interpreted as a positive integer on all
|
||||
-- platforms.
|
||||
local MAX_TIMEOUT = 2 ^ 31 - 1
|
||||
|
||||
--- Synchronously wait (protected) for a task to finish (blocking)
|
||||
---
|
||||
--- If an error is returned, `Task:traceback()` can be used to get the
|
||||
--- stack trace of the error.
|
||||
---
|
||||
--- Example:
|
||||
--- ```lua
|
||||
---
|
||||
--- local ok, err_or_result = task:pwait(10)
|
||||
---
|
||||
--- if not ok then
|
||||
--- error(task:traceback(err_or_result))
|
||||
--- end
|
||||
---
|
||||
--- local _, result = assert(task:pwait(10))
|
||||
--- ```
|
||||
---
|
||||
--- Can be called if a task is closing.
|
||||
--- @param timeout? integer
|
||||
--- @return boolean status
|
||||
--- @return any ... result or error
|
||||
function Task:pwait(timeout)
|
||||
local done = vim.wait(timeout or MAX_TIMEOUT, function()
|
||||
-- Note we use self:_completed() instead of self:await() to avoid creating a
|
||||
-- callback. This avoids having to cleanup/unregister any callback in the
|
||||
-- case of a timeout.
|
||||
return self:_completed()
|
||||
end)
|
||||
|
||||
if not done then
|
||||
return false, 'timeout'
|
||||
elseif self._err then
|
||||
return false, self._err
|
||||
else
|
||||
return true, unpack_len(self._result)
|
||||
end
|
||||
end
|
||||
|
||||
--- Synchronously wait for a task to finish (blocking)
|
||||
---
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- local result = task:wait(10) -- wait for 10ms or else error
|
||||
---
|
||||
--- local result = task:wait() -- wait indefinitely
|
||||
--- ```
|
||||
--- @param timeout? integer Timeout in milliseconds
|
||||
--- @return any ... result
|
||||
function Task:wait(timeout)
|
||||
local res = pack_len(self:pwait(timeout))
|
||||
local stat = res[1]
|
||||
|
||||
if not stat then
|
||||
error(self:traceback(res[2]))
|
||||
end
|
||||
|
||||
return unpack_len(res, 2)
|
||||
end
|
||||
|
||||
--- @private
|
||||
--- @param msg? string
|
||||
--- @param _lvl? integer
|
||||
--- @return string
|
||||
function Task:_traceback(msg, _lvl)
|
||||
_lvl = _lvl or 0
|
||||
|
||||
local thread = ('[%s] '):format(self._thread)
|
||||
|
||||
local child = self._current_child
|
||||
if getmetatable(child) == Task then
|
||||
--- @cast child async.Task
|
||||
msg = child:_traceback(msg, _lvl + 1)
|
||||
end
|
||||
|
||||
local tblvl = getmetatable(child) == Task and 2 or nil
|
||||
msg = (msg or '') .. debug.traceback(self._thread, '', tblvl):gsub('\n\t', '\n\t' .. thread)
|
||||
|
||||
if _lvl == 0 then
|
||||
--- @type string
|
||||
msg = msg
|
||||
:gsub('\nstack traceback:\n', '\nSTACK TRACEBACK:\n', 1)
|
||||
:gsub('\nstack traceback:\n', '\n')
|
||||
:gsub('\nSTACK TRACEBACK:\n', '\nstack traceback:\n', 1)
|
||||
end
|
||||
|
||||
return msg
|
||||
end
|
||||
|
||||
--- Get the traceback of a task when it is not active.
|
||||
--- Will also get the traceback of nested tasks.
|
||||
---
|
||||
--- @param msg? string
|
||||
--- @return string
|
||||
function Task:traceback(msg)
|
||||
return self:_traceback(msg)
|
||||
end
|
||||
|
||||
--- If a task completes with an error, raise the error
|
||||
function Task:raise_on_error()
|
||||
self:await(function(err)
|
||||
if err then
|
||||
error(self:_traceback(err), 0)
|
||||
end
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
--- @private
|
||||
--- @param err? any
|
||||
--- @param result? {[integer]: any, n: integer}
|
||||
function Task:_finish(err, result)
|
||||
self._current_child = nil
|
||||
self._err = err
|
||||
self._result = result
|
||||
threads[self._thread] = nil
|
||||
|
||||
local errs = {} --- @type string[]
|
||||
for _, cb in pairs(self._callbacks) do
|
||||
--- @type boolean, string
|
||||
local ok, cb_err = pcall(cb, err, unpack_len(result))
|
||||
if not ok then
|
||||
errs[#errs + 1] = cb_err
|
||||
end
|
||||
end
|
||||
|
||||
if #errs > 0 then
|
||||
error(table.concat(errs, '\n'), 0)
|
||||
end
|
||||
end
|
||||
|
||||
--- @return boolean
|
||||
function Task:is_closing()
|
||||
return self._closing
|
||||
end
|
||||
|
||||
--- Close the task and all its children.
|
||||
--- If callback is provided it will run asynchronously,
|
||||
--- else it will run synchronously.
|
||||
---
|
||||
--- @param callback? fun()
|
||||
function Task:close(callback)
|
||||
if self:_completed() then
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self._closing then
|
||||
return
|
||||
end
|
||||
|
||||
self._closing = true
|
||||
|
||||
if callback then -- async
|
||||
if self._current_child then
|
||||
self._current_child:close(function()
|
||||
self:_finish('closed')
|
||||
callback()
|
||||
end)
|
||||
else
|
||||
self:_finish('closed')
|
||||
callback()
|
||||
end
|
||||
else -- sync
|
||||
if self._current_child then
|
||||
self._current_child:close(function()
|
||||
self:_finish('closed')
|
||||
end)
|
||||
else
|
||||
self:_finish('closed')
|
||||
end
|
||||
vim.wait(0, function()
|
||||
return self:_completed()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param obj any
|
||||
--- @return boolean
|
||||
local function is_async_handle(obj)
|
||||
local ty = type(obj)
|
||||
return (ty == 'table' or ty == 'userdata') and vim.is_callable(obj.close)
|
||||
end
|
||||
|
||||
--- @param ... any
|
||||
function Task:_resume(...)
|
||||
--- @type [boolean, string|async.CallbackFn]
|
||||
local ret = pack_len(coroutine.resume(self._thread, ...))
|
||||
local stat = ret[1]
|
||||
|
||||
if not stat then
|
||||
-- Coroutine had error
|
||||
self:_finish(ret[2])
|
||||
elseif coroutine.status(self._thread) == 'dead' then
|
||||
-- Coroutine finished
|
||||
local result = pack_len(unpack_len(ret, 2))
|
||||
self:_finish(nil, result)
|
||||
else
|
||||
local fn = ret[2]
|
||||
--- @cast fn -string
|
||||
|
||||
-- TODO(lewis6991): refine error handler to be more specific
|
||||
local ok, r
|
||||
ok, r = pcall(fn, function(...)
|
||||
if is_async_handle(r) then
|
||||
--- @cast r async.Handle
|
||||
-- We must close children before we resume to ensure
|
||||
-- all resources are collected.
|
||||
local args = pack_len(...)
|
||||
r:close(function()
|
||||
self:_resume(unpack_len(args))
|
||||
end)
|
||||
else
|
||||
self:_resume(...)
|
||||
end
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
self:_finish(r)
|
||||
elseif is_async_handle(r) then
|
||||
self._current_child = r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @return 'running'|'suspended'|'normal'|'dead'?
|
||||
function Task:status()
|
||||
return coroutine.status(self._thread)
|
||||
end
|
||||
|
||||
--- Run a function in an async context, asynchronously.
|
||||
---
|
||||
--- Examples:
|
||||
--- ```lua
|
||||
--- -- The two below blocks are equivalent:
|
||||
---
|
||||
--- -- Run a uv function and wait for it
|
||||
--- local stat = async.arun(function()
|
||||
--- return async.await(2, vim.uv.fs_stat, 'foo.txt')
|
||||
--- end):wait()
|
||||
---
|
||||
--- -- Since uv functions have sync versions. You can just do:
|
||||
--- local stat = vim.fs_stat('foo.txt')
|
||||
--- ```
|
||||
--- @generic T, R
|
||||
--- @param func async fun(...: T...): R...
|
||||
--- @param ... T...
|
||||
--- @return async.Task
|
||||
function M.arun(func, ...)
|
||||
local task = Task._new(func)
|
||||
task:_resume(...)
|
||||
return task
|
||||
end
|
||||
|
||||
--- @alias async.TaskFun<T, R> fun(...: T...): async.Task
|
||||
|
||||
--- @generic T, R
|
||||
--- @class async._TaskFun<T, R>
|
||||
--- @field package _fun async fun(...: T...): R...
|
||||
--- @operator call(...: T...): async.Task
|
||||
local TaskFun = {}
|
||||
TaskFun.__index = TaskFun
|
||||
|
||||
--- @generic T, R
|
||||
--- @param self async._TaskFun<T, R>
|
||||
--- @param ... T...
|
||||
--- @return async.Task
|
||||
function TaskFun:__call(...)
|
||||
return M.arun(self._fun, ...)
|
||||
end
|
||||
|
||||
--- Create an async function
|
||||
--- @generic T, R
|
||||
--- @param fun async fun(...: T...): R...
|
||||
--- @return async.TaskFun<T, R>
|
||||
function M.async(fun)
|
||||
return setmetatable({ _fun = fun }, TaskFun)
|
||||
end
|
||||
|
||||
--- Returns the status of a task’s thread.
|
||||
---
|
||||
--- @param task? async.Task
|
||||
--- @return 'running'|'suspended'|'normal'|'dead'?
|
||||
function M.status(task)
|
||||
task = task or running()
|
||||
if task then
|
||||
assert(getmetatable(task) == Task, 'Expected Task')
|
||||
return task:status()
|
||||
end
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- @generic R1, R2, R3, R4
|
||||
--- @param fun fun(callback: fun(r1: R1, r2: R2, r3: R3, r4: R4)): any?
|
||||
--- @return R1, R2, R3, R4
|
||||
local function yield(fun)
|
||||
assert(type(fun) == 'function', 'Expected function')
|
||||
return coroutine.yield(fun)
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- @param task async.Task
|
||||
--- @return any ...
|
||||
local function await_task(task)
|
||||
--- @param callback fun(err?: string, ...: any)
|
||||
--- @return function
|
||||
local res = pack_len(yield(function(callback)
|
||||
task:await(callback)
|
||||
return task
|
||||
end))
|
||||
|
||||
local err = res[1]
|
||||
|
||||
if err then
|
||||
-- TODO(lewis6991): what is the correct level to pass?
|
||||
error(err, 0)
|
||||
end
|
||||
|
||||
return unpack_len(res, 2)
|
||||
end
|
||||
|
||||
--- Asynchronous blocking wait
|
||||
--- @param argc integer
|
||||
--- @param fun async.CallbackFn
|
||||
--- @param ... any func arguments
|
||||
--- @return any ...
|
||||
local function await_cbfun(argc, fun, ...)
|
||||
local args = pack_len(...)
|
||||
|
||||
--- @param callback fun(...:any)
|
||||
--- @return any?
|
||||
return yield(function(callback)
|
||||
args[argc] = callback
|
||||
args.n = math.max(args.n, argc)
|
||||
return fun(unpack_len(args))
|
||||
end)
|
||||
end
|
||||
|
||||
--- @generic T, R
|
||||
--- @param taskfun async.TaskFun<T, R>
|
||||
--- @param ... T...
|
||||
--- @return R...
|
||||
local function await_taskfun(taskfun, ...)
|
||||
return taskfun._fun(...)
|
||||
end
|
||||
|
||||
--- Asynchronous blocking wait
|
||||
---
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- local task = async.arun(function()
|
||||
--- return 1, 'a'
|
||||
--- end)
|
||||
---
|
||||
--- local task_fun = async.async(function(arg)
|
||||
--- return 2, 'b', arg
|
||||
--- end)
|
||||
---
|
||||
--- async.arun(function()
|
||||
--- do -- await a callback function
|
||||
--- async.await(1, vim.schedule)
|
||||
--- end
|
||||
---
|
||||
--- do -- await a task (new async context)
|
||||
--- local n, s = async.await(task)
|
||||
--- assert(n == 1 and s == 'a')
|
||||
--- end
|
||||
---
|
||||
--- do -- await a started task function (new async context)
|
||||
--- local n, s, arg = async.await(task_fun('A'))
|
||||
--- assert(n == 2)
|
||||
--- assert(s == 'b')
|
||||
--- assert(args == 'A')
|
||||
--- end
|
||||
---
|
||||
--- do -- await a task function (re-using the current async context)
|
||||
--- local n, s, arg = async.await(task_fun, 'B')
|
||||
--- assert(n == 2)
|
||||
--- assert(s == 'b')
|
||||
--- assert(args == 'B')
|
||||
--- end
|
||||
--- end)
|
||||
--- ```
|
||||
--- @async
|
||||
--- @overload fun(argc: integer, func: async.CallbackFn, ...:any): any ...
|
||||
--- @overload fun(task: async.Task): any ...
|
||||
--- @overload fun(taskfun: async.TaskFun): any ...
|
||||
function M.await(...)
|
||||
assert(running(), 'Not in async context')
|
||||
|
||||
local arg1 = select(1, ...)
|
||||
|
||||
if type(arg1) == 'number' then
|
||||
return await_cbfun(...)
|
||||
elseif getmetatable(arg1) == Task then
|
||||
return await_task(...)
|
||||
elseif getmetatable(arg1) == TaskFun then
|
||||
return await_taskfun(...)
|
||||
end
|
||||
|
||||
error('Invalid arguments, expected Task or (argc, func) got: ' .. type(arg1), 2)
|
||||
end
|
||||
|
||||
--- Creates an async function with a callback style function.
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- --- Note the callback argument is not present in the return function
|
||||
--- --- @type fun(timeout: integer)
|
||||
--- local sleep = async.awrap(2, function(timeout, callback)
|
||||
--- local timer = vim.uv.new_timer()
|
||||
--- timer:start(timeout * 1000, 0, callback)
|
||||
--- -- uv_timer_t provides a close method so timer will be
|
||||
--- -- cleaned up when this function finishes
|
||||
--- return timer
|
||||
--- end)
|
||||
---
|
||||
--- async.arun(function()
|
||||
--- print('hello')
|
||||
--- sleep(2)
|
||||
--- print('world')
|
||||
--- end)
|
||||
--- ```
|
||||
---
|
||||
--- local atimer = async.awrap(
|
||||
--- @param argc integer
|
||||
--- @param func async.CallbackFn
|
||||
--- @return async function
|
||||
function M.awrap(argc, func)
|
||||
assert(type(argc) == 'number')
|
||||
assert(type(func) == 'function')
|
||||
--- @async
|
||||
return function(...)
|
||||
return M.await(argc, func, ...)
|
||||
end
|
||||
end
|
||||
|
||||
if vim.schedule then
|
||||
--- An async function that when called will yield to the Neovim scheduler to be
|
||||
--- able to call the API.
|
||||
M.schedule = M.awrap(1, vim.schedule)
|
||||
end
|
||||
|
||||
--- Create a function that runs a function when it is garbage collected.
|
||||
--- @generic F
|
||||
--- @param f F
|
||||
--- @param gc fun()
|
||||
--- @return F
|
||||
local function gc_fun(f, gc)
|
||||
local proxy = newproxy(true)
|
||||
local proxy_mt = getmetatable(proxy)
|
||||
proxy_mt.__gc = gc
|
||||
proxy_mt.__call = function(_, ...)
|
||||
return f(...)
|
||||
end
|
||||
|
||||
return proxy
|
||||
end
|
||||
|
||||
--- @param task_cbs table<async.Task,function>
|
||||
local function gc_cbs(task_cbs)
|
||||
for task, tcb in pairs(task_cbs) do
|
||||
for j, cb in pairs(task._callbacks) do
|
||||
if cb == tcb then
|
||||
task._callbacks[j] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- local task1 = async.arun(function()
|
||||
--- return 1, 'a'
|
||||
--- end)
|
||||
---
|
||||
--- local task2 = async.arun(function()
|
||||
--- return 1, 'a'
|
||||
--- end)
|
||||
---
|
||||
--- local task3 = async.arun(function()
|
||||
--- error('task3 error')
|
||||
--- end)
|
||||
---
|
||||
--- async.arun(function()
|
||||
--- for i, err, r1, r2 in async.iter({task1, task2, task3})
|
||||
--- print(i, err, r1, r2)
|
||||
--- end
|
||||
--- end)
|
||||
--- ```
|
||||
---
|
||||
--- Prints:
|
||||
--- ```
|
||||
--- 1 nil 1 'a'
|
||||
--- 2 nil 2 'b'
|
||||
--- 3 'task3 error' nil nil
|
||||
--- ```
|
||||
---
|
||||
--- @param tasks async.Task[]
|
||||
--- @return fun(): (integer?, any?, ...)
|
||||
function M.iter(tasks)
|
||||
assert(running(), 'Not in async context')
|
||||
|
||||
local results = {} --- @type [integer, any, ...][]
|
||||
|
||||
-- Iter blocks in an async context so only one waiter is needed
|
||||
local waiter = nil
|
||||
local task_cbs = {} --- @type table<async.Task,function>
|
||||
local remaining = #tasks
|
||||
|
||||
--- If can_gc_cbs is true, then the iterator function has been garbage
|
||||
--- collected and means any awaiters can also be garbage collected. The
|
||||
--- only time we can't do this is if with the special case when iter() is
|
||||
--- called anonymously (`local i = async.iter(tasks)()`), so we should not
|
||||
--- garbage collect the callbacks until at least one awaiter is called.
|
||||
local can_gc_cbs = false
|
||||
|
||||
for i, task in ipairs(tasks) do
|
||||
local function cb(err, ...)
|
||||
if can_gc_cbs == true then
|
||||
gc_cbs(task_cbs)
|
||||
end
|
||||
|
||||
local callback = waiter
|
||||
|
||||
-- Clear waiter before calling it
|
||||
waiter = nil
|
||||
|
||||
remaining = remaining - 1
|
||||
if callback then
|
||||
-- Iterator is waiting, yield to it
|
||||
callback(i, err, ...)
|
||||
else
|
||||
-- Task finished before Iterator was called. Store results.
|
||||
table.insert(results, pack_len(i, err, ...))
|
||||
end
|
||||
end
|
||||
|
||||
task_cbs[task] = cb
|
||||
task:await(cb)
|
||||
end
|
||||
|
||||
return gc_fun(
|
||||
M.awrap(1, function(callback)
|
||||
if next(results) then
|
||||
local res = table.remove(results, 1)
|
||||
callback(unpack_len(res))
|
||||
elseif remaining == 0 then
|
||||
callback() -- finish
|
||||
else
|
||||
assert(not waiter, 'internal error: waiter already set')
|
||||
waiter = callback
|
||||
end
|
||||
end),
|
||||
function()
|
||||
-- Don't gc callbacks just yet. Wait until at least one of them is called.
|
||||
can_gc_cbs = true
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
do -- join()
|
||||
--- @param results table<integer,table>
|
||||
--- @param i integer
|
||||
--- @param ... any
|
||||
--- @return boolean
|
||||
local function collect(results, i, ...)
|
||||
if i then
|
||||
results[i] = pack_len(...)
|
||||
end
|
||||
return i ~= nil
|
||||
end
|
||||
|
||||
--- @param iter fun(): ...
|
||||
--- @return table<integer,table>
|
||||
local function drain_iter(iter)
|
||||
local results = {} --- @type table<integer,table>
|
||||
while collect(results, iter()) do
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- Wait for all tasks to finish and return their results.
|
||||
---
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- local task1 = async.arun(function()
|
||||
--- return 1, 'a'
|
||||
--- end)
|
||||
---
|
||||
--- local task2 = async.arun(function()
|
||||
--- return 1, 'a'
|
||||
--- end)
|
||||
---
|
||||
--- local task3 = async.arun(function()
|
||||
--- error('task3 error')
|
||||
--- end)
|
||||
---
|
||||
--- async.arun(function()
|
||||
--- local results = async.join({task1, task2, task3})
|
||||
--- print(vim.inspect(results))
|
||||
--- end)
|
||||
--- ```
|
||||
---
|
||||
--- Prints:
|
||||
--- ```
|
||||
--- {
|
||||
--- [1] = { nil, 1, 'a' },
|
||||
--- [2] = { nil, 2, 'b' },
|
||||
--- [3] = { 'task2 error' },
|
||||
--- }
|
||||
--- ```
|
||||
--- @param tasks async.Task[]
|
||||
--- @return table<integer,[any?,...?]>
|
||||
function M.join(tasks)
|
||||
assert(running(), 'Not in async context')
|
||||
return drain_iter(M.iter(tasks))
|
||||
end
|
||||
|
||||
--- @async
|
||||
--- @param tasks async.Task[]
|
||||
--- @return integer?, any?, ...?
|
||||
function M.joinany(tasks)
|
||||
return M.iter(tasks)()
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
56
lua/nvim-treesitter/caching.lua
Normal file
56
lua/nvim-treesitter/caching.lua
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
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,
|
||||
})
|
||||
local loaded_buffers = {}
|
||||
|
||||
function cache.set(type_name, bufnr, value)
|
||||
if not loaded_buffers[bufnr] then
|
||||
loaded_buffers[bufnr] = true
|
||||
-- Clean up the cache if the buffer is detached
|
||||
-- to avoid memory leaks
|
||||
api.nvim_buf_attach(bufnr, false, {
|
||||
on_detach = function()
|
||||
cache.clear_buffer(bufnr)
|
||||
loaded_buffers[tostring(bufnr)] = nil
|
||||
return true
|
||||
end,
|
||||
on_reload = function() end, -- this is needed to prevent on_detach being called on buffer reload
|
||||
})
|
||||
end
|
||||
|
||||
items[tostring(bufnr)][type_name] = value
|
||||
end
|
||||
|
||||
function cache.get(type_name, bufnr)
|
||||
return items[tostring(bufnr)][type_name]
|
||||
end
|
||||
|
||||
function cache.has(type_name, bufnr)
|
||||
return cache.get(type_name, bufnr) ~= nil
|
||||
end
|
||||
|
||||
function cache.remove(type_name, bufnr)
|
||||
items[tostring(bufnr)][type_name] = nil
|
||||
end
|
||||
|
||||
function cache.clear_buffer(bufnr)
|
||||
items[tostring(bufnr)] = nil
|
||||
end
|
||||
|
||||
return cache
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
M.tiers = { 'stable', 'unstable', 'unmaintained', 'unsupported' }
|
||||
|
||||
---@class TSConfig
|
||||
---@field install_dir string
|
||||
|
||||
---@type TSConfig
|
||||
local config = {
|
||||
install_dir = vim.fs.joinpath(vim.fn.stdpath('data') --[[@as string]], 'site'),
|
||||
}
|
||||
|
||||
---Setup call for users to override configuration configurations.
|
||||
---@param user_data TSConfig? user configuration table
|
||||
function M.setup(user_data)
|
||||
if user_data then
|
||||
if user_data.install_dir then
|
||||
user_data.install_dir = vim.fs.normalize(user_data.install_dir)
|
||||
vim.o.rtp = user_data.install_dir .. ',' .. vim.o.rtp
|
||||
end
|
||||
config = vim.tbl_deep_extend('force', config, user_data)
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns the install path for parsers, parser info, and queries.
|
||||
-- If the specified directory does not exist, it is created.
|
||||
---@param dir_name string
|
||||
---@return string
|
||||
function M.get_install_dir(dir_name)
|
||||
local dir = vim.fs.joinpath(config.install_dir, dir_name)
|
||||
|
||||
if not vim.uv.fs_stat(dir) then
|
||||
local ok, err = pcall(vim.fn.mkdir, dir, 'p', '0755')
|
||||
if not ok then
|
||||
local log = require('nvim-treesitter.log')
|
||||
log.error(err --[[@as string]])
|
||||
end
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
---@param type 'queries'|'parsers'?
|
||||
---@return string[]
|
||||
function M.get_installed(type)
|
||||
local installed = {} --- @type table<string, boolean>
|
||||
if not (type and type == 'parsers') then
|
||||
for f in vim.fs.dir(M.get_install_dir('queries')) do
|
||||
installed[f] = true
|
||||
end
|
||||
end
|
||||
if not (type and type == 'queries') then
|
||||
for f in vim.fs.dir(M.get_install_dir('parser')) do
|
||||
installed[vim.fn.fnamemodify(f, ':r')] = true
|
||||
end
|
||||
end
|
||||
return vim.tbl_keys(installed)
|
||||
end
|
||||
|
||||
-- Get a list of all available parsers
|
||||
---@param tier integer? only get parsers of specified tier
|
||||
---@return string[]
|
||||
function M.get_available(tier)
|
||||
vim.api.nvim_exec_autocmds('User', { pattern = 'TSUpdate' })
|
||||
local parsers = require('nvim-treesitter.parsers')
|
||||
--- @type string[]
|
||||
local languages = vim.tbl_keys(parsers)
|
||||
table.sort(languages)
|
||||
if tier then
|
||||
languages = vim.tbl_filter(
|
||||
--- @param p string
|
||||
function(p)
|
||||
return parsers[p] ~= nil and parsers[p].tier == tier
|
||||
end,
|
||||
languages
|
||||
)
|
||||
end
|
||||
return languages
|
||||
end
|
||||
|
||||
local function expand_tiers(list)
|
||||
for i, tier in ipairs(M.tiers) do
|
||||
if vim.list_contains(list, tier) then
|
||||
list = vim.tbl_filter(
|
||||
--- @param l string
|
||||
function(l)
|
||||
return l ~= tier
|
||||
end,
|
||||
list
|
||||
)
|
||||
vim.list_extend(list, M.get_available(i))
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
---Normalize languages
|
||||
---@param languages? string[]|string
|
||||
---@param skip? { missing: boolean?, unsupported: boolean?, installed: boolean?, dependencies: boolean? }
|
||||
---@return string[]
|
||||
function M.norm_languages(languages, skip)
|
||||
if not languages then
|
||||
return {}
|
||||
elseif type(languages) == 'string' then
|
||||
languages = { languages }
|
||||
end
|
||||
|
||||
if vim.list_contains(languages, 'all') then
|
||||
if skip and skip.missing then
|
||||
return M.get_installed()
|
||||
end
|
||||
languages = M.get_available()
|
||||
end
|
||||
|
||||
languages = expand_tiers(languages)
|
||||
|
||||
if skip and skip.installed then
|
||||
local installed = M.get_installed()
|
||||
languages = vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
return not vim.list_contains(installed, v)
|
||||
end,
|
||||
languages
|
||||
)
|
||||
end
|
||||
|
||||
if skip and skip.missing then
|
||||
local installed = M.get_installed()
|
||||
languages = vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
return vim.list_contains(installed, v)
|
||||
end,
|
||||
languages
|
||||
)
|
||||
end
|
||||
|
||||
local parsers = require('nvim-treesitter.parsers')
|
||||
languages = vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
if parsers[v] ~= nil then
|
||||
return true
|
||||
else
|
||||
require('nvim-treesitter.log').warn('skipping unsupported language: ' .. v)
|
||||
return false
|
||||
end
|
||||
end,
|
||||
languages
|
||||
)
|
||||
|
||||
if skip and skip.unsupported then
|
||||
languages = vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
return not (parsers[v] and parsers[v].tier and parsers[v].tier == 4)
|
||||
end,
|
||||
languages
|
||||
)
|
||||
end
|
||||
|
||||
if not (skip and skip.dependencies) then
|
||||
for _, lang in pairs(languages) do
|
||||
if parsers[lang] and parsers[lang].requires then
|
||||
vim.list_extend(languages, parsers[lang].requires)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return vim.list.unique(languages)
|
||||
end
|
||||
|
||||
return M
|
||||
617
lua/nvim-treesitter/configs.lua
Normal file
617
lua/nvim-treesitter/configs.lua
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
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 = {}
|
||||
|
||||
---@class TSConfig
|
||||
---@field modules {[string]:TSModule}
|
||||
---@field sync_install boolean
|
||||
---@field ensure_installed string[]|string
|
||||
---@field ignore_install string[]
|
||||
---@field auto_install boolean
|
||||
---@field update_strategy string
|
||||
---@field parser_install_dir string|nil
|
||||
|
||||
---@type TSConfig
|
||||
local config = {
|
||||
modules = {},
|
||||
sync_install = false,
|
||||
ensure_installed = {},
|
||||
auto_install = false,
|
||||
ignore_install = {},
|
||||
update_strategy = "lockfile",
|
||||
parser_install_dir = nil,
|
||||
}
|
||||
-- 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
|
||||
|
||||
---@class TSModule
|
||||
---@field module_path string
|
||||
---@field enable boolean|string[]|function(string): boolean
|
||||
---@field disable boolean|string[]|function(string): boolean
|
||||
---@field is_supported function(string): boolean
|
||||
---@field attach function(string)
|
||||
---@field detach function(string)
|
||||
---@field enabled_buffers table<integer, boolean>
|
||||
|
||||
---@type {[string]: TSModule}
|
||||
local builtin_modules = {
|
||||
highlight = {
|
||||
module_path = "nvim-treesitter.highlight",
|
||||
-- @deprecated: use `highlight.set_custom_captures` instead
|
||||
custom_captures = {},
|
||||
enable = false,
|
||||
is_supported = function(lang)
|
||||
return queries.has_highlights(lang)
|
||||
end,
|
||||
additional_vim_regex_highlighting = false,
|
||||
},
|
||||
incremental_selection = {
|
||||
module_path = "nvim-treesitter.incremental_selection",
|
||||
enable = false,
|
||||
keymaps = {
|
||||
init_selection = "gnn", -- set to `false` to disable one of the mappings
|
||||
node_incremental = "grn",
|
||||
scope_incremental = "grc",
|
||||
node_decremental = "grm",
|
||||
},
|
||||
is_supported = function()
|
||||
return true
|
||||
end,
|
||||
},
|
||||
indent = {
|
||||
module_path = "nvim-treesitter.indent",
|
||||
enable = false,
|
||||
is_supported = queries.has_indents,
|
||||
},
|
||||
}
|
||||
|
||||
local attached_buffers_by_module = caching.create_buffer_cache()
|
||||
|
||||
---Resolves a module by requiring the `module_path` or using the module definition.
|
||||
---@param mod_name string
|
||||
---@return TSModule|nil
|
||||
local function resolve_module(mod_name)
|
||||
local config_mod = M.get_module(mod_name)
|
||||
|
||||
if not config_mod then
|
||||
return
|
||||
end
|
||||
|
||||
if type(config_mod.attach) == "function" and type(config_mod.detach) == "function" then
|
||||
return config_mod
|
||||
elseif type(config_mod.module_path) == "string" then
|
||||
return require(config_mod.module_path)
|
||||
end
|
||||
end
|
||||
|
||||
---Enables and attaches the module to a buffer for lang.
|
||||
---@param mod string path to module
|
||||
---@param bufnr integer|nil buffer number, defaults to current buffer
|
||||
---@param lang string|nil language, defaults to current language
|
||||
local function enable_module(mod, bufnr, lang)
|
||||
local module = M.get_module(mod)
|
||||
if not module then
|
||||
return
|
||||
end
|
||||
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
lang = lang or parsers.get_buf_lang(bufnr)
|
||||
|
||||
if not module.enable then
|
||||
if module.enabled_buffers then
|
||||
module.enabled_buffers[bufnr] = true
|
||||
else
|
||||
module.enabled_buffers = { [bufnr] = true }
|
||||
end
|
||||
end
|
||||
|
||||
M.attach_module(mod, bufnr, lang)
|
||||
end
|
||||
|
||||
---Enables autocomands for the module.
|
||||
---After the module is loaded `loaded` will be set to true for the module.
|
||||
---@param mod string path to module
|
||||
local function enable_mod_conf_autocmd(mod)
|
||||
local config_mod = M.get_module(mod)
|
||||
if not config_mod or config_mod.loaded then
|
||||
return
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd("FileType", {
|
||||
group = api.nvim_create_augroup("NvimTreesitter-" .. mod, {}),
|
||||
callback = function(args)
|
||||
require("nvim-treesitter.configs").reattach_module(mod, args.buf)
|
||||
end,
|
||||
desc = "Reattach module",
|
||||
})
|
||||
|
||||
config_mod.loaded = true
|
||||
end
|
||||
|
||||
---Enables the module globally and for all current buffers.
|
||||
---After enabled, `enable` will be set to true for the module.
|
||||
---@param mod string path to module
|
||||
local function enable_all(mod)
|
||||
local config_mod = M.get_module(mod)
|
||||
if not config_mod then
|
||||
return
|
||||
end
|
||||
|
||||
enable_mod_conf_autocmd(mod)
|
||||
config_mod.enable = true
|
||||
config_mod.enabled_buffers = nil
|
||||
|
||||
for _, bufnr in pairs(api.nvim_list_bufs()) do
|
||||
enable_module(mod, bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
---Disables and detaches the module for a buffer.
|
||||
---@param mod string path to module
|
||||
---@param bufnr integer buffer number, defaults to current buffer
|
||||
local function disable_module(mod, bufnr)
|
||||
local module = M.get_module(mod)
|
||||
if not module then
|
||||
return
|
||||
end
|
||||
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
if module.enabled_buffers then
|
||||
module.enabled_buffers[bufnr] = false
|
||||
end
|
||||
M.detach_module(mod, bufnr)
|
||||
end
|
||||
|
||||
---Disables autocomands for the module.
|
||||
---After the module is unloaded `loaded` will be set to false for the module.
|
||||
---@param mod string path to module
|
||||
local function disable_mod_conf_autocmd(mod)
|
||||
local config_mod = M.get_module(mod)
|
||||
if not config_mod or not config_mod.loaded then
|
||||
return
|
||||
end
|
||||
api.nvim_clear_autocmds { event = "FileType", group = "NvimTreesitter-" .. mod }
|
||||
config_mod.loaded = false
|
||||
end
|
||||
|
||||
---Disables the module globally and for all current buffers.
|
||||
---After disabled, `enable` will be set to false for the module.
|
||||
---@param mod string path to module
|
||||
local function disable_all(mod)
|
||||
local config_mod = M.get_module(mod)
|
||||
if not config_mod then
|
||||
return
|
||||
end
|
||||
|
||||
config_mod.enabled_buffers = nil
|
||||
disable_mod_conf_autocmd(mod)
|
||||
config_mod.enable = false
|
||||
|
||||
for _, bufnr in pairs(api.nvim_list_bufs()) do
|
||||
disable_module(mod, bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
---Toggles a module for a buffer
|
||||
---@param mod string path to module
|
||||
---@param bufnr integer buffer number, defaults to current buffer
|
||||
---@param lang string language, defaults to current language
|
||||
local function toggle_module(mod, bufnr, lang)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
lang = lang or parsers.get_buf_lang(bufnr)
|
||||
|
||||
if attached_buffers_by_module.has(mod, bufnr) then
|
||||
disable_module(mod, bufnr)
|
||||
else
|
||||
enable_module(mod, bufnr, lang)
|
||||
end
|
||||
end
|
||||
|
||||
-- Toggles the module globally and for all current buffers.
|
||||
-- @param mod path to module
|
||||
local function toggle_all(mod)
|
||||
local config_mod = M.get_module(mod)
|
||||
if not config_mod then
|
||||
return
|
||||
end
|
||||
|
||||
if config_mod.enable then
|
||||
disable_all(mod)
|
||||
else
|
||||
enable_all(mod)
|
||||
end
|
||||
end
|
||||
|
||||
---Recurses through all modules including submodules
|
||||
---@param accumulator function called for each module
|
||||
---@param root {[string]: TSModule}|nil root configuration table to start at
|
||||
---@param path string|nil prefix path
|
||||
local function recurse_modules(accumulator, root, path)
|
||||
root = root or config.modules
|
||||
|
||||
for name, module in pairs(root) do
|
||||
local new_path = path and (path .. "." .. name) or name
|
||||
|
||||
if M.is_module(module) then
|
||||
accumulator(name, module, new_path, root)
|
||||
elseif type(module) == "table" then
|
||||
recurse_modules(accumulator, module, new_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Shows current configuration of all nvim-treesitter modules
|
||||
---@param process_function function used as the `process` parameter
|
||||
--- for vim.inspect (https://github.com/kikito/inspect.lua#optionsprocess)
|
||||
local function config_info(process_function)
|
||||
process_function = process_function
|
||||
or function(item, path)
|
||||
if path[#path] == vim.inspect.METATABLE then
|
||||
return
|
||||
end
|
||||
if path[#path] == "is_supported" then
|
||||
return
|
||||
end
|
||||
return item
|
||||
end
|
||||
print(vim.inspect(config, { process = process_function }))
|
||||
end
|
||||
|
||||
---@param query_group string
|
||||
---@param lang string
|
||||
function M.edit_query_file(query_group, lang)
|
||||
lang = lang or parsers.get_buf_lang()
|
||||
local files = ts_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
|
||||
|
||||
---@param query_group string
|
||||
---@param lang string
|
||||
function M.edit_query_file_user_after(query_group, lang)
|
||||
lang = lang or parsers.get_buf_lang()
|
||||
local folder = utils.join_path(vim.fn.stdpath "config", "after", "queries", lang)
|
||||
local file = utils.join_path(folder, query_group .. ".scm")
|
||||
if vim.fn.isdirectory(folder) ~= 1 then
|
||||
vim.ui.select({ "Yes", "No" }, { prompt = '"' .. folder .. '" does not exist. Create it?' }, function(choice)
|
||||
if choice == "Yes" then
|
||||
vim.fn.mkdir(folder, "p", "0755")
|
||||
vim.cmd(":edit " .. file)
|
||||
end
|
||||
end)
|
||||
else
|
||||
vim.cmd(":edit " .. file)
|
||||
end
|
||||
end
|
||||
|
||||
M.commands = {
|
||||
TSBufEnable = {
|
||||
run = enable_module,
|
||||
args = {
|
||||
"-nargs=1",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSBufDisable = {
|
||||
run = disable_module,
|
||||
args = {
|
||||
"-nargs=1",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSBufToggle = {
|
||||
run = toggle_module,
|
||||
args = {
|
||||
"-nargs=1",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSEnable = {
|
||||
run = enable_all,
|
||||
args = {
|
||||
"-nargs=+",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSDisable = {
|
||||
run = disable_all,
|
||||
args = {
|
||||
"-nargs=+",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSToggle = {
|
||||
run = toggle_all,
|
||||
args = {
|
||||
"-nargs=+",
|
||||
"-complete=custom,nvim_treesitter#available_modules",
|
||||
},
|
||||
},
|
||||
TSConfigInfo = {
|
||||
run = config_info,
|
||||
args = {
|
||||
"-nargs=0",
|
||||
},
|
||||
},
|
||||
TSEditQuery = {
|
||||
run = M.edit_query_file,
|
||||
args = {
|
||||
"-nargs=+",
|
||||
"-complete=custom,nvim_treesitter#available_query_groups",
|
||||
},
|
||||
},
|
||||
TSEditQueryUserAfter = {
|
||||
run = M.edit_query_file_user_after,
|
||||
args = {
|
||||
"-nargs=+",
|
||||
"-complete=custom,nvim_treesitter#available_query_groups",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
---@param mod string module
|
||||
---@param lang string the language of the buffer
|
||||
---@param bufnr integer the bufnr
|
||||
function M.is_enabled(mod, lang, bufnr)
|
||||
if not parsers.has_parser(lang) then
|
||||
return false
|
||||
end
|
||||
|
||||
local module_config = M.get_module(mod)
|
||||
if not module_config then
|
||||
return false
|
||||
end
|
||||
|
||||
local buffer_enabled = module_config.enabled_buffers and module_config.enabled_buffers[bufnr]
|
||||
local config_enabled = module_config.enable or buffer_enabled
|
||||
if not config_enabled or not module_config.is_supported(lang) then
|
||||
return false
|
||||
end
|
||||
|
||||
local disable = module_config.disable
|
||||
if type(disable) == "function" then
|
||||
if disable(lang, bufnr) then
|
||||
return false
|
||||
end
|
||||
elseif type(disable) == "table" then
|
||||
-- Otherwise it's a list of languages
|
||||
for _, parser in pairs(disable) do
|
||||
if lang == parser then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---Setup call for users to override module configurations.
|
||||
---@param user_data TSConfig module overrides
|
||||
function M.setup(user_data)
|
||||
config.modules = vim.tbl_deep_extend("force", config.modules, user_data)
|
||||
config.ignore_install = user_data.ignore_install or {}
|
||||
config.parser_install_dir = user_data.parser_install_dir or nil
|
||||
if config.parser_install_dir then
|
||||
config.parser_install_dir = vim.fn.expand(config.parser_install_dir, ":p")
|
||||
end
|
||||
|
||||
config.auto_install = user_data.auto_install or false
|
||||
if config.auto_install then
|
||||
require("nvim-treesitter.install").setup_auto_install()
|
||||
end
|
||||
|
||||
local ensure_installed = user_data.ensure_installed or {}
|
||||
if #ensure_installed > 0 then
|
||||
if user_data.sync_install then
|
||||
require("nvim-treesitter.install").ensure_installed_sync(ensure_installed)
|
||||
else
|
||||
require("nvim-treesitter.install").ensure_installed(ensure_installed)
|
||||
end
|
||||
end
|
||||
|
||||
config.modules.ensure_installed = nil
|
||||
config.ensure_installed = ensure_installed
|
||||
|
||||
recurse_modules(function(_, _, new_path)
|
||||
local data = utils.get_at_path(config.modules, new_path)
|
||||
if data.enable then
|
||||
enable_all(new_path)
|
||||
end
|
||||
end, config.modules)
|
||||
end
|
||||
|
||||
---Defines a table of modules that can be attached/detached to buffers
|
||||
---based on language support. A module consist of the following properties:
|
||||
---* @enable Whether the modules is enabled. Can be true or false.
|
||||
---* @disable A list of languages to disable the module for. Only relevant if enable is true.
|
||||
---* @keymaps A list of user mappings for a given module if relevant.
|
||||
---* @is_supported A function which, given a ft, will return true if the ft works on the module.
|
||||
---* @module_path A string path to a module file using `require`. The exported module must contain
|
||||
--- an `attach` and `detach` function. This path is not required if `attach` and `detach`
|
||||
--- functions are provided directly on the module definition.
|
||||
---* @attach An attach function that is called for each buffer that the module is enabled for. This is required
|
||||
--- if a `module_path` is not specified.
|
||||
---* @detach A detach function that is called for each buffer that the module is enabled for. This is required
|
||||
--- if a `module_path` is not specified.
|
||||
---Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order
|
||||
---and can be loaded lazily.
|
||||
---@example
|
||||
---require"nvim-treesitter".define_modules {
|
||||
--- my_cool_module = {
|
||||
--- attach = function()
|
||||
--- do_some_cool_setup()
|
||||
--- end,
|
||||
--- detach = function()
|
||||
--- do_some_cool_teardown()
|
||||
--- end
|
||||
--- }
|
||||
---}
|
||||
---@param mod_defs TSModule[]
|
||||
function M.define_modules(mod_defs)
|
||||
if not is_initialized then
|
||||
table.insert(queued_modules_defs, mod_defs)
|
||||
return
|
||||
end
|
||||
|
||||
recurse_modules(function(key, mod, _, group)
|
||||
group[key] = vim.tbl_extend("keep", mod, {
|
||||
enable = false,
|
||||
disable = {},
|
||||
is_supported = function()
|
||||
return true
|
||||
end,
|
||||
})
|
||||
end, mod_defs)
|
||||
|
||||
config.modules = vim.tbl_deep_extend("keep", config.modules, mod_defs)
|
||||
|
||||
for _, mod in ipairs(M.available_modules(mod_defs)) do
|
||||
local module_config = M.get_module(mod)
|
||||
if module_config and module_config.enable then
|
||||
enable_mod_conf_autocmd(mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Attaches a module to a buffer
|
||||
---@param mod_name string the module name
|
||||
---@param bufnr integer the bufnr
|
||||
---@param lang string the language of the buffer
|
||||
function M.attach_module(mod_name, bufnr, lang)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
lang = lang or parsers.get_buf_lang(bufnr)
|
||||
local resolved_mod = resolve_module(mod_name)
|
||||
|
||||
if resolved_mod and not attached_buffers_by_module.has(mod_name, bufnr) and M.is_enabled(mod_name, lang, bufnr) then
|
||||
attached_buffers_by_module.set(mod_name, bufnr, true)
|
||||
resolved_mod.attach(bufnr, lang)
|
||||
end
|
||||
end
|
||||
|
||||
---Detaches a module to a buffer
|
||||
---@param mod_name string the module name
|
||||
---@param bufnr integer the 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 string the module name
|
||||
---@param bufnr integer the bufnr
|
||||
---@param lang string the language of the buffer
|
||||
function M.reattach_module(mod_name, bufnr, lang)
|
||||
M.detach_module(mod_name, bufnr)
|
||||
M.attach_module(mod_name, bufnr, lang)
|
||||
end
|
||||
|
||||
---Gets available modules
|
||||
---@param root {[string]:TSModule}|nil table to find modules
|
||||
function M.available_modules(root)
|
||||
local modules = {}
|
||||
|
||||
recurse_modules(function(_, _, path)
|
||||
table.insert(modules, path)
|
||||
end, root)
|
||||
|
||||
return modules
|
||||
end
|
||||
|
||||
---Gets a module config by path
|
||||
---@param mod_path string path to the module
|
||||
---@return TSModule|nil the module or nil
|
||||
function M.get_module(mod_path)
|
||||
local mod = utils.get_at_path(config.modules, mod_path)
|
||||
|
||||
return M.is_module(mod) and mod or nil
|
||||
end
|
||||
|
||||
---Determines whether the provided table is a module.
|
||||
---A module should contain an attach and detach function.
|
||||
---@param mod table the module table
|
||||
---@return boolean
|
||||
function M.is_module(mod)
|
||||
return type(mod) == "table"
|
||||
and ((type(mod.attach) == "function" and type(mod.detach) == "function") or type(mod.module_path) == "string")
|
||||
end
|
||||
|
||||
---Initializes built-in modules and any queued modules
|
||||
---registered by plugins or the user.
|
||||
function M.init()
|
||||
is_initialized = true
|
||||
M.define_modules(builtin_modules)
|
||||
|
||||
for _, mod_def in ipairs(queued_modules_defs) do
|
||||
M.define_modules(mod_def)
|
||||
end
|
||||
end
|
||||
|
||||
---If parser_install_dir is not nil is used or created.
|
||||
---If parser_install_dir is nil try the package dir of the nvim-treesitter
|
||||
---plugin first, followed by the "site" dir from "runtimepath". "site" dir will
|
||||
---be created if it doesn't exist. Using only the package dir won't work when
|
||||
---the plugin is installed with Nix, since the "/nix/store" is read-only.
|
||||
---@param folder_name string|nil
|
||||
---@return string|nil, string|nil
|
||||
function M.get_parser_install_dir(folder_name)
|
||||
folder_name = folder_name or "parser"
|
||||
|
||||
local install_dir
|
||||
if config.parser_install_dir then
|
||||
install_dir = config.parser_install_dir
|
||||
else
|
||||
install_dir = utils.get_package_path()
|
||||
end
|
||||
local parser_dir = utils.join_path(install_dir, folder_name)
|
||||
|
||||
return utils.create_or_reuse_writable_dir(
|
||||
parser_dir,
|
||||
utils.join_space("Could not create parser dir '", parser_dir, "': "),
|
||||
utils.join_space(
|
||||
"Parser dir '",
|
||||
parser_dir,
|
||||
"' should be read/write (see README on how to configure an alternative install location)"
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
function M.get_parser_info_dir()
|
||||
return M.get_parser_install_dir "parser-info"
|
||||
end
|
||||
|
||||
function M.get_update_strategy()
|
||||
return config.update_strategy
|
||||
end
|
||||
|
||||
function M.get_ignored_parser_installs()
|
||||
return config.ignore_install or {}
|
||||
end
|
||||
|
||||
function M.get_ensure_installed_parsers()
|
||||
return config.ensure_installed or {}
|
||||
end
|
||||
|
||||
return M
|
||||
119
lua/nvim-treesitter/fold.lua
Normal file
119
lua/nvim-treesitter/fold.lua
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
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 _, match in ipairs(matches) do
|
||||
local start, stop, stop_col
|
||||
if match.metadata and match.metadata.range then
|
||||
start, _, stop, stop_col = unpack(match.metadata.range)
|
||||
else
|
||||
start, _, stop, stop_col = match.node:range()
|
||||
end
|
||||
|
||||
if stop_col == 0 then
|
||||
stop = stop - 1
|
||||
end
|
||||
|
||||
local fold_length = stop - start + 1
|
||||
local should_fold = fold_length > min_fold_lines
|
||||
|
||||
-- Fold only multiline nodes that are not exactly the same as previously met folds
|
||||
-- Checking against just the previously found fold is sufficient if nodes
|
||||
-- are returned in preorder or postorder when traversing tree
|
||||
if should_fold and not (start == prev_start and stop == prev_stop) then
|
||||
start_counts[start] = (start_counts[start] or 0) + 1
|
||||
stop_counts[stop] = (stop_counts[stop] or 0) + 1
|
||||
prev_start = start
|
||||
prev_stop = stop
|
||||
end
|
||||
end
|
||||
|
||||
local levels = {}
|
||||
local current_level = 0
|
||||
|
||||
-- We now have the list of fold opening and closing, fill the gaps and mark where fold start
|
||||
for lnum = 0, api.nvim_buf_line_count(bufnr) do
|
||||
local prefix = ""
|
||||
|
||||
local last_trimmed_level = trim_level(current_level)
|
||||
current_level = current_level + (start_counts[lnum] or 0)
|
||||
local trimmed_level = trim_level(current_level)
|
||||
current_level = current_level - (stop_counts[lnum] or 0)
|
||||
local next_trimmed_level = trim_level(current_level)
|
||||
|
||||
-- Determine if it's the start/end of a fold
|
||||
-- NB: vim's fold-expr interface does not have a mechanism to indicate that
|
||||
-- two (or more) folds start at this line, so it cannot distinguish between
|
||||
-- ( \n ( \n )) \n (( \n ) \n )
|
||||
-- versus
|
||||
-- ( \n ( \n ) \n ( \n ) \n )
|
||||
-- If it did have such a mechanism, (trimmed_level - last_trimmed_level)
|
||||
-- would be the correct number of starts to pass on.
|
||||
if trimmed_level - last_trimmed_level > 0 then
|
||||
prefix = ">"
|
||||
elseif trimmed_level - next_trimmed_level > 0 then
|
||||
-- Ending marks tend to confuse vim more than it helps, particularly when
|
||||
-- the fold level changes by at least 2; we can uncomment this if
|
||||
-- vim's behavior gets fixed.
|
||||
-- prefix = "<"
|
||||
prefix = ""
|
||||
end
|
||||
|
||||
levels[lnum + 1] = prefix .. tostring(trimmed_level)
|
||||
end
|
||||
|
||||
return levels
|
||||
end)
|
||||
|
||||
---@param lnum integer
|
||||
---@return string
|
||||
function M.get_fold_indic(lnum)
|
||||
if not parsers.has_parser() or not lnum then
|
||||
return "0"
|
||||
end
|
||||
|
||||
local buf = api.nvim_get_current_buf()
|
||||
|
||||
local levels = folds_levels(buf) or {}
|
||||
|
||||
return levels[lnum] or "0"
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,174 +1,171 @@
|
|||
local parsers = require('nvim-treesitter.parsers')
|
||||
local config = require('nvim-treesitter.config')
|
||||
local util = require('nvim-treesitter.util')
|
||||
local tsq = vim.treesitter.query
|
||||
local health = vim.health
|
||||
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 = vim.health or require "health"
|
||||
|
||||
local M = {}
|
||||
|
||||
local NVIM_TREESITTER_MINIMUM_ABI = 13
|
||||
local TREE_SITTER_MIN_VER = { 0, 26, 1 }
|
||||
|
||||
---@param name string
|
||||
---@return table?
|
||||
local function check_exe(name)
|
||||
if vim.fn.executable(name) == 1 then
|
||||
local path = vim.fn.exepath(name)
|
||||
local out = vim.trim(vim.fn.system({ name, '--version' }))
|
||||
local version = vim.version.parse(out)
|
||||
return { path = path, version = version, out = out }
|
||||
end
|
||||
end
|
||||
|
||||
local function install_health()
|
||||
health.start('Requirements')
|
||||
health.report_start "Installation"
|
||||
|
||||
do -- nvim check
|
||||
if vim.fn.has('nvim-0.12') ~= 1 then
|
||||
health.error('Nvim-treesitter requires Neovim 0.12.0 or later.')
|
||||
end
|
||||
if fn.has "nvim-0.7" == 0 then
|
||||
health.report_error "Nvim-treesitter requires Neovim 0.7.0+"
|
||||
end
|
||||
|
||||
if fn.executable "tree-sitter" == 0 then
|
||||
health.report_warn(
|
||||
"`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar,"
|
||||
.. " not required for :TSInstall)"
|
||||
)
|
||||
else
|
||||
health.report_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.report_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.report_ok("`node` found " .. version .. " (only needed for :TSInstallFromGrammar)")
|
||||
end
|
||||
|
||||
if fn.executable "git" == 0 then
|
||||
health.report_error("`git` executable not found.", {
|
||||
"Install it with your package manager.",
|
||||
"Check that your `$PATH` is set correctly.",
|
||||
})
|
||||
else
|
||||
health.report_ok "`git` executable found."
|
||||
end
|
||||
|
||||
local cc = shell.select_executable(install.compilers)
|
||||
if not cc then
|
||||
health.report_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.report_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 '
|
||||
health.report_ok(
|
||||
"Neovim was compiled with tree-sitter runtime ABI version "
|
||||
.. vim.treesitter.language_version
|
||||
.. ' (required >='
|
||||
.. " (required >="
|
||||
.. NVIM_TREESITTER_MINIMUM_ABI
|
||||
.. ').'
|
||||
.. "). Parsers must be compatible with runtime ABI."
|
||||
)
|
||||
else
|
||||
health.error(
|
||||
'Neovim was compiled with tree-sitter runtime ABI version '
|
||||
health.report_error(
|
||||
"Neovim was compiled with tree-sitter runtime ABI version "
|
||||
.. vim.treesitter.language_version
|
||||
.. '.\n'
|
||||
.. 'nvim-treesitter expects at least ABI version '
|
||||
.. ".\n"
|
||||
.. "nvim-treesitter expects at least ABI version "
|
||||
.. NVIM_TREESITTER_MINIMUM_ABI
|
||||
.. '\n'
|
||||
.. 'Please make sure that Neovim is linked against a recent tree-sitter library when building'
|
||||
.. ' or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI.'
|
||||
.. "\n"
|
||||
.. "Please make sure that Neovim is linked against are recent tree-sitter runtime when building"
|
||||
.. " or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
do -- treesitter check
|
||||
local ts = check_exe('tree-sitter')
|
||||
if ts then
|
||||
if vim.version.ge(ts.version, TREE_SITTER_MIN_VER) then
|
||||
health.ok(string.format('tree-sitter-cli %s (%s)', ts.version, ts.path))
|
||||
else
|
||||
health.error(
|
||||
string.format('tree-sitter-cli v%d.%d.%d is required', unpack(TREE_SITTER_MIN_VER))
|
||||
)
|
||||
end
|
||||
else
|
||||
health.error('tree-sitter-cli not found')
|
||||
end
|
||||
end
|
||||
|
||||
do -- curl+tar check
|
||||
local tar = check_exe('tar')
|
||||
if tar then
|
||||
health.ok(string.format('tar %s (%s)', tar.version, tar.path))
|
||||
else
|
||||
health.error('tar not found')
|
||||
end
|
||||
|
||||
local curl = check_exe('curl')
|
||||
if curl then
|
||||
health.ok(string.format('curl %s (%s)\n%s', curl.version, curl.path, curl.out))
|
||||
else
|
||||
health.error('curl not found')
|
||||
end
|
||||
end
|
||||
|
||||
health.start('OS Info')
|
||||
local osinfo = vim.uv.os_uname() ---@type table<string,string>
|
||||
for k, v in pairs(osinfo) do
|
||||
health.info(k .. ': ' .. v)
|
||||
end
|
||||
|
||||
local installdir = config.get_install_dir('')
|
||||
health.start('Install directory for parsers and queries')
|
||||
health.info(installdir)
|
||||
if vim.uv.fs_access(installdir, 'w') then
|
||||
health.ok('is writable.')
|
||||
else
|
||||
health.error('is not writable.')
|
||||
end
|
||||
if
|
||||
vim.list_contains(vim.tbl_map(vim.fs.normalize, vim.api.nvim_list_runtime_paths()), installdir)
|
||||
then
|
||||
health.ok('is in runtimepath.')
|
||||
else
|
||||
health.error('is not in runtimepath.')
|
||||
end
|
||||
health.report_start("OS Info:\n" .. vim.inspect(vim.loop.os_uname()))
|
||||
end
|
||||
|
||||
local function query_status(lang, query_group)
|
||||
local ok, err = pcall(tsq.get, lang, query_group)
|
||||
local ok, err = pcall(queries.get_query, lang, query_group)
|
||||
if not ok then
|
||||
return 'x', err
|
||||
return "x", err
|
||||
elseif not err then
|
||||
return '.'
|
||||
return "."
|
||||
else
|
||||
return '✓'
|
||||
return "✓"
|
||||
end
|
||||
end
|
||||
|
||||
function M.check()
|
||||
--- @type {[1]: string, [2]: string, [3]: string}[]
|
||||
local error_collection = {}
|
||||
-- Installation dependency checks
|
||||
install_health()
|
||||
|
||||
queries.invalidate_query_cache()
|
||||
-- Parser installation checks
|
||||
health.start('Installed languages' .. string.rep(' ', 5) .. 'H L F I J')
|
||||
local languages = config.get_installed()
|
||||
table.sort(languages)
|
||||
for _, lang in ipairs(languages) do
|
||||
local parser = parsers[lang]
|
||||
local out = lang .. string.rep(' ', 22 - #lang)
|
||||
if parser and parser.install_info then
|
||||
for _, query_group in pairs(M.bundled_queries) do
|
||||
local status, err = query_status(lang, query_group)
|
||||
out = out .. status .. ' '
|
||||
if err then
|
||||
table.insert(error_collection, { lang, query_group, err })
|
||||
end
|
||||
end
|
||||
end
|
||||
if parser and parser.requires then
|
||||
for _, p in pairs(parser.requires) do
|
||||
if not vim.list_contains(languages, p) then
|
||||
table.insert(error_collection, { lang, 'queries', 'dependency ' .. p .. ' missing' })
|
||||
end
|
||||
end
|
||||
end
|
||||
health.info(vim.fn.trim(out, ' ', 2))
|
||||
end
|
||||
health.start(' Legend: [H]ighlights, [L]ocals, [F]olds, [I]ndents, In[J]ections')
|
||||
local parser_installation = { "Parser/Features" .. string.rep(" ", 9) .. "H L F I J" }
|
||||
for _, parser_name in pairs(info.installed_parsers()) do
|
||||
local installed = #api.nvim_get_runtime_file("parser/" .. parser_name .. ".so", false)
|
||||
|
||||
-- Only append information about installed parsers
|
||||
if installed >= 1 then
|
||||
local multiple_parsers = installed > 1 and "+" or ""
|
||||
local out = " - " .. parser_name .. multiple_parsers .. string.rep(" ", 20 - (#parser_name + #multiple_parsers))
|
||||
for _, query_group in pairs(queries.built_in_query_groups) do
|
||||
local status, err = query_status(parser_name, query_group)
|
||||
out = out .. status .. " "
|
||||
if err then
|
||||
table.insert(error_collection, { parser_name, query_group, err })
|
||||
end
|
||||
end
|
||||
table.insert(parser_installation, vim.fn.trim(out, " ", 2))
|
||||
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.report_start(table.concat(parser_installation, "\n"))
|
||||
if #error_collection > 0 then
|
||||
health.start('The following errors have been detected in query files:')
|
||||
health.report_start "The following errors have been detected:"
|
||||
for _, p in ipairs(error_collection) do
|
||||
local lang, type = p[1], p[2]
|
||||
local lang, type, err = unpack(p)
|
||||
local lines = {}
|
||||
table.insert(lines, lang .. '(' .. type .. '): ')
|
||||
local files = tsq.get_files(lang, type)
|
||||
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 query = util.read_file(file)
|
||||
local _, file_err = pcall(tsq.parse, lang, query)
|
||||
if file_err then
|
||||
table.insert(lines, file)
|
||||
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, ''))
|
||||
health.report_error(table.concat(lines, "\n"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
M.bundled_queries = { 'highlights', 'locals', 'folds', 'indents', 'injections' }
|
||||
|
||||
return M
|
||||
|
|
|
|||
48
lua/nvim-treesitter/highlight.lua
Normal file
48
lua/nvim-treesitter/highlight.lua
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
local configs = require "nvim-treesitter.configs"
|
||||
|
||||
local M = {}
|
||||
|
||||
---@param config table
|
||||
---@param lang string
|
||||
---@return boolean
|
||||
local function should_enable_vim_regex(config, lang)
|
||||
local additional_hl = config.additional_vim_regex_highlighting
|
||||
local is_table = type(additional_hl) == "table"
|
||||
|
||||
return additional_hl and (not is_table or vim.tbl_contains(additional_hl, lang))
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
---@param lang string
|
||||
function M.attach(bufnr, lang)
|
||||
local config = configs.get_module "highlight"
|
||||
vim.treesitter.start(bufnr, lang)
|
||||
if config and should_enable_vim_regex(config, lang) then
|
||||
vim.bo[bufnr].syntax = "ON"
|
||||
end
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
function M.detach(bufnr)
|
||||
vim.treesitter.stop(bufnr)
|
||||
end
|
||||
|
||||
---@deprecated
|
||||
function M.start(...)
|
||||
vim.notify(
|
||||
"`nvim-treesitter.highlight.start` is deprecated: use `nvim-treesitter.highlight.attach` or `vim.treesitter.start`",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
M.attach(...)
|
||||
end
|
||||
|
||||
---@deprecated
|
||||
function M.stop(...)
|
||||
vim.notify(
|
||||
"`nvim-treesitter.highlight.stop` is deprecated: use `nvim-treesitter.highlight.detach` or `vim.treesitter.stop`",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
M.detach(...)
|
||||
end
|
||||
|
||||
return M
|
||||
168
lua/nvim-treesitter/incremental_selection.lua
Normal file
168
lua/nvim-treesitter/incremental_selection.lua
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
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
|
||||
|
||||
local FUNCTION_DESCRIPTIONS = {
|
||||
init_selection = "Start selecting nodes with nvim-treesitter",
|
||||
node_incremental = "Increment selection to named node",
|
||||
scope_incremental = "Increment selection to surrounding scope",
|
||||
node_decremental = "Shrink selection to previous named node",
|
||||
}
|
||||
|
||||
function M.attach(bufnr)
|
||||
local config = configs.get_module "incremental_selection"
|
||||
for funcname, mapping in pairs(config.keymaps) do
|
||||
if mapping then
|
||||
local mode
|
||||
local rhs
|
||||
if funcname == "init_selection" then
|
||||
mode = "n"
|
||||
rhs = M[funcname]
|
||||
else
|
||||
mode = "x"
|
||||
-- We need to move to command mode to access marks '< (visual area start) and '> (visual area end) which are not
|
||||
-- properly accessible in visual mode.
|
||||
rhs = string.format(":lua require'nvim-treesitter.incremental_selection'.%s()<CR>", funcname)
|
||||
end
|
||||
vim.keymap.set(
|
||||
mode,
|
||||
mapping,
|
||||
rhs,
|
||||
{ buffer = bufnr, silent = true, noremap = true, desc = FUNCTION_DESCRIPTIONS[funcname] }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.detach(bufnr)
|
||||
local config = configs.get_module "incremental_selection"
|
||||
for f, mapping in pairs(config.keymaps) do
|
||||
if mapping then
|
||||
if f == "init_selection" then
|
||||
vim.keymap.del("n", mapping, { buffer = bufnr })
|
||||
else
|
||||
vim.keymap.del("x", mapping, { buffer = bufnr })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,132 +1,90 @@
|
|||
local parsers = require "nvim-treesitter.parsers"
|
||||
local queries = require "nvim-treesitter.query"
|
||||
local tsutils = require "nvim-treesitter.ts_utils"
|
||||
local ts = vim.treesitter
|
||||
|
||||
local M = {}
|
||||
|
||||
M.avoid_force_reparsing = {
|
||||
yaml = true,
|
||||
}
|
||||
|
||||
M.comment_parsers = {
|
||||
comment = true,
|
||||
luadoc = true,
|
||||
javadoc = true,
|
||||
jsdoc = true,
|
||||
phpdoc = true,
|
||||
}
|
||||
|
||||
local function getline(lnum)
|
||||
return vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)[1] or ''
|
||||
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
|
||||
|
||||
---@param lnum integer
|
||||
---@return integer
|
||||
local function get_indentcols_at_line(lnum)
|
||||
local _, indentcols = getline(lnum):find('^%s*')
|
||||
return indentcols or 0
|
||||
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
|
||||
|
||||
---@param root TSNode
|
||||
---@param lnum integer
|
||||
---@param col? integer
|
||||
---@return TSNode?
|
||||
local function get_first_node_at_line(root, lnum, col)
|
||||
col = col or get_indentcols_at_line(lnum)
|
||||
return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1)
|
||||
end
|
||||
|
||||
---@param root TSNode
|
||||
---@param lnum integer
|
||||
---@param col? integer
|
||||
---@return TSNode?
|
||||
local function get_last_node_at_line(root, lnum, col)
|
||||
col = col or (#getline(lnum) - 1)
|
||||
return root:descendant_for_range(lnum - 1, col, lnum - 1, col + 1)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
---@param node TSNode
|
||||
---@param delimiter string
|
||||
---@return TSNode? child
|
||||
---@return boolean? is_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_() }
|
||||
local trimmed_after_delim
|
||||
local escaped_delimiter = delimiter:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]', '%%%1')
|
||||
trimmed_after_delim =
|
||||
assert(line):sub(end_char[2] + 1):gsub('[%s' .. escaped_delimiter .. ']*', '')
|
||||
return child, #trimmed_after_delim == 0
|
||||
return child, #line == end_char[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Memoize a function using hash_fn to hash the arguments.
|
||||
---@generic F: function
|
||||
---@param fn F
|
||||
---@param hash_fn fun(...): any
|
||||
---@return F
|
||||
local function memoize(fn, hash_fn)
|
||||
local cache = setmetatable({}, { __mode = 'kv' }) ---@type table<any,any>
|
||||
|
||||
return function(...)
|
||||
local key = hash_fn(...)
|
||||
if cache[key] == nil then
|
||||
local v = fn(...) ---@type any
|
||||
cache[key] = v ~= nil and v or vim.NIL
|
||||
end
|
||||
|
||||
local v = cache[key]
|
||||
return v ~= vim.NIL and v or nil
|
||||
end
|
||||
end
|
||||
|
||||
local get_indents = memoize(function(bufnr, root, lang)
|
||||
---@type table<string,table<string,table>>
|
||||
local get_indents = tsutils.memoize_by_buf_tick(function(bufnr, root, lang)
|
||||
local map = {
|
||||
['indent.auto'] = {},
|
||||
['indent.begin'] = {},
|
||||
['indent.end'] = {},
|
||||
['indent.dedent'] = {},
|
||||
['indent.branch'] = {},
|
||||
['indent.ignore'] = {},
|
||||
['indent.align'] = {},
|
||||
['indent.zero'] = {},
|
||||
auto = {},
|
||||
indent = {},
|
||||
indent_end = {},
|
||||
dedent = {},
|
||||
branch = {},
|
||||
ignore = {},
|
||||
aligned_indent = {},
|
||||
zero_indent = {},
|
||||
}
|
||||
|
||||
local query = ts.query.get(lang, 'indents')
|
||||
if not query then
|
||||
return map
|
||||
end
|
||||
for id, node, metadata in query:iter_captures(root, bufnr) do
|
||||
if assert(query.captures[id]):sub(1, 1) ~= '_' then
|
||||
map[query.captures[id]][node:id()] = metadata or {}
|
||||
end
|
||||
for name, node, metadata in queries.iter_captures(bufnr, "indents", root, lang) do
|
||||
map[name][node:id()] = metadata or {}
|
||||
end
|
||||
|
||||
return map
|
||||
end, function(bufnr, root, lang)
|
||||
return tostring(bufnr) .. root:id() .. '_' .. lang
|
||||
end)
|
||||
end, {
|
||||
-- Memoize by bufnr and lang together.
|
||||
key = function(bufnr, root, lang)
|
||||
return tostring(bufnr) .. root:id() .. "_" .. lang
|
||||
end,
|
||||
})
|
||||
|
||||
---@param lnum integer (1-indexed)
|
||||
---@return integer
|
||||
---@param lnum number (1-indexed)
|
||||
function M.get_indent(lnum)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local parser = ts.get_parser(bufnr)
|
||||
local parser = parsers.get_parser(bufnr)
|
||||
if not parser or not lnum then
|
||||
return -1
|
||||
end
|
||||
|
||||
parser:parse({ vim.fn.line('w0') - 1, vim.fn.line('w$') })
|
||||
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 ---@type TSNode, vim.treesitter.LanguageTree
|
||||
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 ts.is_in_node_range(local_root, lnum - 1, 0) then
|
||||
if not root or root:byte_length() >= local_root:byte_length() then
|
||||
if not root or tsutils.node_length(root) >= tsutils.node_length(local_root) then
|
||||
root = local_root
|
||||
lang_tree = tree
|
||||
end
|
||||
|
|
@ -139,27 +97,12 @@ function M.get_indent(lnum)
|
|||
end
|
||||
|
||||
local q = get_indents(vim.api.nvim_get_current_buf(), root, lang_tree:lang())
|
||||
local node ---@type TSNode?
|
||||
if getline(lnum):find('^%s*$') then
|
||||
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)
|
||||
local indentcols = get_indentcols_at_line(prevlnum)
|
||||
local prevline = vim.trim(getline(prevlnum))
|
||||
-- The final position can be trailing spaces, which should not affect indentation
|
||||
node = get_last_node_at_line(root, prevlnum, indentcols + #prevline - 1)
|
||||
if node and node:type():match('comment') then
|
||||
-- The final node we capture of the previous line can be a comment node, which should also be ignored
|
||||
-- Unless the last line is an entire line of comment, ignore the comment range and find the last node again
|
||||
local first_node = get_first_node_at_line(root, prevlnum, indentcols)
|
||||
local _, scol, _, _ = node:range()
|
||||
if first_node and first_node:id() ~= node:id() then
|
||||
-- In case the last captured node is a trailing comment node, re-trim the string
|
||||
prevline = vim.trim(prevline:sub(1, scol - indentcols))
|
||||
-- Add back indent as indent of prevline was trimmed away
|
||||
local col = indentcols + #prevline - 1
|
||||
node = get_last_node_at_line(root, prevlnum, col)
|
||||
end
|
||||
end
|
||||
if node and q['indent.end'][node:id()] then
|
||||
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
|
||||
|
|
@ -175,33 +118,22 @@ function M.get_indent(lnum)
|
|||
end
|
||||
|
||||
-- tracks to ensure multiple indent levels are not applied for same line
|
||||
local is_processed_by_row = {} --- @type table<integer,boolean>
|
||||
local is_processed_by_row = {}
|
||||
|
||||
if node and q['indent.zero'][node:id()] then
|
||||
if q.zero_indent[node:id()] then
|
||||
return 0
|
||||
end
|
||||
|
||||
while node do
|
||||
-- do 'autoindent' if not marked as @indent
|
||||
if
|
||||
not q['indent.begin'][node:id()]
|
||||
and not q['indent.align'][node:id()]
|
||||
and q['indent.auto'][node:id()]
|
||||
and node:start() < lnum - 1
|
||||
and lnum - 1 <= node:end_()
|
||||
then
|
||||
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.begin'][node:id()]
|
||||
and q['indent.ignore'][node:id()]
|
||||
and node:start() < lnum - 1
|
||||
and lnum - 1 <= node:end_()
|
||||
then
|
||||
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
|
||||
|
||||
|
|
@ -211,10 +143,7 @@ function M.get_indent(lnum)
|
|||
|
||||
if
|
||||
not is_processed_by_row[srow]
|
||||
and (
|
||||
(q['indent.branch'][node:id()] and srow == lnum - 1)
|
||||
or (q['indent.dedent'][node:id()] and srow ~= lnum - 1)
|
||||
)
|
||||
and ((q.branch[node:id()] and srow == lnum - 1) or (q.dedent[node:id()] and srow ~= lnum - 1))
|
||||
then
|
||||
indent = indent - indent_size
|
||||
is_processed = true
|
||||
|
|
@ -225,112 +154,38 @@ function M.get_indent(lnum)
|
|||
local is_in_err = false
|
||||
if should_process then
|
||||
local parent = node:parent()
|
||||
is_in_err = parent and parent:has_error() or false
|
||||
is_in_err = parent and parent:has_error()
|
||||
end
|
||||
if
|
||||
should_process
|
||||
and (
|
||||
q['indent.begin'][node:id()]
|
||||
and (srow ~= erow or is_in_err or q['indent.begin'][node:id()]['indent.immediate'])
|
||||
and (srow ~= lnum - 1 or q['indent.begin'][node:id()]['indent.start_at_same_line'])
|
||||
q.indent[node:id()]
|
||||
and (srow ~= erow or is_in_err)
|
||||
and (srow ~= lnum - 1 or q.indent[node:id()].start_at_same_line)
|
||||
)
|
||||
then
|
||||
indent = indent + indent_size
|
||||
is_processed = true
|
||||
end
|
||||
|
||||
if is_in_err and not q['indent.align'][node:id()] then
|
||||
-- only when the node is in error, promote the
|
||||
-- first child's aligned indent to the error node
|
||||
-- to work around ((ERROR "X" . (_)) @aligned_indent (#set! "delimiter" "AB"))
|
||||
-- matching for all X, instead set do
|
||||
-- (ERROR "X" @aligned_indent (#set! "delimiter" "AB") . (_))
|
||||
-- and we will fish it out here.
|
||||
for c in node:iter_children() do
|
||||
if q['indent.align'][c:id()] then
|
||||
q['indent.align'][node:id()] = q['indent.align'][c:id()]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- do not indent for nodes that starts-and-ends on same line and starts on target line (lnum)
|
||||
if
|
||||
should_process
|
||||
and q['indent.align'][node:id()]
|
||||
and (srow ~= erow or is_in_err)
|
||||
and (srow ~= lnum - 1)
|
||||
then
|
||||
local metadata = q['indent.align'][node:id()]
|
||||
local o_delim_node, o_is_last_in_line ---@type TSNode?, boolean?
|
||||
local c_delim_node, c_is_last_in_line ---@type TSNode?, boolean?, boolean?
|
||||
local indent_is_absolute = false
|
||||
if metadata['indent.open_delimiter'] then
|
||||
o_delim_node, o_is_last_in_line =
|
||||
find_delimiter(bufnr, node, metadata['indent.open_delimiter'])
|
||||
if 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 metadata['indent.close_delimiter'] then
|
||||
c_delim_node, c_is_last_in_line =
|
||||
find_delimiter(bufnr, node, metadata['indent.close_delimiter'])
|
||||
else
|
||||
c_delim_node = node
|
||||
end
|
||||
|
||||
if o_delim_node then
|
||||
local o_srow, o_scol = o_delim_node:start()
|
||||
local c_srow = nil --- @type integer?
|
||||
if c_delim_node then
|
||||
c_srow = c_delim_node:start()
|
||||
end
|
||||
if o_is_last_in_line then
|
||||
if is_last_in_line then
|
||||
-- hanging indent (previous line ended with starting delimiter)
|
||||
-- should be processed like indent
|
||||
if should_process then
|
||||
indent = indent + indent_size * 1
|
||||
if c_is_last_in_line then
|
||||
-- If current line is outside the range of a node marked with `@aligned_indent`
|
||||
-- Then its indent level shouldn't be affected by `@aligned_indent` node
|
||||
if c_srow and c_srow < lnum - 1 then
|
||||
indent = math.max(indent - indent_size, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
indent = indent + indent_size * 1
|
||||
else
|
||||
-- aligned indent
|
||||
if c_is_last_in_line and c_srow and o_srow ~= c_srow and c_srow < lnum - 1 then
|
||||
-- If current line is outside the range of a node marked with `@aligned_indent`
|
||||
-- Then its indent level shouldn't be affected by `@aligned_indent` node
|
||||
indent = math.max(indent - indent_size, 0)
|
||||
else
|
||||
indent = o_scol + (metadata['indent.increment'] or 1)
|
||||
indent_is_absolute = true
|
||||
end
|
||||
end
|
||||
-- deal with the final line
|
||||
local avoid_last_matching_next = false
|
||||
if c_srow and c_srow ~= o_srow and c_srow == lnum - 1 then
|
||||
-- delims end on current line, and are not open and closed same line.
|
||||
-- then this last line may need additional indent to avoid clashes
|
||||
-- with the next. `indent.avoid_last_matching_next` controls this behavior,
|
||||
-- for example this is needed for function parameters.
|
||||
avoid_last_matching_next = metadata['indent.avoid_last_matching_next'] or false
|
||||
end
|
||||
if avoid_last_matching_next then
|
||||
-- last line must be indented more in cases where
|
||||
-- it would be same indent as next line (we determine this as one
|
||||
-- width more than the open indent to avoid confusing with any
|
||||
-- hanging indents)
|
||||
if indent <= vim.fn.indent(o_srow + 1) + indent_size then
|
||||
indent = indent + indent_size * 1
|
||||
else
|
||||
indent = indent
|
||||
end
|
||||
end
|
||||
is_processed = true
|
||||
if indent_is_absolute then
|
||||
-- don't allow further indenting by parent nodes, this is an absolute position
|
||||
return indent
|
||||
local _, o_scol = o_delim_node:start()
|
||||
return math.max(indent, 0) + o_scol + (metadata.increment or 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -343,4 +198,15 @@ function M.get_indent(lnum)
|
|||
return indent
|
||||
end
|
||||
|
||||
local indent_funcs = {}
|
||||
|
||||
function M.attach(bufnr)
|
||||
indent_funcs[bufnr] = vim.bo.indentexpr
|
||||
vim.bo.indentexpr = "nvim_treesitter#indent()"
|
||||
end
|
||||
|
||||
function M.detach(bufnr)
|
||||
vim.bo.indentexpr = indent_funcs[bufnr]
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
179
lua/nvim-treesitter/info.lua
Normal file
179
lua/nvim-treesitter/info.lua
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
local api = vim.api
|
||||
local configs = require "nvim-treesitter.configs"
|
||||
local parsers = require "nvim-treesitter.parsers"
|
||||
|
||||
local M = {}
|
||||
|
||||
local function install_info()
|
||||
local max_len = 0
|
||||
for _, ft in pairs(parsers.available_parsers()) do
|
||||
if #ft > max_len then
|
||||
max_len = #ft
|
||||
end
|
||||
end
|
||||
|
||||
local parser_list = parsers.available_parsers()
|
||||
table.sort(parser_list)
|
||||
for _, lang in pairs(parser_list) do
|
||||
local is_installed = #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0
|
||||
api.nvim_out_write(lang .. string.rep(" ", max_len - #lang + 1))
|
||||
if is_installed then
|
||||
api.nvim_out_write "[✓] installed\n"
|
||||
elseif pcall(vim.treesitter.inspect_lang, lang) then
|
||||
api.nvim_out_write "[✗] not installed (but still loaded. Restart Neovim!)\n"
|
||||
else
|
||||
api.nvim_out_write "[✗] not installed\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort a list of modules into namespaces.
|
||||
-- {'mod1', 'mod2.sub1', 'mod2.sub2', 'mod3'}
|
||||
-- ->
|
||||
-- { default = {'mod1', 'mod3'}, mod2 = {'sub1', 'sub2'}}
|
||||
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")
|
||||
vim.cmd [[
|
||||
syntax match TSModuleInfoGood /✓/
|
||||
syntax match TSModuleInfoBad /✗/
|
||||
syntax match TSModuleInfoHeader /^>>.*$/ contains=TSModuleInfoNamespace
|
||||
syntax match TSModuleInfoNamespace /^>> \w*/ contained
|
||||
syntax match TSModuleInfoParser /^[^> ]*\ze /
|
||||
]]
|
||||
|
||||
local highlights = {
|
||||
TSModuleInfoGood = { fg = "LightGreen", bold = true, default = true },
|
||||
TSModuleInfoBad = { fg = "Crimson", default = true },
|
||||
TSModuleInfoHeader = { link = "Type", default = true },
|
||||
TSModuleInfoNamespace = { link = "Statement", default = true },
|
||||
TSModuleInfoParser = { link = "Identifier", default = true },
|
||||
}
|
||||
for k, v in pairs(highlights) do
|
||||
api.nvim_set_hl(0, k, v)
|
||||
end
|
||||
end
|
||||
|
||||
local function module_info(module)
|
||||
if module and not configs.get_module(module) then
|
||||
return
|
||||
end
|
||||
|
||||
local parserlist = parsers.available_parsers()
|
||||
if module then
|
||||
print_info_modules(parserlist, module)
|
||||
else
|
||||
print_info_modules(parserlist)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
function M.setup(...)
|
||||
require('nvim-treesitter.config').setup(...)
|
||||
end
|
||||
|
||||
function M.get_available(...)
|
||||
return require('nvim-treesitter.config').get_available(...)
|
||||
end
|
||||
|
||||
function M.get_installed(...)
|
||||
return require('nvim-treesitter.config').get_installed(...)
|
||||
end
|
||||
|
||||
function M.install(...)
|
||||
return require('nvim-treesitter.install').install(...)
|
||||
end
|
||||
|
||||
function M.uninstall(...)
|
||||
return require('nvim-treesitter.install').uninstall(...)
|
||||
end
|
||||
|
||||
function M.update(...)
|
||||
return require('nvim-treesitter.install').update(...)
|
||||
end
|
||||
|
||||
function M.indentexpr()
|
||||
return require('nvim-treesitter.indent').get_indent(vim.v.lnum)
|
||||
end
|
||||
|
||||
return M
|
||||
File diff suppressed because it is too large
Load diff
350
lua/nvim-treesitter/locals.lua
Normal file
350
lua/nvim-treesitter/locals.lua
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
-- 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 ts_query = vim.treesitter.query
|
||||
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_query.get_node_text(node_entry.node, bufnr)
|
||||
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_query.get_node_text(node, bufnr)
|
||||
|
||||
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_query.get_node_text(node, bufnr)
|
||||
|
||||
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_query.get_node_text(match.reference.node, bufnr) == node_text
|
||||
then
|
||||
local def_node, _, kind = M.find_definition(match.reference.node, bufnr)
|
||||
|
||||
if kind == nil or def_node == node then
|
||||
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
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
local echo = vim.api.nvim_echo
|
||||
|
||||
-- TODO(lewis6991): write these out to a file
|
||||
local messages = {} --- @type {[1]: string, [2]: string?, [3]: string}[]
|
||||
|
||||
local sev_to_hl = {
|
||||
trace = 'DiagnosticHint',
|
||||
debug = 'Normal',
|
||||
info = 'MoreMsg',
|
||||
warn = 'WarningMsg',
|
||||
error = 'ErrorMsg',
|
||||
}
|
||||
|
||||
---@param ctx string?
|
||||
---@return string
|
||||
local function mkpfx(ctx)
|
||||
return ctx and string.format('[nvim-treesitter/%s]', ctx) or '[nvim-treesitter]'
|
||||
end
|
||||
|
||||
---@class TSLogModule
|
||||
---@field trace fun(fmt: string, ...: any)
|
||||
---@field debug fun(fmt: string, ...: any)
|
||||
---@field info fun(fmt: string, ...: any)
|
||||
---@field warn fun(fmt: string, ...: any)
|
||||
---@field error fun(fmt: string, ...: any)
|
||||
local M = {}
|
||||
|
||||
---@class Logger
|
||||
---@field ctx? string
|
||||
local Logger = {}
|
||||
|
||||
M.Logger = Logger
|
||||
|
||||
---@param ctx? string
|
||||
---@return Logger
|
||||
function M.new(ctx)
|
||||
return setmetatable({ ctx = ctx }, { __index = Logger })
|
||||
end
|
||||
|
||||
---@param m string
|
||||
---@param ... any
|
||||
function Logger:trace(m, ...)
|
||||
messages[#messages + 1] = { 'trace', self.ctx, m:format(...) }
|
||||
end
|
||||
|
||||
---@param m string
|
||||
---@param ... any
|
||||
function Logger:debug(m, ...)
|
||||
messages[#messages + 1] = { 'debug', self.ctx, m:format(...) }
|
||||
end
|
||||
|
||||
---@param m string
|
||||
---@param ... any
|
||||
function Logger:info(m, ...)
|
||||
local m1 = m:format(...)
|
||||
messages[#messages + 1] = { 'info', self.ctx, m1 }
|
||||
echo({ { mkpfx(self.ctx) .. ': ' .. m1, sev_to_hl.info } }, true, {})
|
||||
end
|
||||
|
||||
---@param m string
|
||||
---@param ... any
|
||||
function Logger:warn(m, ...)
|
||||
local m1 = m:format(...)
|
||||
messages[#messages + 1] = { 'warn', self.ctx, m1 }
|
||||
echo({ { mkpfx(self.ctx) .. ' warning: ' .. m1, sev_to_hl.warn } }, true, {})
|
||||
end
|
||||
|
||||
---@param m string
|
||||
---@param ... any
|
||||
---@return string
|
||||
function Logger:error(m, ...)
|
||||
local m1 = m:format(...)
|
||||
messages[#messages + 1] = { 'error', self.ctx, m1 }
|
||||
echo({ { mkpfx(self.ctx) .. ' error: ' .. m1, sev_to_hl.error } }, true, {})
|
||||
return m1
|
||||
end
|
||||
|
||||
local noctx_logger = M.new()
|
||||
|
||||
setmetatable(M, {
|
||||
__index = function(t, k)
|
||||
--- @diagnostic disable-next-line:no-unknown
|
||||
t[k] = function(...)
|
||||
return noctx_logger[k](noctx_logger, ...)
|
||||
end
|
||||
return t[k]
|
||||
end,
|
||||
})
|
||||
|
||||
function M.show()
|
||||
for _, l in ipairs(messages) do
|
||||
local sev, ctx, msg = l[1], l[2], l[3]
|
||||
local hl = sev_to_hl[sev]
|
||||
local text = ctx and string.format('%s(%s): %s', sev, ctx, msg)
|
||||
or string.format('%s: %s', sev, msg)
|
||||
echo({ { text, hl } }, false, {})
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
File diff suppressed because it is too large
Load diff
433
lua/nvim-treesitter/query.lua
Normal file
433
lua/nvim-treesitter/query.lua
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
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.
|
||||
---@param query string
|
||||
---@return function(string): boolean
|
||||
local function get_query_guard(query)
|
||||
return function(lang)
|
||||
return M.has_query_files(lang, query)
|
||||
end
|
||||
end
|
||||
|
||||
for _, query in ipairs(M.built_in_query_groups) do
|
||||
M["has_" .. query] = get_query_guard(query)
|
||||
end
|
||||
|
||||
---@return string[]
|
||||
function M.available_query_groups()
|
||||
local query_files = api.nvim_get_runtime_file("queries/*/*.scm", true)
|
||||
local groups = {}
|
||||
for _, f in ipairs(query_files) do
|
||||
groups[vim.fn.fnamemodify(f, ":t:r")] = true
|
||||
end
|
||||
local list = {}
|
||||
for k, _ in pairs(groups) do
|
||||
table.insert(list, k)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
do
|
||||
local query_cache = caching.create_buffer_cache()
|
||||
|
||||
local function update_cached_matches(bufnr, changed_tick, query_group)
|
||||
query_cache.set(query_group, bufnr, {
|
||||
tick = changed_tick,
|
||||
cache = M.collect_group_results(bufnr, query_group) or {},
|
||||
})
|
||||
end
|
||||
|
||||
function M.get_matches(bufnr, query_group)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
local cached_local = query_cache.get(query_group, bufnr)
|
||||
if not cached_local or api.nvim_buf_get_changedtick(bufnr) > cached_local.tick then
|
||||
update_cached_matches(bufnr, api.nvim_buf_get_changedtick(bufnr), query_group)
|
||||
end
|
||||
|
||||
return query_cache.get(query_group, bufnr).cache
|
||||
end
|
||||
end
|
||||
|
||||
---@param lang string
|
||||
---@param query_name string
|
||||
---@return string[]
|
||||
local function runtime_queries(lang, query_name)
|
||||
return api.nvim_get_runtime_file(string.format("queries/%s/%s.scm", lang, query_name), true) or {}
|
||||
end
|
||||
|
||||
---@type table<string, table<string, boolean>>
|
||||
local query_files_cache = {}
|
||||
|
||||
---@param lang string
|
||||
---@param query_name string
|
||||
---@return boolean
|
||||
function M.has_query_files(lang, query_name)
|
||||
if not query_files_cache[lang] then
|
||||
query_files_cache[lang] = {}
|
||||
end
|
||||
if query_files_cache[lang][query_name] == nil then
|
||||
local files = runtime_queries(lang, query_name)
|
||||
query_files_cache[lang][query_name] = files and #files > 0
|
||||
end
|
||||
return query_files_cache[lang][query_name]
|
||||
end
|
||||
|
||||
do
|
||||
local mt = {}
|
||||
mt.__index = function(tbl, key)
|
||||
if rawget(tbl, key) == nil then
|
||||
rawset(tbl, key, {})
|
||||
end
|
||||
return rawget(tbl, key)
|
||||
end
|
||||
|
||||
-- cache will auto set the table for each lang if it is nil
|
||||
local cache = setmetatable({}, mt)
|
||||
|
||||
--- Same as `vim.treesitter.query` except will return cached values
|
||||
---@param lang string
|
||||
---@param query_name string
|
||||
function M.get_query(lang, query_name)
|
||||
if cache[lang][query_name] == nil then
|
||||
cache[lang][query_name] = 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
|
||||
---@param lang string
|
||||
---@param query_name string
|
||||
function M.invalidate_query_cache(lang, query_name)
|
||||
if lang and query_name then
|
||||
cache[lang][query_name] = nil
|
||||
if query_files_cache[lang] then
|
||||
query_files_cache[lang][query_name] = nil
|
||||
end
|
||||
elseif lang and not query_name then
|
||||
query_files_cache[lang] = nil
|
||||
for query_name0, _ in pairs(cache[lang]) do
|
||||
M.invalidate_query_cache(lang, query_name0)
|
||||
end
|
||||
elseif not lang and not query_name then
|
||||
query_files_cache = {}
|
||||
for lang0, _ in pairs(cache) do
|
||||
for query_name0, _ in pairs(cache[lang0]) do
|
||||
M.invalidate_query_cache(lang0, query_name0)
|
||||
end
|
||||
end
|
||||
else
|
||||
error "Cannot have query_name by itself!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- This function is meant for an autocommand and not to be used. Only use if file is a query file.
|
||||
---@param fname string
|
||||
function M.invalidate_query_file(fname)
|
||||
local fnamemodify = vim.fn.fnamemodify
|
||||
M.invalidate_query_cache(fnamemodify(fname, ":p:h:t"), fnamemodify(fname, ":t:r"))
|
||||
end
|
||||
|
||||
---@class QueryInfo
|
||||
---@field root LanguageTree
|
||||
---@field source integer
|
||||
---@field start integer
|
||||
---@field stop integer
|
||||
|
||||
---@param bufnr integer
|
||||
---@param query_name string
|
||||
---@param root LanguageTree
|
||||
---@param root_lang string|nil
|
||||
---@return Query|nil, QueryInfo|nil
|
||||
local function prepare_query(bufnr, query_name, root, root_lang)
|
||||
local buf_lang = parsers.get_buf_lang(bufnr)
|
||||
|
||||
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
|
||||
|
||||
---@param query Query
|
||||
---@param bufnr integer
|
||||
---@param start_row integer
|
||||
---@param end_row integer
|
||||
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, metadata = 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)
|
||||
local metadata_path = split(name .. ".metadata")
|
||||
insert_to_path(prepared_match, metadata_path, metadata[id])
|
||||
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 integer the buffer
|
||||
---@param captures string|string[]
|
||||
---@param query_group string the name of query group (highlights or injections for example)
|
||||
---@param root LanguageTree|nil node from where to start the search
|
||||
---@param lang string|nil the language from where to get the captures.
|
||||
--- Root nodes can have several languages.
|
||||
---@return table|nil
|
||||
function M.get_capture_matches(bufnr, captures, query_group, root, lang)
|
||||
if type(captures) == "string" then
|
||||
captures = { captures }
|
||||
end
|
||||
local strip_captures = {}
|
||||
for i, capture in ipairs(captures) do
|
||||
if capture:sub(1, 1) ~= "@" then
|
||||
error 'Captures must start with "@"'
|
||||
return
|
||||
end
|
||||
-- Remove leading "@".
|
||||
strip_captures[i] = capture:sub(2)
|
||||
end
|
||||
|
||||
local matches = {}
|
||||
for match in M.iter_group_results(bufnr, query_group, root, lang) do
|
||||
for _, capture in ipairs(strip_captures) do
|
||||
local insert = utils.get_at_path(match, capture)
|
||||
if insert then
|
||||
table.insert(matches, insert)
|
||||
end
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end
|
||||
|
||||
function M.iter_captures(bufnr, query_name, root, lang)
|
||||
local query, params = prepare_query(bufnr, query_name, root, lang)
|
||||
if not query then
|
||||
return EMPTY_ITER
|
||||
end
|
||||
assert(params)
|
||||
|
||||
local iter = query:iter_captures(params.root, params.source, params.start, params.stop)
|
||||
|
||||
local function wrapped_iter()
|
||||
local id, node, metadata = iter()
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
|
||||
local name = query.captures[id]
|
||||
if string.sub(name, 1, 1) == "_" then
|
||||
return wrapped_iter()
|
||||
end
|
||||
|
||||
return name, node, metadata
|
||||
end
|
||||
|
||||
return wrapped_iter
|
||||
end
|
||||
|
||||
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 integer the buffer
|
||||
---@param query_group string the query file to use
|
||||
---@param root LanguageTree the root node
|
||||
---@param root_lang string|nil 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
|
||||
assert(params)
|
||||
|
||||
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
|
||||
|
||||
---@alias CaptureResFn function(string, LanguageTree, LanguageTree): string, string
|
||||
|
||||
--- Same as get_capture_matches except this will recursively get matches for every language in the tree.
|
||||
---@param bufnr integer The bufnr
|
||||
---@param capture_or_fn string|CaptureResFn The capture to get. If a function is provided then that
|
||||
--- function will be used to resolve both the capture and query argument.
|
||||
--- The function can return `nil` to ignore that tree.
|
||||
---@param query_type string The query to get the capture from. This is ignore if a function is provided
|
||||
--- for the captuer argument.
|
||||
function M.get_capture_matches_recursively(bufnr, capture_or_fn, query_type)
|
||||
---@type CaptureResFn
|
||||
local type_fn
|
||||
if type(capture_or_fn) == "function" then
|
||||
type_fn = capture_or_fn
|
||||
else
|
||||
type_fn = function(_, _, _)
|
||||
return capture_or_fn, query_type
|
||||
end
|
||||
end
|
||||
local parser = parsers.get_parser(bufnr)
|
||||
local matches = {}
|
||||
|
||||
if parser then
|
||||
parser:for_each_tree(function(tree, lang_tree)
|
||||
local lang = lang_tree:lang()
|
||||
local capture, type_ = type_fn(lang, tree, lang_tree)
|
||||
|
||||
if capture then
|
||||
vim.list_extend(matches, M.get_capture_matches(bufnr, capture, type_, tree:root(), lang))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
return M
|
||||
188
lua/nvim-treesitter/query_predicates.lua
Normal file
188
lua/nvim-treesitter/query_predicates.lua
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
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)
|
||||
|
||||
query.add_directive("exclude_children!", function(match, _pattern, _bufnr, pred, metadata)
|
||||
local capture_id = pred[2]
|
||||
local node = match[capture_id]
|
||||
local start_row, start_col, end_row, end_col = node:range()
|
||||
local ranges = {}
|
||||
for i = 0, node:named_child_count() - 1 do
|
||||
local child = node:named_child(i)
|
||||
local child_start_row, child_start_col, child_end_row, child_end_col = child:range()
|
||||
if child_start_row > start_row or child_start_col > start_col then
|
||||
table.insert(ranges, {
|
||||
start_row,
|
||||
start_col,
|
||||
child_start_row,
|
||||
child_start_col,
|
||||
})
|
||||
end
|
||||
start_row = child_end_row
|
||||
start_col = child_end_col
|
||||
end
|
||||
if end_row > start_row or end_col > start_col then
|
||||
table.insert(ranges, { start_row, start_col, end_row, end_col })
|
||||
end
|
||||
metadata.content = ranges
|
||||
end)
|
||||
|
||||
-- Trim blank lines from end of the region
|
||||
-- Arguments are the captures to trim.
|
||||
query.add_directive("trim!", function(match, _, bufnr, pred, metadata)
|
||||
for _, id in ipairs { select(2, unpack(pred)) } do
|
||||
local node = match[id]
|
||||
local start_row, start_col, end_row, end_col = node:range()
|
||||
|
||||
-- Don't trim if region ends in middle of a line
|
||||
if end_col ~= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
while true do
|
||||
-- As we only care when end_col == 0, always inspect one line above end_row.
|
||||
local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
|
||||
|
||||
if end_line ~= "" then
|
||||
break
|
||||
end
|
||||
|
||||
end_row = end_row - 1
|
||||
end
|
||||
|
||||
-- If this produces an invalid range, we just skip it.
|
||||
if start_row < end_row or (start_row == end_row and start_col <= end_col) then
|
||||
if not metadata[id] then
|
||||
metadata[id] = {}
|
||||
end
|
||||
metadata[id].range = { start_row, start_col, end_row, end_col }
|
||||
end
|
||||
end
|
||||
end)
|
||||
288
lua/nvim-treesitter/shell_command_selectors.lua
Normal file
288
lua/nvim-treesitter/shell_command_selectors.lua
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
local fn = vim.fn
|
||||
local utils = require "nvim-treesitter.utils"
|
||||
|
||||
-- Convert path for cmd.exe on Windows.
|
||||
-- This is needed when vim.opt.shellslash is in use.
|
||||
local function cmdpath(p)
|
||||
if vim.opt.shellslash:get() then
|
||||
local r = p:gsub("/", "\\")
|
||||
return r
|
||||
else
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.select_mkdir_cmd(directory, cwd, info_msg)
|
||||
if fn.has "win32" == 1 then
|
||||
return {
|
||||
cmd = "cmd",
|
||||
opts = {
|
||||
args = { "/C", "mkdir", cmdpath(directory) },
|
||||
cwd = cwd,
|
||||
},
|
||||
info = info_msg,
|
||||
err = "Could not create " .. directory,
|
||||
}
|
||||
else
|
||||
return {
|
||||
cmd = "mkdir",
|
||||
opts = {
|
||||
args = { directory },
|
||||
cwd = cwd,
|
||||
},
|
||||
info = info_msg,
|
||||
err = "Could not create " .. directory,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function M.select_rm_file_cmd(file, info_msg)
|
||||
if fn.has "win32" == 1 then
|
||||
return {
|
||||
cmd = "cmd",
|
||||
opts = {
|
||||
args = { "/C", "if", "exist", cmdpath(file), "del", cmdpath(file) },
|
||||
},
|
||||
info = info_msg,
|
||||
err = "Could not delete " .. file,
|
||||
}
|
||||
else
|
||||
return {
|
||||
cmd = "rm",
|
||||
opts = {
|
||||
args = { file },
|
||||
},
|
||||
info = info_msg,
|
||||
err = "Could not delete " .. file,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
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",
|
||||
}
|
||||
if
|
||||
#vim.tbl_filter(function(file)
|
||||
local ext = vim.fn.fnamemodify(file, ":e")
|
||||
return ext == "cc" or ext == "cpp" or ext == "cxx"
|
||||
end, repo.files) > 0
|
||||
then
|
||||
table.insert(args, "-lstdc++")
|
||||
end
|
||||
if fn.has "win32" == 0 then
|
||||
table.insert(args, "-fPIC")
|
||||
end
|
||||
return args
|
||||
end
|
||||
end
|
||||
|
||||
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", cmdpath(dir), "rmdir", "/s", "/q", cmdpath(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", cmdpath(from), cmdpath(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$", "")
|
||||
|
||||
local folder_rev = revision
|
||||
if is_github and revision:match "^v%d" then
|
||||
folder_rev = revision:sub(2)
|
||||
end
|
||||
|
||||
return {
|
||||
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"),
|
||||
{
|
||||
cmd = "curl",
|
||||
info = "Downloading " .. project_name .. "...",
|
||||
err = "Error during download, please verify your internet connection",
|
||||
opts = {
|
||||
args = {
|
||||
"--silent",
|
||||
"-L", -- follow redirects
|
||||
is_github and url .. "/archive/" .. revision .. ".tar.gz"
|
||||
or url .. "/-/archive/" .. revision .. "/" .. project_name .. "-" .. revision .. ".tar.gz",
|
||||
"--output",
|
||||
project_name .. ".tar.gz",
|
||||
},
|
||||
cwd = cache_folder,
|
||||
},
|
||||
},
|
||||
M.select_mkdir_cmd(project_name .. "-tmp", cache_folder, "Creating temporary directory"),
|
||||
{
|
||||
cmd = "tar",
|
||||
info = "Extracting " .. project_name .. "...",
|
||||
err = "Error during tarball extraction.",
|
||||
opts = {
|
||||
args = {
|
||||
"-xvzf",
|
||||
project_name .. ".tar.gz",
|
||||
"-C",
|
||||
project_name .. "-tmp",
|
||||
},
|
||||
cwd = cache_folder,
|
||||
},
|
||||
},
|
||||
M.select_rm_file_cmd(cache_folder .. path_sep .. project_name .. ".tar.gz"),
|
||||
M.select_mv_cmd(
|
||||
utils.join_path(project_name .. "-tmp", url:match "[^/]-$" .. "-" .. folder_rev),
|
||||
project_name,
|
||||
cache_folder
|
||||
),
|
||||
M.select_install_rm_cmd(cache_folder, project_name .. "-tmp"),
|
||||
}
|
||||
else
|
||||
local git_folder = utils.join_path(cache_folder, project_name)
|
||||
local clone_error = "Error during download, please verify your internet connection"
|
||||
|
||||
return {
|
||||
{
|
||||
cmd = "git",
|
||||
info = "Downloading " .. project_name .. "...",
|
||||
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", cmdpath(dir), command)
|
||||
else
|
||||
return string.format("cd %s;\n %s", dir, command)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
50
lua/nvim-treesitter/statusline.lua
Normal file
50
lua/nvim-treesitter/statusline.lua
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
local parsers = require "nvim-treesitter.parsers"
|
||||
local ts_utils = require "nvim-treesitter.ts_utils"
|
||||
|
||||
local M = {}
|
||||
|
||||
-- Trim spaces and opening brackets from end
|
||||
local transform_line = function(line)
|
||||
return line:gsub("%s*[%[%(%{]*%s*$", "")
|
||||
end
|
||||
|
||||
function M.statusline(opts)
|
||||
if not parsers.has_parser() then
|
||||
return
|
||||
end
|
||||
local options = opts or {}
|
||||
if type(opts) == "number" then
|
||||
options = { indicator_size = opts }
|
||||
end
|
||||
local bufnr = options.bufnr or 0
|
||||
local indicator_size = options.indicator_size or 100
|
||||
local type_patterns = options.type_patterns or { "class", "function", "method" }
|
||||
local transform_fn = options.transform_fn or transform_line
|
||||
local separator = options.separator or " -> "
|
||||
|
||||
local current_node = ts_utils.get_node_at_cursor()
|
||||
if not current_node then
|
||||
return ""
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
local expr = current_node
|
||||
|
||||
while expr do
|
||||
local line = ts_utils._get_line_for_node(expr, type_patterns, transform_fn, bufnr)
|
||||
if line ~= "" 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
|
||||
454
lua/nvim-treesitter/ts_utils.lua
Normal file
454
lua/nvim-treesitter/ts_utils.lua
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
local api = vim.api
|
||||
|
||||
local parsers = require "nvim-treesitter.parsers"
|
||||
local utils = require "nvim-treesitter.utils"
|
||||
local ts = vim.treesitter
|
||||
|
||||
local M = {}
|
||||
|
||||
local function get_node_text(node, bufnr)
|
||||
bufnr = bufnr or api.nvim_get_current_buf()
|
||||
if not node then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- We have to remember that end_col is end-exclusive
|
||||
local start_row, start_col, end_row, end_col = ts.get_node_range(node)
|
||||
|
||||
if start_row ~= end_row then
|
||||
local lines = api.nvim_buf_get_lines(bufnr, start_row, end_row + 1, false)
|
||||
if next(lines) == nil then
|
||||
return {}
|
||||
end
|
||||
lines[1] = string.sub(lines[1], start_col + 1)
|
||||
-- end_row might be just after the last line. In this case the last line is not truncated.
|
||||
if #lines == end_row - start_row + 1 then
|
||||
lines[#lines] = string.sub(lines[#lines], 1, end_col)
|
||||
end
|
||||
return lines
|
||||
else
|
||||
local line = api.nvim_buf_get_lines(bufnr, start_row, start_row + 1, false)[1]
|
||||
-- If line is nil then the line is empty
|
||||
return line and { string.sub(line, start_col + 1, end_col) } or {}
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
function M._get_line_for_node(node, type_patterns, transform_fn, bufnr)
|
||||
local node_type = node:type()
|
||||
local is_valid = false
|
||||
for _, rgx in ipairs(type_patterns) do
|
||||
if node_type:find(rgx) then
|
||||
is_valid = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not is_valid then
|
||||
return ""
|
||||
end
|
||||
local line = transform_fn(vim.trim(get_node_text(node, bufnr)[1] or ""))
|
||||
-- Escape % to avoid statusline to evaluate content as expression
|
||||
return line:gsub("%%", "%%%%")
|
||||
end
|
||||
|
||||
--- Gets the actual text content of a node
|
||||
-- @deprecated Use vim.treesitter.query.get_node_text
|
||||
-- @param node the node to get the text from
|
||||
-- @param bufnr the buffer containing the node
|
||||
-- @return list of lines of text of the node
|
||||
function M.get_node_text(node, bufnr)
|
||||
vim.notify_once(
|
||||
"nvim-treesitter.ts_utils.get_node_text is deprecated: use vim.treesitter.query.get_node_text",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
return get_node_text(node, bufnr)
|
||||
end
|
||||
|
||||
--- Determines whether a node is the parent of another
|
||||
-- @param dest the possible parent
|
||||
-- @param source the possible child node
|
||||
function M.is_parent(dest, source)
|
||||
if not (dest and source) then
|
||||
return false
|
||||
end
|
||||
|
||||
local current = source
|
||||
while current ~= nil do
|
||||
if current == dest then
|
||||
return true
|
||||
end
|
||||
|
||||
current = current:parent()
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Get next node with same parent
|
||||
-- @param node 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, ignore_injected_langs)
|
||||
winnr = winnr or 0
|
||||
local cursor = api.nvim_win_get_cursor(winnr)
|
||||
local cursor_range = { cursor[1] - 1, cursor[2] }
|
||||
|
||||
local buf = vim.api.nvim_win_get_buf(winnr)
|
||||
local root_lang_tree = parsers.get_parser(buf)
|
||||
if not root_lang_tree then
|
||||
return
|
||||
end
|
||||
|
||||
local root
|
||||
if ignore_injected_langs then
|
||||
for _, tree in ipairs(root_lang_tree:trees()) do
|
||||
local tree_root = tree:root()
|
||||
if tree_root and ts.is_in_node_range(tree_root, cursor_range[1], cursor_range[2]) then
|
||||
root = tree_root
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
root = M.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree)
|
||||
end
|
||||
|
||||
if not root then
|
||||
return
|
||||
end
|
||||
|
||||
return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2])
|
||||
end
|
||||
|
||||
function M.get_root_for_position(line, col, root_lang_tree)
|
||||
if not root_lang_tree then
|
||||
if not parsers.has_parser() then
|
||||
return
|
||||
end
|
||||
|
||||
root_lang_tree = parsers.get_parser()
|
||||
end
|
||||
|
||||
local lang_tree = root_lang_tree:language_for_range { line, col, line, col }
|
||||
|
||||
for _, tree in ipairs(lang_tree:trees()) do
|
||||
local root = tree:root()
|
||||
|
||||
if root and ts.is_in_node_range(root, line, col) then
|
||||
return root, tree, lang_tree
|
||||
end
|
||||
end
|
||||
|
||||
-- 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
|
||||
ecol = math.max(ecol, 1)
|
||||
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)
|
||||
local start_row, start_col, end_row, end_col = M.get_vim_range({ ts.get_node_range(node) }, buf)
|
||||
|
||||
local v_table = { charwise = "v", linewise = "V", blockwise = "<C-v>" }
|
||||
selection_mode = selection_mode or "charwise"
|
||||
|
||||
-- Normalise selection_mode
|
||||
if vim.tbl_contains(vim.tbl_keys(v_table), selection_mode) then
|
||||
selection_mode = v_table[selection_mode]
|
||||
end
|
||||
|
||||
-- enter visual mode if normal or operator-pending (no) mode
|
||||
-- Why? According to https://learnvimscriptthehardway.stevelosh.com/chapters/15.html
|
||||
-- If your operator-pending mapping ends with some text visually selected, Vim will operate on that text.
|
||||
-- Otherwise, Vim will operate on the text between the original cursor position and the new position.
|
||||
local mode = api.nvim_get_mode()
|
||||
if mode.mode ~= selection_mode then
|
||||
-- Call to `nvim_replace_termcodes()` is needed for sending appropriate command to enter blockwise mode
|
||||
selection_mode = vim.api.nvim_replace_termcodes(selection_mode, true, true, true)
|
||||
api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {})
|
||||
end
|
||||
|
||||
api.nvim_win_set_cursor(0, { start_row, start_col - 1 })
|
||||
vim.cmd "normal! o"
|
||||
api.nvim_win_set_cursor(0, { end_row, end_col - 1 })
|
||||
end
|
||||
|
||||
-- Byte length of node range
|
||||
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
|
||||
--- @deprecated Use `vim.treesitter.is_in_node_range()` instead
|
||||
-- @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)
|
||||
vim.notify_once(
|
||||
"nvim-treesitter.ts_utils.is_in_node_range is deprecated: use vim.treesitter.is_in_node_range",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
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
|
||||
|
||||
--- @deprecated Use `vim.treesitter.get_node_range()` instead
|
||||
function M.get_node_range(node_or_range)
|
||||
vim.notify_once(
|
||||
"nvim-treesitter.ts_utils.get_node_range is deprecated: use vim.treesitter.get_node_range",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
return ts.get_node_range(node_or_range)
|
||||
end
|
||||
|
||||
function M.node_to_lsp_range(node)
|
||||
local start_line, start_col, end_line, end_col = ts.get_node_range(node)
|
||||
local rtn = {}
|
||||
rtn.start = { line = start_line, character = start_col }
|
||||
rtn["end"] = { line = end_line, character = end_col }
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Memoizes a function based on the buffer tick of the provided bufnr.
|
||||
-- The cache entry is cleared when the buffer is detached to avoid memory leaks.
|
||||
-- @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 = get_node_text(node_or_range1, bufnr)
|
||||
local text2 = get_node_text(node_or_range2, bufnr)
|
||||
|
||||
local edit1 = { range = range1, newText = table.concat(text2, "\n") }
|
||||
local edit2 = { range = range2, newText = table.concat(text1, "\n") }
|
||||
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
|
||||
154
lua/nvim-treesitter/tsrange.lua
Normal file
154
lua/nvim-treesitter/tsrange.lua
Normal 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
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
--- @param filename string
|
||||
--- @return string
|
||||
function M.read_file(filename)
|
||||
local file = assert(io.open(filename, 'r'))
|
||||
local r = file:read('*a')
|
||||
file:close()
|
||||
return r
|
||||
end
|
||||
|
||||
--- @param filename string
|
||||
--- @param content string
|
||||
function M.write_file(filename, content)
|
||||
local file = assert(io.open(filename, 'w'))
|
||||
file:write(content)
|
||||
file:close()
|
||||
end
|
||||
|
||||
return M
|
||||
219
lua/nvim-treesitter/utils.lua
Normal file
219
lua/nvim-treesitter/utils.lua
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
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
|
||||
|
||||
-- Returns the system specific path seperator.
|
||||
---@return string
|
||||
function M.get_path_sep()
|
||||
return (fn.has "win32" == 1 and not vim.opt.shellslash:get()) and "\\" or "/"
|
||||
end
|
||||
|
||||
-- Returns a function that joins the given arguments with separator. Arguments
|
||||
-- can't be nil. Example:
|
||||
--[[
|
||||
print(M.generate_join(" ")("foo", "bar"))
|
||||
--]]
|
||||
-- prints "foo bar"
|
||||
function M.generate_join(separator)
|
||||
return function(...)
|
||||
return table.concat({ ... }, separator)
|
||||
end
|
||||
end
|
||||
|
||||
M.join_path = M.generate_join(M.get_path_sep())
|
||||
|
||||
M.join_space = M.generate_join " "
|
||||
|
||||
--- 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!",
|
||||
"-bar",
|
||||
def.args,
|
||||
command_name,
|
||||
call_fn,
|
||||
}
|
||||
api.nvim_command(table.concat(parts, " "))
|
||||
end
|
||||
end
|
||||
|
||||
---@param dir string
|
||||
---@param create_err string
|
||||
---@param writeable_err string
|
||||
---@return string|nil, string|nil
|
||||
function M.create_or_reuse_writable_dir(dir, create_err, writeable_err)
|
||||
create_err = create_err or M.join_space("Could not create dir '", dir, "': ")
|
||||
writeable_err = writeable_err or M.join_space("Invalid rights, '", dir, "' should be read/write")
|
||||
-- Try creating and using parser_dir if it doesn't exist
|
||||
if not luv.fs_stat(dir) then
|
||||
local ok, error = pcall(vim.fn.mkdir, dir, "p", "0755")
|
||||
if not ok then
|
||||
return nil, M.join_space(create_err, error)
|
||||
end
|
||||
|
||||
return dir
|
||||
end
|
||||
|
||||
-- parser_dir exists, use it if it's read/write
|
||||
if luv.fs_access(dir, "RW") then
|
||||
return dir
|
||||
end
|
||||
|
||||
-- parser_dir exists but isn't read/write, give up
|
||||
return nil, M.join_space(writeable_err, dir, "'")
|
||||
end
|
||||
|
||||
function M.get_package_path()
|
||||
-- Path to this source file, removing the leading '@'
|
||||
local source = string.sub(debug.getinfo(1, "S").source, 2)
|
||||
|
||||
-- Path to the package root
|
||||
return fn.fnamemodify(source, ":p:h:h:h")
|
||||
end
|
||||
|
||||
function M.get_cache_dir()
|
||||
local cache_dir = fn.stdpath "data"
|
||||
|
||||
if luv.fs_access(cache_dir, "RW") then
|
||||
return cache_dir
|
||||
elseif luv.fs_access("/tmp", "RW") then
|
||||
return "/tmp"
|
||||
end
|
||||
|
||||
return nil, M.join_space("Invalid cache rights,", fn.stdpath "data", "or /tmp should be read/write")
|
||||
end
|
||||
|
||||
-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in
|
||||
-- runtimepath
|
||||
function M.get_site_dir()
|
||||
return M.join_path(fn.stdpath "data", "site")
|
||||
end
|
||||
|
||||
-- Gets a property at path
|
||||
-- @param tbl 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
|
||||
|
||||
---@return string|nil
|
||||
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
|
||||
2
parser-info/.gitignore
vendored
Normal file
2
parser-info/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
2
parser/.gitignore
vendored
Normal file
2
parser/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
local filetypes = {
|
||||
angular = { 'htmlangular' },
|
||||
bash = { 'sh' },
|
||||
bibtex = { 'bib' },
|
||||
c_sharp = { 'cs', 'csharp' },
|
||||
commonlisp = { 'lisp' },
|
||||
cooklang = { 'cook' },
|
||||
devicetree = { 'dts' },
|
||||
diff = { 'gitdiff' },
|
||||
eex = { 'eelixir' },
|
||||
elixir = { 'ex' },
|
||||
embedded_template = { 'eruby' },
|
||||
erlang = { 'erl' },
|
||||
facility = { 'fsd' },
|
||||
faust = { 'dsp' },
|
||||
gdshader = { 'gdshaderinc' },
|
||||
git_config = { 'gitconfig' },
|
||||
git_rebase = { 'gitrebase' },
|
||||
glimmer = { 'handlebars', 'html.handlebars' },
|
||||
godot_resource = { 'gdresource' },
|
||||
haskell = { 'hs' },
|
||||
haskell_persistent = { 'haskellpersistent' },
|
||||
idris = { 'idris2' },
|
||||
ini = { 'confini', 'dosini' },
|
||||
janet_simple = { 'janet' },
|
||||
javascript = { 'javascriptreact', 'ecma', 'ecmascript', 'jsx', 'js' },
|
||||
json = { 'jsonc' },
|
||||
glimmer_javascript = { 'javascript.glimmer' },
|
||||
latex = { 'tex' },
|
||||
linkerscript = { 'ld' },
|
||||
m68k = { 'asm68k' },
|
||||
make = { 'automake' },
|
||||
markdown = { 'pandoc' },
|
||||
muttrc = { 'neomuttrc' },
|
||||
ocaml_interface = { 'ocamlinterface' },
|
||||
perl = { 'pl' },
|
||||
poe_filter = { 'poefilter' },
|
||||
powershell = { 'ps1' },
|
||||
properties = { 'jproperties' },
|
||||
python = { 'py', 'gyp' },
|
||||
qmljs = { 'qml' },
|
||||
runescript = { 'clientscript' },
|
||||
scala = { 'sbt' },
|
||||
slang = { 'shaderslang' },
|
||||
sqp = { 'mysqp' },
|
||||
ssh_config = { 'sshconfig' },
|
||||
starlark = { 'bzl' },
|
||||
surface = { 'sface' },
|
||||
systemverilog = { 'verilog' },
|
||||
t32 = { 'trace32' },
|
||||
tcl = { 'expect' },
|
||||
terraform = { 'terraform-vars' },
|
||||
textproto = { 'pbtxt' },
|
||||
tlaplus = { 'tla' },
|
||||
tsx = { 'typescriptreact', 'typescript.tsx' },
|
||||
typescript = { 'ts' },
|
||||
glimmer_typescript = { 'typescript.glimmer' },
|
||||
typst = { 'typ' },
|
||||
udev = { 'udevrules' },
|
||||
uxntal = { 'tal', 'uxn' },
|
||||
v = { 'vlang' },
|
||||
vhs = { 'tape' },
|
||||
xml = { 'xsd', 'xslt', 'svg' },
|
||||
xresources = { 'xdefaults' },
|
||||
}
|
||||
|
||||
for lang, ft in pairs(filetypes) do
|
||||
vim.treesitter.language.register(lang, ft)
|
||||
end
|
||||
|
|
@ -1,75 +1,34 @@
|
|||
-- Last Change: 2022 Apr 16
|
||||
|
||||
if vim.g.loaded_nvim_treesitter then
|
||||
return
|
||||
end
|
||||
vim.g.loaded_nvim_treesitter = true
|
||||
|
||||
-- setup modules
|
||||
require("nvim-treesitter").setup()
|
||||
|
||||
local api = vim.api
|
||||
|
||||
local function complete_available_parsers(arglead)
|
||||
return vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
return v:find(arglead) ~= nil
|
||||
end,
|
||||
require('nvim-treesitter.config').get_available()
|
||||
)
|
||||
end
|
||||
-- define autocommands
|
||||
local augroup = api.nvim_create_augroup("NvimTreesitter", {})
|
||||
|
||||
local function complete_installed_parsers(arglead)
|
||||
return vim.tbl_filter(
|
||||
--- @param v string
|
||||
function(v)
|
||||
return v:find(arglead) ~= nil
|
||||
end,
|
||||
require('nvim-treesitter.config').get_installed()
|
||||
)
|
||||
end
|
||||
|
||||
-- create user commands
|
||||
api.nvim_create_user_command('TSInstall', function(args)
|
||||
require('nvim-treesitter.install').install(args.fargs, { force = args.bang, summary = true })
|
||||
end, {
|
||||
nargs = '+',
|
||||
bang = true,
|
||||
bar = true,
|
||||
complete = complete_available_parsers,
|
||||
desc = 'Install treesitter parsers',
|
||||
})
|
||||
|
||||
api.nvim_create_user_command('TSInstallFromGrammar', function(args)
|
||||
require('nvim-treesitter.install').install(args.fargs, {
|
||||
generate = true,
|
||||
summary = true,
|
||||
force = args.bang,
|
||||
})
|
||||
end, {
|
||||
nargs = '+',
|
||||
bang = true,
|
||||
bar = true,
|
||||
complete = complete_available_parsers,
|
||||
desc = 'Install treesitter parsers from grammar',
|
||||
})
|
||||
|
||||
api.nvim_create_user_command('TSUpdate', function(args)
|
||||
require('nvim-treesitter.install').update(args.fargs, { summary = true })
|
||||
end, {
|
||||
nargs = '*',
|
||||
bar = true,
|
||||
complete = complete_installed_parsers,
|
||||
desc = 'Update installed treesitter parsers',
|
||||
})
|
||||
|
||||
api.nvim_create_user_command('TSUninstall', function(args)
|
||||
require('nvim-treesitter.install').uninstall(args.fargs, { summary = true })
|
||||
end, {
|
||||
nargs = '+',
|
||||
bar = true,
|
||||
complete = complete_installed_parsers,
|
||||
desc = 'Uninstall treesitter parsers',
|
||||
})
|
||||
|
||||
api.nvim_create_user_command('TSLog', function()
|
||||
require('nvim-treesitter.log').show()
|
||||
end, {
|
||||
desc = 'View log messages',
|
||||
api.nvim_create_autocmd("Filetype", {
|
||||
pattern = "query",
|
||||
group = augroup,
|
||||
callback = function()
|
||||
api.nvim_clear_autocmds {
|
||||
group = augroup,
|
||||
event = "BufWritePost",
|
||||
}
|
||||
api.nvim_create_autocmd("BufWritePost", {
|
||||
group = augroup,
|
||||
buffer = 0,
|
||||
callback = function(opts)
|
||||
require("nvim-treesitter.query").invalidate_query_file(opts.file)
|
||||
end,
|
||||
desc = "Invalidate query file",
|
||||
})
|
||||
end,
|
||||
desc = "Reload query",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
local query = vim.treesitter.query
|
||||
|
||||
local predicates = {
|
||||
---@param match table<integer,TSNode[]>
|
||||
---@param pred any[]
|
||||
---@param any boolean
|
||||
---@return boolean
|
||||
['kind-eq'] = function(match, pred, any)
|
||||
local nodes = match[pred[2]]
|
||||
if not nodes or #nodes == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local types = { unpack(pred, 3) }
|
||||
for _, node in ipairs(nodes) do
|
||||
local res = vim.list_contains(types, node:type())
|
||||
if any and res then
|
||||
return true
|
||||
elseif not any and not res then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return not any
|
||||
end,
|
||||
}
|
||||
|
||||
-- register custom predicates (overwrite existing; needed for CI)
|
||||
|
||||
---@param match table<integer,TSNode[]>
|
||||
---@param pred any[]
|
||||
---@return boolean
|
||||
query.add_predicate('kind-eq?', function(match, _, _, pred)
|
||||
return predicates['kind-eq'](match, pred, false)
|
||||
end, { force = true })
|
||||
|
||||
---@param match table<integer,TSNode[]>
|
||||
---@param pred any[]
|
||||
---@return boolean
|
||||
query.add_predicate('any-kind-eq?', function(match, _, _, pred)
|
||||
return predicates['kind-eq'](match, pred, true)
|
||||
end, { force = true })
|
||||
13
queries/ada/folds.scm
Normal file
13
queries/ada/folds.scm
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
;; Support for folding in Ada
|
||||
;; za toggles folding a package, subprogram, if statement or loop
|
||||
|
||||
[
|
||||
(package_specification)
|
||||
(package_body)
|
||||
(subprogram_body)
|
||||
(block_statement)
|
||||
(if_statement)
|
||||
(loop_statement)
|
||||
(gnatprep_declarative_if_statement)
|
||||
(gnatprep_if_statement)
|
||||
] @fold
|
||||
179
queries/ada/highlights.scm
Normal file
179
queries/ada/highlights.scm
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
;; highlight queries.
|
||||
;; See the syntax at https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries
|
||||
;; See also https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md#parser-configurations
|
||||
;; for a list of recommended @ tags, though not all of them have matching
|
||||
;; highlights in neovim.
|
||||
|
||||
[
|
||||
"abort"
|
||||
"abs"
|
||||
"abstract"
|
||||
"accept"
|
||||
"access"
|
||||
"all"
|
||||
"array"
|
||||
"at"
|
||||
"begin"
|
||||
"declare"
|
||||
"delay"
|
||||
"delta"
|
||||
"digits"
|
||||
"do"
|
||||
"end"
|
||||
"entry"
|
||||
"exit"
|
||||
"generic"
|
||||
"interface"
|
||||
"is"
|
||||
"limited"
|
||||
"null"
|
||||
"of"
|
||||
"others"
|
||||
"out"
|
||||
"pragma"
|
||||
"private"
|
||||
"range"
|
||||
"synchronized"
|
||||
"tagged"
|
||||
"task"
|
||||
"terminate"
|
||||
"until"
|
||||
"when"
|
||||
] @keyword
|
||||
[
|
||||
"aliased"
|
||||
"constant"
|
||||
"renames"
|
||||
] @storageclass
|
||||
[
|
||||
"mod"
|
||||
"new"
|
||||
"protected"
|
||||
"record"
|
||||
"subtype"
|
||||
"type"
|
||||
] @keyword.type
|
||||
[
|
||||
"with"
|
||||
"use"
|
||||
] @include
|
||||
[
|
||||
"body"
|
||||
"function"
|
||||
"overriding"
|
||||
"procedure"
|
||||
"package"
|
||||
"separate"
|
||||
] @keyword.function
|
||||
[
|
||||
"and"
|
||||
"in"
|
||||
"not"
|
||||
"or"
|
||||
"xor"
|
||||
] @keyword.operator
|
||||
[
|
||||
"while"
|
||||
"loop"
|
||||
"for"
|
||||
"parallel"
|
||||
"reverse"
|
||||
"some"
|
||||
] @repeat
|
||||
[
|
||||
"return"
|
||||
] @keyword.return
|
||||
[
|
||||
"case"
|
||||
"if"
|
||||
"else"
|
||||
"then"
|
||||
"elsif"
|
||||
"select"
|
||||
] @conditional
|
||||
[
|
||||
"exception"
|
||||
"raise"
|
||||
] @exception
|
||||
(comment) @comment
|
||||
(comment) @spell ;; spell-check comments
|
||||
(string_literal) @string
|
||||
(string_literal) @spell ;; spell-check strings
|
||||
(character_literal) @string
|
||||
(numeric_literal) @number
|
||||
|
||||
;; Highlight the name of subprograms
|
||||
(procedure_specification name: (_) @function)
|
||||
(function_specification name: (_) @function)
|
||||
(package_specification name: (_) @function)
|
||||
(package_body name: (_) @function)
|
||||
(generic_instantiation name: (_) @function)
|
||||
|
||||
;; Some keywords should take different categories depending on the context
|
||||
(use_clause "use" @include "type" @include)
|
||||
(with_clause "private" @include)
|
||||
(with_clause "limited" @include)
|
||||
(use_clause (_) @namespace)
|
||||
(with_clause (_) @namespace)
|
||||
|
||||
(loop_statement "end" @keyword.repeat)
|
||||
(if_statement "end" @conditional)
|
||||
(loop_parameter_specification "in" @keyword.repeat)
|
||||
(loop_parameter_specification "in" @keyword.repeat)
|
||||
(iterator_specification ["in" "of"] @keyword.repeat)
|
||||
(range_attribute_designator "range" @keyword.repeat)
|
||||
|
||||
(raise_statement "with" @exception)
|
||||
|
||||
(gnatprep_declarative_if_statement) @preproc
|
||||
(gnatprep_if_statement) @preproc
|
||||
(gnatprep_identifier) @preproc
|
||||
|
||||
(subprogram_declaration "is" @keyword.function "abstract" @keyword.function)
|
||||
(aspect_specification "with" @keyword.function)
|
||||
|
||||
(full_type_declaration "is" @keyword.type)
|
||||
(subtype_declaration "is" @keyword.type)
|
||||
(record_definition "end" @keyword.type)
|
||||
(full_type_declaration (_ "access" @keyword.type))
|
||||
(array_type_definition "array" @keyword.type "of" @keyword.type)
|
||||
(access_to_object_definition "access" @keyword.type)
|
||||
(access_to_object_definition "access" @keyword.type
|
||||
[
|
||||
(general_access_modifier "constant" @keyword.type)
|
||||
(general_access_modifier "all" @keyword.type)
|
||||
]
|
||||
)
|
||||
(range_constraint "range" @keyword.type)
|
||||
(signed_integer_type_definition "range" @keyword.type)
|
||||
(index_subtype_definition "range" @keyword.type)
|
||||
(record_type_definition "abstract" @keyword.type)
|
||||
(record_type_definition "tagged" @keyword.type)
|
||||
(record_type_definition "limited" @keyword.type)
|
||||
(record_type_definition (record_definition "null" @keyword.type))
|
||||
(private_type_declaration "is" @keyword.type "private" @keyword.type)
|
||||
(private_type_declaration "tagged" @keyword.type)
|
||||
(private_type_declaration "limited" @keyword.type)
|
||||
(task_type_declaration "task" @keyword.type "is" @keyword.type)
|
||||
|
||||
;; Gray the body of expression functions
|
||||
(expression_function_declaration
|
||||
(function_specification)
|
||||
"is"
|
||||
(_) @attribute
|
||||
)
|
||||
(subprogram_declaration (aspect_specification) @attribute)
|
||||
|
||||
;; Highlight full subprogram specifications
|
||||
;(subprogram_body
|
||||
; [
|
||||
; (procedure_specification)
|
||||
; (function_specification)
|
||||
; ] @function.spec
|
||||
;)
|
||||
|
||||
;; Highlight errors in red. This is not very useful in practice, as text will
|
||||
;; be highlighted as user types, and the error could be elsewhere in the code.
|
||||
;; This also requires defining :hi @error guifg=Red for instance.
|
||||
(ERROR) @error
|
||||
|
||||
33
queries/ada/locals.scm
Normal file
33
queries/ada/locals.scm
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
;; Better highlighting by referencing to the definition, for variable
|
||||
;; references. However, this is not yet supported by neovim
|
||||
;; See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables
|
||||
|
||||
(compilation) @scope
|
||||
(package_specification) @scope
|
||||
(package_body) @scope
|
||||
(subprogram_declaration) @scope
|
||||
(subprogram_body) @scope
|
||||
(block_statement) @scope
|
||||
|
||||
(with_clause (identifier) @definition.import)
|
||||
(procedure_specification name: (_) @definition.function)
|
||||
(function_specification name: (_) @definition.function)
|
||||
(package_specification name: (_) @definition.var)
|
||||
(package_body name: (_) @definition.var)
|
||||
(generic_instantiation . name: (_) @definition.var)
|
||||
(component_declaration . (identifier) @definition.var)
|
||||
(exception_declaration . (identifier) @definition.var)
|
||||
(formal_object_declaration . (identifier) @definition.var)
|
||||
(object_declaration . (identifier) @definition.var)
|
||||
(parameter_specification . (identifier) @definition.var)
|
||||
(full_type_declaration . (identifier) @definition.type)
|
||||
(private_type_declaration . (identifier) @definition.type)
|
||||
(private_extension_declaration . (identifier) @definition.type)
|
||||
(incomplete_type_declaration . (identifier) @definition.type)
|
||||
(protected_type_declaration . (identifier) @definition.type)
|
||||
(formal_complete_type_declaration . (identifier) @definition.type)
|
||||
(formal_incomplete_type_declaration . (identifier) @definition.type)
|
||||
(task_type_declaration . (identifier) @definition.type)
|
||||
(subtype_declaration . (identifier) @definition.type)
|
||||
|
||||
(identifier) @reference
|
||||
4
queries/agda/folds.scm
Normal file
4
queries/agda/folds.scm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
(record)
|
||||
(module)
|
||||
] @fold
|
||||
82
queries/agda/highlights.scm
Normal file
82
queries/agda/highlights.scm
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
;; Constants
|
||||
(integer) @number
|
||||
|
||||
;; Variables and Symbols
|
||||
|
||||
(typed_binding (atom (qid) @variable))
|
||||
(untyped_binding) @variable
|
||||
(typed_binding (expr) @type)
|
||||
|
||||
(id) @function
|
||||
(bid) @function
|
||||
|
||||
(function_name (atom (qid) @function))
|
||||
(field_name) @function
|
||||
|
||||
|
||||
[(data_name) (record_name)] @constructor
|
||||
|
||||
; Set
|
||||
(SetN) @type.builtin
|
||||
|
||||
(expr . (atom) @function)
|
||||
|
||||
((atom) @boolean
|
||||
(#any-of? @boolean "true" "false" "True" "False"))
|
||||
|
||||
;; Imports and Module Declarations
|
||||
|
||||
"import" @include
|
||||
|
||||
(module_name) @namespace
|
||||
|
||||
;; Pragmas and comments
|
||||
|
||||
(pragma) @preproc
|
||||
|
||||
(comment) @comment
|
||||
|
||||
;; Keywords
|
||||
[
|
||||
"where"
|
||||
"data"
|
||||
"rewrite"
|
||||
"postulate"
|
||||
"public"
|
||||
"private"
|
||||
"tactic"
|
||||
"Prop"
|
||||
"quote"
|
||||
"renaming"
|
||||
"open"
|
||||
"in"
|
||||
"hiding"
|
||||
"constructor"
|
||||
"abstract"
|
||||
"let"
|
||||
"field"
|
||||
"mutual"
|
||||
"module"
|
||||
"infix"
|
||||
"infixl"
|
||||
"infixr"
|
||||
"record"
|
||||
(ARROW)
|
||||
]
|
||||
@keyword
|
||||
|
||||
;;;(expr
|
||||
;;; f_name: (atom) @function)
|
||||
;; Brackets
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"{"
|
||||
"}"]
|
||||
@punctuation.bracket
|
||||
|
||||
[
|
||||
"="
|
||||
] @operator
|
||||
111
queries/arduino/highlights.scm
Normal file
111
queries/arduino/highlights.scm
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
; inherits: cpp
|
||||
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
; Digital I/O
|
||||
"digitalRead"
|
||||
"digitalWrite"
|
||||
"pinMode"
|
||||
; Analog I/O
|
||||
"analogRead"
|
||||
"analogReference"
|
||||
"analogWrite"
|
||||
; Zero, Due & MKR Family
|
||||
"analogReadResolution"
|
||||
"analogWriteResolution"
|
||||
; Advanced I/O
|
||||
"noTone"
|
||||
"pulseIn"
|
||||
"pulseInLong"
|
||||
"shiftIn"
|
||||
"shiftOut"
|
||||
"tone"
|
||||
; Time
|
||||
"delay"
|
||||
"delayMicroseconds"
|
||||
"micros"
|
||||
"millis"
|
||||
; Math
|
||||
"abs"
|
||||
"constrain"
|
||||
"map"
|
||||
"max"
|
||||
"min"
|
||||
"pow"
|
||||
"sq"
|
||||
"sqrt"
|
||||
; Trigonometry
|
||||
"cos"
|
||||
"sin"
|
||||
"tan"
|
||||
; Characters
|
||||
"isAlpha"
|
||||
"isAlphaNumeric"
|
||||
"isAscii"
|
||||
"isControl"
|
||||
"isDigit"
|
||||
"isGraph"
|
||||
"isHexadecimalDigit"
|
||||
"isLowerCase"
|
||||
"isPrintable"
|
||||
"isPunct"
|
||||
"isSpace"
|
||||
"isUpperCase"
|
||||
"isWhitespace"
|
||||
; Random Numbers
|
||||
"random"
|
||||
"randomSeed"
|
||||
; Bits and Bytes
|
||||
"bit"
|
||||
"bitClear"
|
||||
"bitRead"
|
||||
"bitSet"
|
||||
"bitWrite"
|
||||
"highByte"
|
||||
"lowByte"
|
||||
; External Interrupts
|
||||
"attachInterrupt"
|
||||
"detachInterrupt"
|
||||
; Interrupts
|
||||
"interrupts"
|
||||
"noInterrupts"
|
||||
))
|
||||
|
||||
((identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"Serial"
|
||||
"SPI"
|
||||
"Stream"
|
||||
"Wire"
|
||||
"Keyboard"
|
||||
"Mouse"
|
||||
"String"
|
||||
))
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#any-of? @constant.builtin
|
||||
"HIGH"
|
||||
"LOW"
|
||||
"INPUT"
|
||||
"OUTPUT"
|
||||
"INPUT_PULLUP"
|
||||
"LED_BUILTIN"
|
||||
))
|
||||
|
||||
(function_definition
|
||||
(function_declarator
|
||||
declarator: (identifier) @function.builtin)
|
||||
(#any-of? @function.builtin "loop" "setup"))
|
||||
|
||||
(call_expression
|
||||
function: (primitive_type) @function.builtin)
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @constructor
|
||||
(#any-of? @constructor "SPISettings" "String"))
|
||||
|
||||
(declaration
|
||||
(type_identifier) @type.builtin
|
||||
(function_declarator
|
||||
declarator: (identifier) @constructor)
|
||||
(#eq? @type.builtin "SPISettings"))
|
||||
3
queries/arduino/injections.scm
Normal file
3
queries/arduino/injections.scm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(preproc_arg) @arduino
|
||||
|
||||
(comment) @comment
|
||||
5
queries/astro/highlights.scm
Normal file
5
queries/astro/highlights.scm
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
; inherits: html
|
||||
|
||||
[ "---" ] @punctuation.delimiter
|
||||
|
||||
[ "{" "}" ] @punctuation.special
|
||||
19
queries/astro/injections.scm
Normal file
19
queries/astro/injections.scm
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
; inherits: html
|
||||
|
||||
((frontmatter
|
||||
(raw_text) @typescript))
|
||||
|
||||
((interpolation
|
||||
(raw_text) @tsx))
|
||||
|
||||
((script_element
|
||||
(raw_text) @typescript))
|
||||
|
||||
((style_element
|
||||
(start_tag
|
||||
(attribute
|
||||
(attribute_name) @_lang_attr
|
||||
(quoted_attribute_value (attribute_value) @_lang_value)))
|
||||
(raw_text) @scss)
|
||||
(#eq? @_lang_attr "lang")
|
||||
(#eq? @_lang_value "scss"))
|
||||
154
queries/awk/highlights.scm
Normal file
154
queries/awk/highlights.scm
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
; adapted from https://github.com/Beaglefoot/tree-sitter-awk
|
||||
|
||||
[
|
||||
(identifier)
|
||||
(field_ref)
|
||||
] @variable
|
||||
(field_ref (_) @variable)
|
||||
|
||||
(number) @number
|
||||
|
||||
(string) @string
|
||||
(regex) @string.regex
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
(ns_qualified_name (namespace) @namespace)
|
||||
(ns_qualified_name "::" @punctuation.delimiter)
|
||||
|
||||
(func_def name: (_ (identifier) @function) @function)
|
||||
(func_call name: (_ (identifier) @function) @function)
|
||||
|
||||
(func_def (param_list (identifier) @parameter))
|
||||
|
||||
[
|
||||
"print"
|
||||
"printf"
|
||||
"getline"
|
||||
] @function.builtin
|
||||
|
||||
[
|
||||
(delete_statement)
|
||||
(break_statement)
|
||||
(continue_statement)
|
||||
(next_statement)
|
||||
(nextfile_statement)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"func"
|
||||
"function"
|
||||
] @keyword.function
|
||||
|
||||
[
|
||||
"return"
|
||||
"exit"
|
||||
] @keyword.return
|
||||
|
||||
[
|
||||
"do"
|
||||
"while"
|
||||
"for"
|
||||
"in"
|
||||
] @repeat
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"case"
|
||||
"default"
|
||||
] @conditional
|
||||
|
||||
[
|
||||
"@include"
|
||||
"@load"
|
||||
] @include
|
||||
|
||||
"@namespace" @preproc
|
||||
|
||||
[
|
||||
"BEGIN"
|
||||
"END"
|
||||
"BEGINFILE"
|
||||
"ENDFILE"
|
||||
] @label
|
||||
|
||||
(binary_exp [
|
||||
"^"
|
||||
"**"
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"+"
|
||||
"-"
|
||||
"<"
|
||||
">"
|
||||
"<="
|
||||
">="
|
||||
"=="
|
||||
"!="
|
||||
"~"
|
||||
"!~"
|
||||
"in"
|
||||
"&&"
|
||||
"||"
|
||||
] @operator)
|
||||
|
||||
(unary_exp [
|
||||
"!"
|
||||
"+"
|
||||
"-"
|
||||
] @operator)
|
||||
|
||||
(assignment_exp [
|
||||
"="
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"^="
|
||||
] @operator)
|
||||
|
||||
(ternary_exp [
|
||||
"?"
|
||||
":"
|
||||
] @conditional.ternary)
|
||||
|
||||
(update_exp [
|
||||
"++"
|
||||
"--"
|
||||
] @operator)
|
||||
|
||||
(redirected_io_statement [
|
||||
">"
|
||||
">>"
|
||||
] @operator)
|
||||
|
||||
(piped_io_statement [
|
||||
"|"
|
||||
"|&"
|
||||
] @operator)
|
||||
|
||||
(piped_io_exp [
|
||||
"|"
|
||||
"|&"
|
||||
] @operator)
|
||||
|
||||
(field_ref "$" @punctuation.delimiter)
|
||||
|
||||
(regex "/" @punctuation.delimiter)
|
||||
(regex_constant "@" @punctuation.delimiter)
|
||||
|
||||
[ ";" "," ] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
2
queries/awk/injections.scm
Normal file
2
queries/awk/injections.scm
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(comment) @comment
|
||||
(regex) @regex
|
||||
|
|
@ -5,5 +5,4 @@
|
|||
(for_statement)
|
||||
(while_statement)
|
||||
(c_style_for_statement)
|
||||
(heredoc_redirect)
|
||||
] @fold
|
||||
136
queries/bash/highlights.scm
Normal file
136
queries/bash/highlights.scm
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
(simple_expansion) @none
|
||||
(expansion
|
||||
"${" @punctuation.special
|
||||
"}" @punctuation.special) @none
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"(("
|
||||
"))"
|
||||
"{"
|
||||
"}"
|
||||
"["
|
||||
"]"
|
||||
"[["
|
||||
"]]"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
";"
|
||||
";;"
|
||||
(heredoc_start)
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"$"
|
||||
] @punctuation.special
|
||||
|
||||
[
|
||||
">"
|
||||
">>"
|
||||
"<"
|
||||
"<<"
|
||||
"&"
|
||||
"&&"
|
||||
"|"
|
||||
"||"
|
||||
"="
|
||||
"=~"
|
||||
"=="
|
||||
"!="
|
||||
] @operator
|
||||
|
||||
[
|
||||
(string)
|
||||
(raw_string)
|
||||
(ansi_c_string)
|
||||
(heredoc_body)
|
||||
] @string @spell
|
||||
|
||||
(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
|
||||
|
||||
; trap -l
|
||||
((word) @constant.builtin
|
||||
(#match? @constant.builtin "^SIG(HUP|INT|QUIT|ILL|TRAP|ABRT|BUS|FPE|KILL|USR[12]|SEGV|PIPE|ALRM|TERM|STKFLT|CHLD|CONT|STOP|TSTP|TT(IN|OU)|URG|XCPU|XFSZ|VTALRM|PROF|WINCH|IO|PWR|SYS|RTMIN([+]([1-9]|1[0-5]))?|RTMAX(-([1-9]|1[0-4]))?)$"))
|
||||
|
||||
((word) @boolean
|
||||
(#match? @boolean "^(true|false)$"))
|
||||
|
||||
(comment) @comment @spell
|
||||
(test_operator) @string
|
||||
|
||||
(command_substitution
|
||||
[ "$(" ")" ] @punctuation.bracket)
|
||||
|
||||
(process_substitution
|
||||
[ "<(" ")" ] @punctuation.bracket)
|
||||
|
||||
|
||||
(function_definition
|
||||
name: (word) @function)
|
||||
|
||||
(command_name (word) @function.call)
|
||||
|
||||
((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
|
||||
|
||||
((program . (comment) @preproc)
|
||||
(#match? @preproc "^#!/"))
|
||||
3
queries/bash/injections.scm
Normal file
3
queries/bash/injections.scm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(comment) @comment
|
||||
|
||||
(regex) @regex
|
||||
13
queries/bash/locals.scm
Normal file
13
queries/bash/locals.scm
Normal 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
|
||||
4
queries/beancount/folds.scm
Normal file
4
queries/beancount/folds.scm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
(transaction)
|
||||
(section)
|
||||
] @fold
|
||||
|
|
@ -1,57 +1,24 @@
|
|||
(date) @variable.member
|
||||
|
||||
(date) @field
|
||||
(txn) @attribute
|
||||
|
||||
(account) @type
|
||||
|
||||
(amount) @number
|
||||
|
||||
(incomplete_amount) @number
|
||||
|
||||
(compound_amount) @number
|
||||
|
||||
(amount_tolerance) @number
|
||||
|
||||
(currency) @property
|
||||
|
||||
(key) @label
|
||||
|
||||
(string) @string
|
||||
|
||||
(string) @string @spell
|
||||
(narration) @string @spell
|
||||
|
||||
(payee) @string @spell
|
||||
|
||||
(tag) @constant
|
||||
|
||||
(link) @constant
|
||||
|
||||
[
|
||||
(minus)
|
||||
(plus)
|
||||
(slash)
|
||||
(asterisk)
|
||||
(minus) (plus) (slash) (asterisk)
|
||||
] @operator
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
[
|
||||
(balance)
|
||||
(open)
|
||||
(close)
|
||||
(commodity)
|
||||
(pad)
|
||||
(event)
|
||||
(price)
|
||||
(note)
|
||||
(document)
|
||||
(query)
|
||||
(custom)
|
||||
(pushtag)
|
||||
(poptag)
|
||||
(pushmeta)
|
||||
(popmeta)
|
||||
(option)
|
||||
(include)
|
||||
(plugin)
|
||||
(balance) (open) (close) (commodity) (pad)
|
||||
(event) (price) (note) (document) (query)
|
||||
(custom) (pushtag) (poptag) (pushmeta)
|
||||
(popmeta) (option) (include) (plugin)
|
||||
] @keyword
|
||||
3
queries/bibtex/folds.scm
Normal file
3
queries/bibtex/folds.scm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
(entry)
|
||||
] @fold
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
; CREDITS @pfoerster (adapted from https://github.com/latex-lsp/tree-sitter-bibtex)
|
||||
|
||||
[
|
||||
(string_type)
|
||||
(preamble_type)
|
||||
|
|
@ -10,8 +11,6 @@
|
|||
(comment)
|
||||
] @comment
|
||||
|
||||
(comment) @spell
|
||||
|
||||
[
|
||||
"="
|
||||
"#"
|
||||
|
|
@ -22,27 +21,20 @@
|
|||
(number) @number
|
||||
|
||||
(field
|
||||
name: (identifier) @property)
|
||||
name: (identifier) @field)
|
||||
|
||||
(token
|
||||
(identifier) @variable.parameter)
|
||||
(identifier) @parameter)
|
||||
|
||||
[
|
||||
(brace_word)
|
||||
(quote_word)
|
||||
] @string
|
||||
|
||||
((field
|
||||
name: (identifier) @_url
|
||||
value: (value
|
||||
(token
|
||||
(brace_word) @string.special.url)))
|
||||
(#any-of? @_url "url" "doi"))
|
||||
|
||||
[
|
||||
(key_brace)
|
||||
(key_paren)
|
||||
] @markup.link.label
|
||||
] @symbol
|
||||
|
||||
(string
|
||||
name: (identifier) @constant)
|
||||
10
queries/bibtex/indents.scm
Normal file
10
queries/bibtex/indents.scm
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[
|
||||
(entry)
|
||||
] @indent
|
||||
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
] @branch
|
||||
|
||||
(comment) @ignore
|
||||
57
queries/blueprint/highlights.scm
Normal file
57
queries/blueprint/highlights.scm
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
(object_id) @variable
|
||||
|
||||
(string) @string
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(constant) @constant.builtin
|
||||
|
||||
(boolean) @boolean
|
||||
|
||||
(using) @include
|
||||
|
||||
(template) @keyword
|
||||
|
||||
(decorator) @attribute
|
||||
|
||||
(property_definition (property_name) @property)
|
||||
|
||||
(object) @type
|
||||
|
||||
(signal_binding (signal_name) @function.builtin)
|
||||
(signal_binding (function (identifier)) @function)
|
||||
(signal_binding "swapped" @keyword)
|
||||
|
||||
(styles_list "styles" @function.macro)
|
||||
(layout_definition "layout" @function.macro)
|
||||
|
||||
(gettext_string "_" @function.builtin)
|
||||
|
||||
(menu_definition "menu" @keyword)
|
||||
(menu_section "section" @keyword)
|
||||
(menu_item "item" @function.macro)
|
||||
|
||||
(template_definition (template_name_qualifier) @type.qualifier)
|
||||
|
||||
(import_statement (gobject_library) @namespace)
|
||||
|
||||
(import_statement (version_number) @float)
|
||||
|
||||
(float) @float
|
||||
(number) @number
|
||||
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
19
queries/c/folds.scm
Normal file
19
queries/c/folds.scm
Normal 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)
|
||||
202
queries/c/highlights.scm
Normal file
202
queries/c/highlights.scm
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
; Lower priority to prefer @parameter when identifier appears in parameter_declaration.
|
||||
((identifier) @variable (#set! "priority" 95))
|
||||
|
||||
[
|
||||
"default"
|
||||
"enum"
|
||||
"struct"
|
||||
"typedef"
|
||||
"union"
|
||||
"goto"
|
||||
] @keyword
|
||||
|
||||
"sizeof" @keyword.operator
|
||||
|
||||
"return" @keyword.return
|
||||
|
||||
[
|
||||
"while"
|
||||
"for"
|
||||
"do"
|
||||
"continue"
|
||||
"break"
|
||||
] @repeat
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"case"
|
||||
"switch"
|
||||
] @conditional
|
||||
|
||||
[
|
||||
"#if"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#else"
|
||||
"#elif"
|
||||
"#endif"
|
||||
(preproc_directive)
|
||||
] @preproc
|
||||
|
||||
"#define" @define
|
||||
|
||||
"#include" @include
|
||||
|
||||
[ ";" ":" "," ] @punctuation.delimiter
|
||||
|
||||
"..." @punctuation.special
|
||||
|
||||
[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket
|
||||
|
||||
[
|
||||
"="
|
||||
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"+"
|
||||
"%"
|
||||
|
||||
"~"
|
||||
"|"
|
||||
"&"
|
||||
"^"
|
||||
"<<"
|
||||
">>"
|
||||
|
||||
"->"
|
||||
"."
|
||||
|
||||
"<"
|
||||
"<="
|
||||
">="
|
||||
">"
|
||||
"=="
|
||||
"!="
|
||||
|
||||
"!"
|
||||
"&&"
|
||||
"||"
|
||||
|
||||
"-="
|
||||
"+="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"|="
|
||||
"&="
|
||||
"^="
|
||||
">>="
|
||||
"<<="
|
||||
"--"
|
||||
"++"
|
||||
] @operator
|
||||
|
||||
;; Make sure the comma operator is given a highlight group after the comma
|
||||
;; punctuator so the operator is highlighted properly.
|
||||
(comma_expression [ "," ] @operator)
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(conditional_expression [ "?" ":" ] @conditional.ternary)
|
||||
|
||||
(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_designator) @property
|
||||
(((field_identifier) @property)
|
||||
(#has-ancestor? @property field_declaration)
|
||||
(#not-has-ancestor? @property function_declarator))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
[
|
||||
(type_identifier)
|
||||
(sized_type_specifier)
|
||||
(type_descriptor)
|
||||
] @type
|
||||
|
||||
(storage_class_specifier) @storageclass
|
||||
|
||||
(type_qualifier) @type.qualifier
|
||||
|
||||
(linkage_specification
|
||||
"extern" @storageclass)
|
||||
|
||||
(type_definition
|
||||
declarator: (type_identifier) @type.definition)
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
|
||||
(enumerator
|
||||
name: (identifier) @constant)
|
||||
(case_statement
|
||||
value: (identifier) @constant)
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#any-of? @constant.builtin "stderr" "stdin" "stdout"))
|
||||
|
||||
;; Preproc def / undef
|
||||
(preproc_def
|
||||
name: (_) @constant)
|
||||
(preproc_call
|
||||
directive: (preproc_directive) @_u
|
||||
argument: (_) @constant
|
||||
(#eq? @_u "#undef"))
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.call))
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.macro)
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
;; 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_declaration)
|
||||
] @attribute
|
||||
|
||||
(ERROR) @error
|
||||
120
queries/c/indents.scm
Normal file
120
queries/c/indents.scm
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
[
|
||||
(compound_statement)
|
||||
(field_declaration_list)
|
||||
(case_statement)
|
||||
(enumerator_list)
|
||||
(compound_literal_expression)
|
||||
(initializer_list)
|
||||
(init_declarator)
|
||||
(expression_statement)
|
||||
] @indent
|
||||
|
||||
(
|
||||
ERROR
|
||||
"for" "(" @indent ";" ";" ")" @indent_end)
|
||||
(
|
||||
(for_statement
|
||||
body: (_) @_body
|
||||
) @indent
|
||||
(#not-has-type? @_body compound_statement)
|
||||
)
|
||||
|
||||
(
|
||||
while_statement
|
||||
condition: (_) @indent
|
||||
)
|
||||
(
|
||||
(while_statement
|
||||
body: (_) @_body
|
||||
) @indent
|
||||
(#not-has-type? @_body compound_statement)
|
||||
)
|
||||
|
||||
(
|
||||
(if_statement)
|
||||
(ERROR "else") @indent
|
||||
)
|
||||
|
||||
(
|
||||
if_statement
|
||||
condition: (_) @indent
|
||||
)
|
||||
;; Make sure all cases of if-else are tagged with @indent
|
||||
;; So we will offset the indents for the else case
|
||||
(
|
||||
(if_statement
|
||||
consequence: (compound_statement)
|
||||
"else" @branch
|
||||
alternative:
|
||||
[
|
||||
[ "{" "}" ] @branch
|
||||
(compound_statement ["{" "}"] @branch)
|
||||
(_)
|
||||
]
|
||||
) @indent
|
||||
)
|
||||
(
|
||||
(if_statement
|
||||
consequence: (_ ";" @indent_end) @_consequence
|
||||
) @indent
|
||||
(#not-has-type? @_consequence compound_statement)
|
||||
)
|
||||
(
|
||||
(if_statement
|
||||
consequence: (_) @_consequence
|
||||
"else" @branch
|
||||
alternative:
|
||||
[
|
||||
[ "{" "}" ] @branch
|
||||
(compound_statement ["{" "}"] @branch)
|
||||
(_)
|
||||
]
|
||||
)
|
||||
(#not-has-type? @_consequence compound_statement)
|
||||
)
|
||||
|
||||
;; Dedent for chaining if-else statements
|
||||
;; this will go recursively through each if-elseif
|
||||
;; if-elseif -> second `if` is dedented once, indented twice
|
||||
;; if-elseif-elseif -> third `if` is dedented twice, indented 3 times
|
||||
;; -> all are indented once
|
||||
(
|
||||
(if_statement
|
||||
consequence: (_)
|
||||
alternative:
|
||||
[
|
||||
(if_statement consequence: (compound_statement) @dedent)
|
||||
(_)
|
||||
] @dedent
|
||||
)
|
||||
)
|
||||
|
||||
(compound_statement "}" @indent_end)
|
||||
|
||||
[
|
||||
")"
|
||||
"}"
|
||||
(statement_identifier)
|
||||
] @branch
|
||||
|
||||
[
|
||||
"#define"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#elif"
|
||||
"#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
|
||||
3
queries/c/injections.scm
Normal file
3
queries/c/injections.scm
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(preproc_arg) @c
|
||||
|
||||
(comment) @comment
|
||||
53
queries/c/locals.scm
Normal file
53
queries/c/locals.scm
Normal 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
|
||||
15
queries/c_sharp/folds.scm
Normal file
15
queries/c_sharp/folds.scm
Normal 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
|
||||
399
queries/c_sharp/highlights.scm
Normal file
399
queries/c_sharp/highlights.scm
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
(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.call))
|
||||
|
||||
(invocation_expression
|
||||
function: (conditional_access_expression
|
||||
(member_binding_expression
|
||||
name: (identifier) @method.call)))
|
||||
|
||||
(namespace_declaration
|
||||
name: [(qualified_name) (identifier)] @namespace)
|
||||
|
||||
(qualified_name
|
||||
(identifier) @type)
|
||||
|
||||
(invocation_expression
|
||||
(identifier) @method.call)
|
||||
|
||||
(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)
|
||||
] @type.builtin
|
||||
|
||||
(implicit_type) @keyword
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
(using_directive
|
||||
(identifier) @type)
|
||||
|
||||
(using_directive
|
||||
(name_equals (identifier) @type.definition))
|
||||
|
||||
(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.call))
|
||||
|
||||
(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"
|
||||
"as"
|
||||
] @include
|
||||
|
||||
(alias_qualified_name
|
||||
(identifier "global") @include)
|
||||
|
||||
[
|
||||
"with"
|
||||
"new"
|
||||
"typeof"
|
||||
"sizeof"
|
||||
"is"
|
||||
"and"
|
||||
"or"
|
||||
"not"
|
||||
"stackalloc"
|
||||
"in"
|
||||
"out"
|
||||
"ref"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"lock"
|
||||
"params"
|
||||
"operator"
|
||||
"default"
|
||||
"implicit"
|
||||
"explicit"
|
||||
"override"
|
||||
"async"
|
||||
"await"
|
||||
"class"
|
||||
"delegate"
|
||||
"enum"
|
||||
"interface"
|
||||
"namespace"
|
||||
"struct"
|
||||
"get"
|
||||
"set"
|
||||
"init"
|
||||
"where"
|
||||
"record"
|
||||
"event"
|
||||
"add"
|
||||
"remove"
|
||||
"checked"
|
||||
"unchecked"
|
||||
"fixed"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"const"
|
||||
"extern"
|
||||
"readonly"
|
||||
"static"
|
||||
"volatile"
|
||||
"required"
|
||||
] @storageclass
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"private"
|
||||
"protected"
|
||||
"internal"
|
||||
"public"
|
||||
"partial"
|
||||
"sealed"
|
||||
"virtual"
|
||||
] @type.qualifier
|
||||
|
||||
(parameter_modifier) @operator
|
||||
|
||||
(query_expression
|
||||
(_ [
|
||||
"from"
|
||||
"orderby"
|
||||
"select"
|
||||
"group"
|
||||
"by"
|
||||
"ascending"
|
||||
"descending"
|
||||
"equals"
|
||||
"let"
|
||||
] @keyword))
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
1
queries/c_sharp/injections.scm
Normal file
1
queries/c_sharp/injections.scm
Normal file
|
|
@ -0,0 +1 @@
|
|||
(comment) @comment
|
||||
41
queries/c_sharp/locals.scm
Normal file
41
queries/c_sharp/locals.scm
Normal 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
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue