diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..780d2f3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.github +.gitignore +.gitlab-ci-yml +.luacheckrc +.projections.json +.vintrc +*Dockerfile +test/new/env diff --git a/.github/workflows/neovim.yml b/.github/workflows/neovim.yml index 13b8847..64e31f3 100644 --- a/.github/workflows/neovim.yml +++ b/.github/workflows/neovim.yml @@ -3,7 +3,7 @@ name: Neovim on: push: branches: - - '*' + - "*" pull_request: branches: - master @@ -13,28 +13,52 @@ jobs: strategy: matrix: neovim_version: - - 'head' - - 'v0.10.1' + - "head" + - "v0.11.2" runs-on: ubuntu-latest + env: + TESTS_ENABLE_TREESITTER: 1 steps: - - uses: 'actions/checkout@v2' + - uses: "actions/checkout@v2" + - uses: tree-sitter/setup-action@v2 + with: + install-lib: false - name: Install vader.vim run: git clone --depth=1 https://github.com/junegunn/vader.vim.git test/vader/vader.vim - - name: 'setup Neovim' - uses: 'thinca/action-setup-vim@v2' + - name: "setup Neovim" + uses: "thinca/action-setup-vim@v2" with: - vim_version: '${{ matrix.neovim_version }}' - vim_type: 'Neovim' + vim_version: "${{ matrix.neovim_version }}" + vim_type: "Neovim" - - name: 'Show version' + - name: "Show version" run: nvim --version - - name: 'Run test' + - name: Clone tree-sitter-python + run: git clone --depth=1 https://github.com/tree-sitter/tree-sitter-python.git + working-directory: /tmp + + - name: Clone tree-sitter-ruby + run: git clone --depth=1 https://github.com/tree-sitter/tree-sitter-ruby.git + working-directory: /tmp + + - name: Create default nvim runtime parser directory + run: mkdir -p $HOME/.local/share/nvim/site/parser + + - name: Build tree-sitter-python + run: tree-sitter build -o $HOME/.local/share/nvim/site/parser/python.so + working-directory: /tmp/tree-sitter-python + + - name: Build tree-sitter-ruby + run: tree-sitter build -o $HOME/.local/share/nvim/site/parser/ruby.so + working-directory: /tmp/tree-sitter-ruby + + - name: "Run test" run: | bash -c 'VIMCMD=nvim test/vader/run' - - name: 'Run new tests' + - name: "Run new tests" run: | cd ./test/new && make -j1 && make -j1 coverage diff --git a/.github/workflows/neovim_treesitter.yml b/.github/workflows/neovim_treesitter.yml deleted file mode 100644 index 52ea1fa..0000000 --- a/.github/workflows/neovim_treesitter.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Neovim with Tree-sitter - -on: - push: - branches: - - '*' - pull_request: - branches: - - master - -jobs: - build: - strategy: - matrix: - neovim_version: - - 'head' - - 'v0.10.1' - runs-on: ubuntu-latest - env: - TESTS_ENABLE_TREESITTER: 1 - steps: - - uses: 'actions/checkout@v2' - - - name: Install vader.vim - run: git clone --depth=1 https://github.com/junegunn/vader.vim.git test/vader/vader.vim - - - name: 'setup Neovim' - uses: 'thinca/action-setup-vim@v2' - with: - vim_version: '${{ matrix.neovim_version }}' - vim_type: 'Neovim' - - - name: Install nvim-treesitter - run: git clone --depth=1 https://github.com/nvim-treesitter/nvim-treesitter.git test/vader/plugged/nvim-treesitter - - - name: Install python treesitter module - run: nvim --headless -Nu test/vader/minvimrc -c 'TSInstallSync python' -c 'q' - - - name: 'Show version' - run: nvim --version - - - name: 'Run test' - run: | - bash -c 'VIMCMD=nvim test/vader/run' - - - name: Install ruby treesitter module - run: nvim --headless -Nu test/vader/minvimrc -c 'TSInstallSync ruby' -c 'q' - - - name: 'Run new tests' - run: | - cd ./test/new && make -j1 && make -j1 coverage diff --git a/.gitignore b/.gitignore index 926ccaa..12604df 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ doc/tags +.nvim.lua diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a2245ac --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +PHONY: docker_build docker_test_nvim + +RUNFOR ?= nvim +VIMCMD = $(shell if [ $(RUNFOR) = nvim ]; then echo "nvim --headless"; else echo "vim -T dumb --not-a-term -n"; fi) +VIMCMD != if [ $(RUNFOR) = nvim ]; then echo "nvim --headless"; else echo "vim -T dumb --not-a-term -n"; fi +NVIM_VERSION ?= stable +NVIM_ARCH ?= -linux-x86_64 +VIM_VERSION ?= v9.1.1287 + +docker_build: + docker build --tag 'vim-matchup-nvim-stable' \ + --file vim.Dockerfile \ + --build-arg NVIM_VERSION=${NVIM_VERSION} \ + --build-arg NVIM_ARCH=${NVIM_ARCH} \ + --build-arg VIM_VERSION=${VIM_VERSION} \ + . + +docker_test_old: docker_build + docker run --rm -it --pull=never --name nvim vim-matchup-nvim-stable -c 'VIMCMD="${VIMCMD}" test/vader/run' + +docker_test_new: docker_build + docker run --rm -it --pull=never --name nvim vim-matchup-nvim-stable -c 'cd ./test/new && make -j1 MYVIM="${VIMCMD}"' + +docker_test_shell: docker_build + docker run --rm -it --pull=never --name nvim vim-matchup-nvim-stable diff --git a/README.md b/README.md index 611cd4c..10e9027 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ match-up is a plugin that lets you highlight, navigate, and operate on -sets of matching text. It extends vim's `%` key to language-specific +sets of matching text. It extends vim's `%` key to language-specific words instead of just single characters. ## Screenshot @@ -18,20 +18,20 @@ words instead of just single characters. ## Table of contents - * [Overview](#overview) - * [Installation](#installation) - * [Features](#features) - * [Options](#options) - * [FAQ](#faq) - * [Interoperability](#interoperability) - * [Acknowledgments](#acknowledgments) - * [Development](#development) +- [Overview](#overview) +- [Installation](#installation) +- [Features](#features) +- [Options](#options) +- [FAQ](#faq) +- [Interoperability](#interoperability) +- [Acknowledgments](#acknowledgments) +- [Development](#development) ## Overview match-up can be used as a drop-in replacement for the classic plugin [matchit.vim]. match-up aims to enhance all of matchit's features, fix a number of its -deficiencies and bugs, and add a few totally new features. It also +deficiencies and bugs, and add a few totally new features. It also replaces the standard plugin [matchparen], allowing all of matchit's words to be highlighted along with the `matchpairs` (`(){}[]`). @@ -39,9 +39,9 @@ to be highlighted along with the `matchpairs` (`(){}[]`). [matchparen]: http://ftp.vim.org/pub/vim/runtime/doc/pi_paren.txt See [detailed feature documentation](#detailed-feature-documentation) for -more information. This plugin: +more information. This plugin: -- Extends vim's `%` motion to language-specific words. The following vim +- Extends vim's `%` motion to language-specific words. The following vim file type plugins currently provide special support for match-up: > abaqus, ada, aspvbs, bash, c, cpp, chicken, clojure, cmake, cobol, @@ -60,7 +60,7 @@ more information. This plugin: - Adds motions `g%`, `[%`, `]%`, and `z%`. - Combines these motions into convenient text objects `i%` and `a%`. - Highlights symbols and words under the cursor which `%` can work on, - and highlights matching symbols and words. Now you can easily tell + and highlights matching symbols and words. Now you can easily tell where `%` will jump to. ## Installation @@ -91,28 +91,43 @@ end) and run `:PackerSync` or similar. -See [Tree-sitter integration](https://github.com/andymass/vim-matchup#tree-sitter-integration) -for information on how to enable tree-sitter matching with neovim. - -Note: I do not recommend using alternative loading strategies such as -`event = 'VimEnter'` or `event = 'CursorMoved'` as match-up already -loads a minimal amount of code on start-up. It may work, but if you run -into issues, remove the event key as a first debugging step. - -With [LunarVim](https://www.lunarvim.org/), tree-sitter integration can be -enabled as follows: +If you use [lazy.nvim](https://github.com/folke/lazy.nvim), add the following +to your plugins spec ```lua { - "andymass/vim-matchup", - setup = function() - vim.g.matchup_matchparen_offscreen = { method = "popup" } - end, -}, + 'andymass/vim-matchup' + init = function() + -- modify your configuration vars here + vim.g.matchup_treesitter_stopline = 500 -lvim.builtin.treesitter.matchup.enable = true + -- or call the setup function provided as a helper. It defines the + -- configuration vars for you + require('match-up').setup({ + treesitter = { + stopline = 500 + } + }) + end, + -- or use the `opts` mechanism built into `lazy.nvim`. It calls + -- `require('match-up').setup` under the hood + ---@type matchup.Config + opts = { + treesitter = { + stopline = 500, + } + } +} ``` +See [Tree-sitter integration](https://github.com/andymass/vim-matchup#tree-sitter-integration) +for information on how the tree-sitter integration works on Neovim. + +Note: I do not recommend using alternative loading strategies such as +`event = 'VimEnter'` or `event = 'CursorMoved'` as match-up already +loads a minimal amount of code on start-up. It may work, but if you run +into issues, remove the event key as a first debugging step. + You can use any other plugin manager such as [vundle](https://github.com/gmarik/vundle), [dein](https://github.com/Shougo/dein.vim), @@ -131,39 +146,18 @@ together with other plugins. ### Tree-sitter integration -_Note: Currently this feature is possible in neovim only. Only the latest -version of neovim is supported._ +_Note: Currently this feature is possible in Neovim only. Only the latest +stable version of Neovim is supported._ -match-up has support for language syntax provided by tree-sitter. The +match-up has support for language syntax provided by tree-sitter. The list of supported languages is available [here](https://github.com/andymass/vim-matchup/tree/master/after/queries). -This feature requires manual opt-in in your init.vim and requires -[nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) to -be installed. +This feature is automatically enabled if you are using Neovim. And does not +require other plugins to work. -```vim -Plug 'nvim-treesitter/nvim-treesitter' -lua < (matchup-hi-surround) ``` + There is no default map for this feature. You can also highlight surrounding delimiters always as the cursor moves. + ```vim let g:matchup_matchparen_deferred = 1 let g:matchup_matchparen_hi_surround_always = 1 ``` + default: 0 (off) This can be set on a per-buffer basis: + ```vim autocmd FileType tex let b:matchup_matchparen_hi_surround_always = 1 ``` @@ -702,43 +753,53 @@ enabled. In vim, `{count}%` goes to the `{count}` percentage in the file. match-up overrides this motion for small `{count}` (by default, anything -less than 7). To allow `{count}%` for `{count}` less than 12, +less than 7). To allow `{count}%` for `{count}` less than 12, + ```vim g:matchup_motion_override_Npercent = 11 ``` + To disable this feature, and restore vim's default `{count}%`, + ```vim g:matchup_motion_override_Npercent = 0 ``` + To always enable this feature, use any value greater than 99, + ```vim g:matchup_motion_override_Npercent = 100 ``` + default: 6 If enabled, cursor will land on the end of mid and close words while -moving downwards (`%`/`]%`). While moving upwards (`g%`, `[%`) the cursor -will land on the beginning. To disable, +moving downwards (`%`/`]%`). While moving upwards (`g%`, `[%`) the cursor +will land on the beginning. To disable, + ```vim let g:matchup_motion_cursor_end = 0 ``` + default: 1 ### Module text_obj Modify the set of operators which may operate [line-wise](#line-wise-operatortext-object-combinations) + ```vim let g:matchup_text_obj_linewise_operators = ['d', 'y'] ``` + default: `['d', 'y']` ## FAQ - match-up doesn't work - This plugin requires at least vim 7.4. It should work in vim 7.4.898 - but at least vim 7.4.1689 is better. I recommend using the most recent + This plugin requires at least vim 7.4. It should work in vim 7.4.898 + but at least vim 7.4.1689 is better. I recommend using the most recent version of vim if possible. If you have issues, please tell me your vim version and error messages. @@ -760,7 +821,7 @@ default: `['d', 'y']` - Highlighting is not correct for construct X match-up uses matchit's filetype-specific data, which may not give - enough information to create proper highlights. To fix this, you may + enough information to create proper highlights. To fix this, you may need to modify `b:match_words` in your configuration. For more help, please open a new issue and be as specific as possible. @@ -768,17 +829,17 @@ default: `['d', 'y']` - I'm having performance problems match-up aims to be as fast as possible, but highlighting matching words - can be intensive and may be slow on less powerful machines. There are a + can be intensive and may be slow on less powerful machines. There are a few things you can try to improve performance: - 1. Update to a recent version of vim. Newer versions are faster, more - extensively tested, and better supported by match-up. + 1. Update to a recent version of vim. Newer versions are faster, more + extensively tested, and better supported by match-up. 2. Try [deferred highlighting](#deferred-highlighting), which delays - highlighting until the cursor is stationary to improve cursor movement - performance. - 3. Lower the [highlighting timeouts](#highlighting-timeouts). Note that - if highlighting takes longer than the timeout, highlighting will not be - attempted again until the cursor moves. + highlighting until the cursor is stationary to improve cursor movement + performance. + 3. Lower the [highlighting timeouts](#highlighting-timeouts). Note that + if highlighting takes longer than the timeout, highlighting will not be + attempted again until the cursor moves. If are having any other performance issues, please open a new issue and report the output of `:MatchupShowTimes`. @@ -786,7 +847,7 @@ default: `['d', 'y']` - Why is there a weird entry on the status line? This is a feature which helps you see matches that are outside of the - vim screen, similar to some IDEs. If you wish to disable it, use + vim screen, similar to some IDEs. If you wish to disable it, use ```vim let g:matchup_matchparen_offscreen = {} @@ -794,13 +855,14 @@ default: `['d', 'y']` - Matching does not work when lines are too far apart. - The number of search lines is limited for performance reasons. You may + The number of search lines is limited for performance reasons. You may increase the limits with the following options: ```vim let g:matchup_delim_stopline = 1500 " generally let g:matchup_matchparen_stopline = 400 " for match highlighting only ``` + - The maps `1i%` and `1a%` are difficult to press. You may use the following maps `I%` and `A%` for convenience: @@ -824,7 +886,7 @@ default: `['d', 'y']` - How can I contribute? Read the [contribution guidelines](CONTRIBUTING.md) and [issue - template](.github/ISSUE_TEMPLATE/bug_report.md). Be as precise and + template](.github/ISSUE_TEMPLATE/bug_report.md). Be as precise and detailed as possible when submitting issues and pull requests. @@ -833,15 +895,15 @@ default: `['d', 'y']` ### vimtex, for LaTeX documents By default, match-up will be disabled automatically for tex files when -[vimtex] is detected. To enable match-up for tex files, use +[vimtex] is detected. To enable match-up for tex files, use ```vim let g:matchup_override_vimtex = 1 ``` match-up's matching engine is more advanced than vimtex's and supports -middle delimiters such as `\middle|` and `\else`. The exact set of -delimiters recognized may differ between the two plugins. For example, +middle delimiters such as `\middle|` and `\else`. The exact set of +delimiters recognized may differ between the two plugins. For example, the mappings `da%` and `dad` will not always match, particularly if you have customized vimtex's delimiters. @@ -850,7 +912,7 @@ have customized vimtex's delimiters. match-up provides built-in support for [vim-surround]-style `ds%` and `cs%` operations (`let g:matchup_surround_enabled = 1`). If vim-surround is installed, you can use vim-surround -replacements such as `cs%)`. `%` cannot be used as a replacement. +replacements such as `cs%)`. `%` cannot be used as a replacement. An alternative plugin is [vim-sandwich], which allows more complex surround replacement rules but is not currently supported. @@ -873,23 +935,23 @@ See for instance one of the following plugins for this; match-up tries to work around matchit.vim in all cases, but if you experience problems, read the following: -- For vim, matchit.vim should not be loaded. If it is loaded, it should +- For vim, matchit.vim should not be loaded. If it is loaded, it should be loaded after match-up (in this case, matchit.vim will be disabled). Note that some plugins, such as [vim-sensible](https://github.com/tpope/vim-sensible), load matchit.vim so these should also be initialized after match-up. -- For neovim, matchit.vim is loaded by default. This should not cause any +- For Neovim, matchit.vim is loaded by default. This should not cause any problems, but you may see a very slight start-up time improvement by setting `let g:loaded_matchit = 1` in your `init.vim`. ### Matchparen emulation -match-up loads [matchparen] if it is not already loaded. Ordinarily, match-up +match-up loads [matchparen] if it is not already loaded. Ordinarily, match-up disables matchparen's highlighting and emulates it to highlight the symbol -contained in the 'matchpairs' option (by default `()`, `[]`, and `{}`). If match-up +contained in the 'matchpairs' option (by default `()`, `[]`, and `{}`). If match-up is disabled per-buffer using `b:matchup_matchparen_enabled`, match-up will use -matchparen instead of its own highlighting. See `b:matchup_matchparen_fallback` +matchparen instead of its own highlighting. See `b:matchup_matchparen_fallback` for more information. ## Acknowledgments @@ -897,7 +959,7 @@ for more information. ### Origins match-up was originally based on [@lervag](https://github.com/lervag)'s -[vimtex]. The concept and style of this plugin and its development are +[vimtex]. The concept and style of this plugin and its development are heavily influenced by vimtex. :beers: [vimtex]: https://github.com/lervag/vimtex @@ -914,8 +976,8 @@ heavily influenced by vimtex. :beers: ### Reporting problems -Thorough issue reports are encouraged. Please read the [issue -template](.github/ISSUE_TEMPLATE/bug_report.md) first. Be as precise and +Thorough issue reports are encouraged. Please read the [issue +template](.github/ISSUE_TEMPLATE/bug_report.md) first. Be as precise and detailed as possible when submitting issues. @@ -927,4 +989,3 @@ Please read the [contribution guidelines](CONTRIBUTING.md) before contributing. Contributions are welcome! - diff --git a/after/queries/bash/matchup.scm b/after/queries/bash/matchup.scm index a5d0d57..a55f65b 100644 --- a/after/queries/bash/matchup.scm +++ b/after/queries/bash/matchup.scm @@ -20,8 +20,11 @@ (do_group "done" @close.loop)) @scope.loop -((word) @mid.loop.1 (#eq? @mid.loop.1 "break")) -((word) @mid.loop.2 (#eq? @mid.loop.2 "continue")) +((word) @mid.loop.1 + (#eq? @mid.loop.1 "break")) + +((word) @mid.loop.2 + (#eq? @mid.loop.2 "continue")) (case_statement "case" @open.case @@ -29,5 +32,9 @@ "esac" @close.case) @scope.case (heredoc_redirect - (heredoc_start) @open.rhrd - (heredoc_end ) @close.rhrd) @scope.rhrd + (heredoc_start) @open.rhrd + (heredoc_end) @close.rhrd) @scope.rhrd + +(compound_statement + "{" @open.block + "}" @close.block) @scope.block diff --git a/after/queries/c/matchup.scm b/after/queries/c/matchup.scm index 48caa18..5afeabb 100644 --- a/after/queries/c/matchup.scm +++ b/after/queries/c/matchup.scm @@ -1,32 +1,40 @@ ; inherits: quote (preproc_ifdef - ["#ifdef" "#ifndef"] @open.def + [ + "#ifdef" + "#ifndef" + ] @open.def "#endif" @close.def) @scope.def (preproc_if "#if" @open.def "#endif" @close.def) @scope.def -(preproc_elif "#elif" @mid.def.1) -(preproc_else "#else" @mid.def.2) +(preproc_elif + "#elif" @mid.def.1) + +(preproc_else + "#else" @mid.def.2) (switch_statement "switch" @open.switch body: (compound_statement - (case_statement "case" @mid.switch.1)? - (case_statement "default" @mid.switch.2)?)) @scope.switch + (case_statement + "case" @mid.switch.1)? + (case_statement + "default" @mid.switch.2)?)) @scope.switch ; 'else' and 'else if' (else_clause - "else" @_start (if_statement "if" @_end)? - (#make-range! "mid.if.1" @_start @_end)) + "else" @mid.if.1 + (if_statement + "if" @mid.if.1)?) ; if ((if_statement "if" @open.if) @scope.if - (#not-has-parent? @scope.if else_clause)) - + (#not-has-parent? @scope.if else_clause)) ; if (compound_statement @@ -35,11 +43,31 @@ ; Functions (function_definition) @scope.function -(function_declarator declarator: (identifier) @open.function) -(return_statement "return" @mid.function.1) + +(function_declarator + declarator: (identifier) @open.function) + +(return_statement + "return" @mid.function.1) ; Loops -(for_statement "for" @open.loop) @scope.loop -(while_statement "while" @open.loop) @scope.loop -(do_statement "do" @open.loop "while" @close.loop) @scope.loop -(break_statement "break" @mid.loop.1) +(for_statement + "for" @open.loop) @scope.loop + +(while_statement + "while" @open.loop) @scope.loop + +(do_statement + "do" @open.loop + "while" @close.loop) @scope.loop + +(break_statement + "break" @mid.loop.1) + +(compound_statement + "{" @open.block + "}" @close.block) @scope.block + +(argument_list + "(" @open.call + ")" @close.call) @scope.call diff --git a/after/queries/ecma/matchup.scm b/after/queries/ecma/matchup.scm index 9b9f1da..4cc899b 100644 --- a/after/queries/ecma/matchup.scm +++ b/after/queries/ecma/matchup.scm @@ -2,35 +2,57 @@ ; functions [ - (arrow_function "=>" @open.function) - (function_expression "function" @open.function) - (function_declaration "function" @open.function) - (method_definition body: (statement_block "{" @open.function "}" @close.function)) + (arrow_function + "=>" @open.function) + (function_expression + "function" @open.function) + (function_declaration + "function" @open.function) + (method_definition + body: (statement_block + "{" @open.function + "}" @close.function)) ] @scope.function -(return_statement "return" @mid.function.1) +(statement_block + "{" @open.block + "}" @close.block) @scope.block + +(return_statement + "return" @mid.function.1) ; switch case -(switch_statement "switch" @open.switch) @scope.switch -(switch_case "case" @mid.switch.1) -(switch_default "default" @mid.switch.2) +(switch_statement + "switch" @open.switch) @scope.switch + +(switch_case + "case" @mid.switch.1) + +(switch_default + "default" @mid.switch.2) ; 'else' and 'else if' (else_clause - "else" @_start (if_statement "if" @_end)? - (#make-range! "mid.if.1" @_start @_end)) + "else" @mid.if.1 + (if_statement + "if" @mid.if.1)?) ; if ((if_statement "if" @open.if) @scope.if - (#not-has-parent? @scope.if else_clause)) + (#not-has-parent? @scope.if else_clause)) ; try -(try_statement "try" @open.try) @scope.try -(catch_clause "catch" @mid.try.1) -(finally_clause "finally" @mid.try.2) +(try_statement + "try" @open.try) @scope.try + +(catch_clause + "catch" @mid.try.1) + +(finally_clause + "finally" @mid.try.2) ; template strings (template_string - "`" @open.tmpl_str - "`" @close.tmpl_str) @scope.tmpl_str + "`" @open.tmpl_str + "`" @close.tmpl_str) @scope.tmpl_str diff --git a/after/queries/elm/matchup.scm b/after/queries/elm/matchup.scm index 0c98830..43b0f27 100644 --- a/after/queries/elm/matchup.scm +++ b/after/queries/elm/matchup.scm @@ -1,10 +1,10 @@ (if_else_expr - . "if" @open.if) @scope.if + . + "if" @open.if) @scope.if (if_else_expr - "else" @_else - "if"? @_if - (#make-range! "mid.if.1" @_else @_if)) + "else" @mid.if.1 + "if"? @mid.if.1) (let_in_expr "let" @open.let @@ -13,4 +13,4 @@ (case_of_expr (case) @open.case (case_of_branch - (arrow) @mid.case.1)) @scope.case + (arrow) @mid.case.1)) @scope.case diff --git a/after/queries/fish/matchup.scm b/after/queries/fish/matchup.scm index 3d98879..95a4727 100644 --- a/after/queries/fish/matchup.scm +++ b/after/queries/fish/matchup.scm @@ -1,21 +1,25 @@ (if_statement "if" @open.if - (else_if_clause ("else" "if") @mid.if.1)? - (else_clause "else" @mid.if.2)? - "end" @close.if - ) @scope.if + (else_if_clause + ("else" + "if") @mid.if.1)? + (else_clause + "else" @mid.if.2)? + "end" @close.if) @scope.if (switch_statement "switch" @open.switch - (case_clause "case" @mid.switch.1)? - "end" @close.switch - ) @scope.switch + (case_clause + "case" @mid.switch.1)? + "end" @close.switch) @scope.switch (for_statement "for" @open.loop "in" @mid.loop.1 "end" @close.loop) @scope.loop + ((break) @mid.loop.2)? + ((continue) @mid.loop.3)? (while_statement @@ -29,4 +33,6 @@ (function_definition "function" @open.func "end" @close.func) @scope.func -(return "return" @mid.func.1) + +(return + "return" @mid.func.1) diff --git a/after/queries/glimmer/matchup.scm b/after/queries/glimmer/matchup.scm index bf55ff3..7fb950a 100644 --- a/after/queries/glimmer/matchup.scm +++ b/after/queries/glimmer/matchup.scm @@ -2,33 +2,42 @@ (element_node) @scope.tag -(element_node_start (tag_name) @open.tag) +(element_node_start + (tag_name) @open.tag) + (element_node_end (tag_name) @close.tag (#offset! @close.tag 0 -1 0 0)) (block_statement (block_statement_start) @open.block - (block_statement_end) @close.block - ) @scope.block + (block_statement_end) @close.block) @scope.block + ; {{else if ...}} (mustache_statement - (helper_invocation helper: (identifier) @mid.block.1 (#lua-match? @mid.block.1 "else")) - ) + (helper_invocation + helper: (identifier) @mid.block.1 + (#lua-match? @mid.block.1 "else"))) + ; {{else}} -(mustache_statement ((identifier) @mid.block.2 (#lua-match? @mid.block.2 "else"))) +(mustache_statement + ((identifier) @mid.block.2 + (#lua-match? @mid.block.2 "else"))) (element_node_void (tag_name) @open.selftag - "/>" @close.selftag - ) @scope.selftag + "/>" @close.selftag) @scope.selftag (mustache_statement - [(helper_invocation) "{{"] @open.mustache - "}}" @close.mustache - ) @scope.mustache + [ + (helper_invocation) + "{{" + ] @open.mustache + "}}" @close.mustache) @scope.mustache (sub_expression - [(helper_invocation) "("] @open.subexpr - ")" @close.subexpr - ) @scope.subexpr + [ + (helper_invocation) + "(" + ] @open.subexpr + ")" @close.subexpr) @scope.subexpr diff --git a/after/queries/go/matchup.scm b/after/queries/go/matchup.scm index ed8415f..16478a2 100644 --- a/after/queries/go/matchup.scm +++ b/after/queries/go/matchup.scm @@ -1,22 +1,42 @@ -(function_declaration "func" @open.func) @scope.func -(method_declaration "func" @open.func) @scope.func -(func_literal "func" @open.func) @scope.func +(function_declaration + "func" @open.func) @scope.func -(return_statement "return" @mid.func.1) +(method_declaration + "func" @open.func) @scope.func + +(func_literal + "func" @open.func) @scope.func + +(return_statement + "return" @mid.func.1) ; 'else' and 'else if' (if_statement - "else" @_start (if_statement "if" @_end)? - (#make-range! "mid.if.1" @_start @_end)) + "else" @mid.if.1 + (if_statement + "if" @mid.if.1)?) ; if -(block (if_statement "if" @open.if) @scope.if) +(block + (if_statement + "if" @open.if) @scope.if) ; switch -(expression_switch_statement "switch" @open.switch - (expression_case "case" @mid.switch.1) - (default_case "default" @mid.switch.2)) @scope.switch +(expression_switch_statement + "switch" @open.switch + (expression_case + "case" @mid.switch.1) + (default_case + "default" @mid.switch.2)) @scope.switch (_ "\"" @open.quote_double "\"" @close.quote_double) @scope.quote_double + +(block + "{" @open.block + "}" @close.block) @scope.block + +(argument_list + "(" @open.call + ")" @close.call) @scope.call diff --git a/after/queries/haskell/matchup.scm b/after/queries/haskell/matchup.scm index bba5693..ffcbca5 100644 --- a/after/queries/haskell/matchup.scm +++ b/after/queries/haskell/matchup.scm @@ -1,73 +1,67 @@ ; --------------- module/where --------------- -(header ( - ("module" @open.module (module)) - ("where" @mid.module.1) -)) @scope.module +(header + (("module" @open.module + (module)) + "where" @mid.module.1)) @scope.module ; ----------------- case/of ------------------ -(expression/case - "case" @open.case (_) +(expression/case + "case" @open.case + (_) "of" @mid.case.1 (alternatives - (alternative) @mid.case.2 - ) -) @scope.case + (alternative) @mid.case.2)) @scope.case ; --------------- lambda case ---------------- -(expression/lambda_case - "\case" @open.case +(expression/lambda_case + "\\case" @open.case (alternatives - (alternative) @mid.case.1 - ) -) @scope.case + (alternative) @mid.case.1)) @scope.case ; -------------- if/then/else ---------------- (expression/conditional - "if" @open.if (_) - "then" @mid.if.1 (_) - "else" @mid.if.2 (_) -) @scope.if + "if" @open.if + (_) + "then" @mid.if.1 + (_) + "else" @mid.if.2 + (_)) @scope.if ;------------------ let/in ------------------- (expression/let_in - ("let" @open.let (local_binds)) - ("in" @mid.let.1 (_)) -) @scope.let + ("let" @open.let + (local_binds)) + ("in" @mid.let.1 + (_))) @scope.let ; -------- ADT data/constructors ------------- (data_type - "data" @open.adt (_) + "data" @open.adt + (_) constructors: (data_constructors (data_constructor - constructor: (_) @mid.adt.2 - )) -) @scope.adt + constructor: (_) @mid.adt.2))) @scope.adt ; --------------- ADT record ------------------ (data_type - "data" @open.rec (_) + "data" @open.rec + (_) constructors: (data_constructors constructor: (data_constructor (record fields: (fields - "{" @mid.rec.1 (_) - "}" @mid.rec.2 - ) - ) - ) - ) -) @scope.rec + "{" @mid.rec.1 + (_) + "}" @mid.rec.2))))) @scope.rec ; ------------- GADT data/where --------------- (data_type - "data" @open.gadt (_) + "data" @open.gadt + (_) "where" @mid.gadt.1 constructors: (gadt_constructors constructor: (gadt_constructor - name: (constructor) @mid.gadt.2 - ) - ) -) @scope.gadt + name: (constructor) @mid.gadt.2))) @scope.gadt ; --------------- class/where ----------------- (class @@ -75,7 +69,4 @@ "where" @mid.class.1 declarations: (class_declarations declaration: (_ - name: (variable) @mid.class.2 - ) - ) -) @scope.class + name: (variable) @mid.class.2))) @scope.class diff --git a/after/queries/html/matchup.scm b/after/queries/html/matchup.scm index 735233f..1b5b79f 100644 --- a/after/queries/html/matchup.scm +++ b/after/queries/html/matchup.scm @@ -1,12 +1,14 @@ ; inherits: quote [ - (element) - (script_element) - (style_element) + (element) + (script_element) + (style_element) ] @scope.tag -(start_tag (tag_name) @open.tag) +(start_tag + (tag_name) @open.tag) + (end_tag (tag_name) @close.tag (#offset! @close.tag 0 -1 0 0)) diff --git a/after/queries/jsx/matchup.scm b/after/queries/jsx/matchup.scm index 1005926..31c5bcc 100644 --- a/after/queries/jsx/matchup.scm +++ b/after/queries/jsx/matchup.scm @@ -1,5 +1,8 @@ (jsx_element) @scope.tag -(jsx_opening_element (identifier) @open.tag) + +(jsx_opening_element + (identifier) @open.tag) + (jsx_closing_element (identifier) @close.tag (#offset! @close.tag 0 -1 0 0)) diff --git a/after/queries/julia/matchup.scm b/after/queries/julia/matchup.scm index d997fc3..636d5ee 100644 --- a/after/queries/julia/matchup.scm +++ b/after/queries/julia/matchup.scm @@ -1,35 +1,46 @@ (function_definition "function" @open.function "end" @close.function) @scope.function + (return_statement "return" @mid.function.1) (if_statement "if" @open.if "end" @close.if) @scope.if -(else_clause "else" @mid.if.1) -(elseif_clause "elseif" @mid.if.2) + +(else_clause + "else" @mid.if.1) + +(elseif_clause + "elseif" @mid.if.2) (for_statement "for" @open.loop "end" @close.loop) @scope.loop + (while_statement "while" @open.loop "end" @close.loop) @scope.loop + (break_statement) @mid.loop.1 + (continue_statement) @mid.loop.2 (try_statement "try" @open.try "end" @close.try) @scope.try + (catch_clause "catch" @mid.try.2) + (finally_clause "finally" @mid.try.1) (compound_statement "begin" @open.block "end" @close.block) @scope.block + (do_clause "do" @open.block "end" @close.block) @scope.block @@ -37,6 +48,7 @@ (let_statement "let" @open.let "end" @close.let) @scope.let + (module_definition "module" @open.module "end" @close.module) @scope.module diff --git a/after/queries/lua/matchup.scm b/after/queries/lua/matchup.scm index b75d554..fecd5a3 100644 --- a/after/queries/lua/matchup.scm +++ b/after/queries/lua/matchup.scm @@ -13,12 +13,17 @@ (if_statement "if" @open.if "end" @close.if) @scope.if -(else_statement "else" @mid.if.1) -(elseif_statement "elseif" @mid.if.2) + +(else_statement + "else" @mid.if.1) + +(elseif_statement + "elseif" @mid.if.2) (function_declaration "function" @open.function "end" @close.function) @scope.function + (function_definition "function" @open.function "end" @close.function) @scope.function @@ -29,3 +34,12 @@ (do_statement "do" @open.block "end" @close.block) @scope.block + +(table_constructor + "{" @open.table + "}" @close.table) @scope.table + +(function_call + (arguments + "(" @open.call + ")" @close.call)) @scope.call diff --git a/after/queries/nim/matchup.scm b/after/queries/nim/matchup.scm index 2bbb553..3082d78 100644 --- a/after/queries/nim/matchup.scm +++ b/after/queries/nim/matchup.scm @@ -1,40 +1,87 @@ -(for "for" @open.loop "in" @mid.loop.3) @scope.loop -(while "while" @open.loop) @scope.loop -(block "block" @open.loop) @scope.loop -(break_statement "break" @mid.loop.1) -(continue_statement "continue" @mid.loop.2) +(for + "for" @open.loop + "in" @mid.loop.3) @scope.loop -(if "if" @open.conditional) @scope.conditional -(when "when" @open.conditional) @scope.conditional -(elif_branch "elif" @mid.conditional.1) -(else_branch "else" @mid.conditional.2) +(while + "while" @open.loop) @scope.loop -(case "case" @open.conditional) @scope.conditional -(of_branch "of" @mid.conditional.3) +(block + "block" @open.loop) @scope.loop -(variant_declaration "case" @open.conditional) @scope.conditional -(conditional_declaration "when" @open.conditional) @scope.conditional +(break_statement + "break" @mid.loop.1) -(try "try" @open.try) @scope.try -(except_branch "except" @mid.try.1 ) -(finally_branch "finally" @mid.try.2) +(continue_statement + "continue" @mid.loop.2) -(proc_declaration "proc" @open.routine) @scope.routine -(func_declaration "func" @open.routine) @scope.routine -(method_declaration "method" @open.routine) @scope.routine -(converter_declaration "converter" @open.routine) @scope.routine -(template_declaration "template" @open.routine) @scope.routine -(macro_declaration "macro" @open.routine) @scope.routine +(if + "if" @open.conditional) @scope.conditional -(proc_expression "proc" @open.routine) @scope.routine -(func_expression "func" @open.routine) @scope.routine +(when + "when" @open.conditional) @scope.conditional -(return_statement "return" @mid.routine.1) +(elif_branch + "elif" @mid.conditional.1) -(iterator_declaration "iterator" @open.iterator) @scope.iterator -(iterator_expression "iterator" @open.iterator) @scope.iterator +(else_branch + "else" @mid.conditional.2) -(yield_statement "yield" @mid.iterator.1) +(case + "case" @open.conditional) @scope.conditional + +(of_branch + "of" @mid.conditional.3) + +(variant_declaration + "case" @open.conditional) @scope.conditional + +(conditional_declaration + "when" @open.conditional) @scope.conditional + +(try + "try" @open.try) @scope.try + +(except_branch + "except" @mid.try.1) + +(finally_branch + "finally" @mid.try.2) + +(proc_declaration + "proc" @open.routine) @scope.routine + +(func_declaration + "func" @open.routine) @scope.routine + +(method_declaration + "method" @open.routine) @scope.routine + +(converter_declaration + "converter" @open.routine) @scope.routine + +(template_declaration + "template" @open.routine) @scope.routine + +(macro_declaration + "macro" @open.routine) @scope.routine + +(proc_expression + "proc" @open.routine) @scope.routine + +(func_expression + "func" @open.routine) @scope.routine + +(return_statement + "return" @mid.routine.1) + +(iterator_declaration + "iterator" @open.iterator) @scope.iterator + +(iterator_expression + "iterator" @open.iterator) @scope.iterator + +(yield_statement + "yield" @mid.iterator.1) (import_statement "import" @open.import @@ -46,38 +93,63 @@ "import" @mid.from.1) @scope.from (char_literal - . "'" @open.char + . + "'" @open.char "'" @close.char .) @scope.char (interpreted_string_literal - . "\"" @open.string + . + "\"" @open.string "\"" @close.string .) @scope.string (raw_string_literal - . ["r\"" "R\""] @open.string + . + [ + "r\"" + "R\"" + ] @open.string "\"" @close.string .) @scope.string (long_string_literal - . ["\"\"\"" "r\"\"\"" "R\"\"\""] @open.multistring + . + [ + "\"\"\"" + "r\"\"\"" + "R\"\"\"" + ] @open.multistring "\"\"\"" @close.multistring .) @scope.multistring (generalized_string function: (_) - . ["\"" "\"\"\""] @open.multistring - ["\"" "\"\"\""] @close.multistring .) @scope.multistring + . + [ + "\"" + "\"\"\"" + ] @open.multistring + [ + "\"" + "\"\"\"" + ] @close.multistring .) @scope.multistring (accent_quoted - . "`" @open.accent + . + "`" @open.accent "`" @close.accent .) @scope.accent (block_documentation_comment - . "##[" @open.doc_comment + . + "##[" @open.doc_comment "]##" @close.doc_comment .) @scope.doc_comment (block_comment - . "#[" @open.comment + . + "#[" @open.comment "]#" @close.comment .) @scope.comment (pragma_list - . "{." @open.pragma - ["}" ".}"] @close.pragma .) @scope.pragma + . + "{." @open.pragma + [ + "}" + ".}" + ] @close.pragma .) @scope.pragma diff --git a/after/queries/nix/matchup.scm b/after/queries/nix/matchup.scm index c7cc2f5..bfa399a 100644 --- a/after/queries/nix/matchup.scm +++ b/after/queries/nix/matchup.scm @@ -1,9 +1,13 @@ ; --------------- let/in --------------- (let_expression - "let" @open.let (binding_set) - "in" @mid.let.1 (_)) @scope.let + "let" @open.let + (binding_set) + "in" @mid.let.1 + (_)) @scope.let + ; --------------- binding -------------- ; (binding (_)+ (function_exppression) ";") tend to be many lines long (binding - (attrpath) @open.binding (function_expression) + (attrpath) @open.binding + (function_expression) ";" @close.binding) @scope.binding diff --git a/after/queries/perl/matchup.scm b/after/queries/perl/matchup.scm index e95ce2d..fedae80 100644 --- a/after/queries/perl/matchup.scm +++ b/after/queries/perl/matchup.scm @@ -1,34 +1,54 @@ ; matches any conditional -> else type block -> end of final block (conditional_statement - ["if" "unless"] @open.if - "elsif"? @mid.if.1 - "else"? @mid.if.2 - (block "}" @close.if) . -) @scope.if + [ + "if" + "unless" + ] @open.if + "elsif"? @mid.if.1 + "else"? @mid.if.2 + (block + "}" @close.if) .) @scope.if ; matches any loop construct -> loop control (last, next) -> end of final block (_ - ["for" "foreach" "while" "unless"] @open.loop - (block "}" @close.loop) . - ) @scope.loop + [ + "for" + "foreach" + "while" + "unless" + ] @open.loop + (block + "}" @close.loop) .) @scope.loop + (loopex_expression) @mid.loop.1 ; matches sub -> return -> end of block (_ - "sub" @open.fun - (block "}" @close.fun) . -) @scope.fun -(return_expression "return" @mid.fun.1) + "sub" @open.fun + (block + "}" @close.fun) .) @scope.fun + +(return_expression + "return" @mid.fun.1) ; handling for all the different quote types; multi part quotes cycle through [ - (_ "'" @open.quotelike (string_content) "'" @close.quotelike) - (quoted_regexp "'" @open.quotelike "'" @close.quotelike) - (_ "'" @open.quotelike (_) "'"+ @mid.quotelike.1 (replacement) "'" @close.quotelike) + (_ + "'" @open.quotelike + (string_content) + "'" @close.quotelike) + (quoted_regexp + "'" @open.quotelike + "'" @close.quotelike) + (_ + "'" @open.quotelike + (_) + "'"+ @mid.quotelike.1 + (replacement) + "'" @close.quotelike) ] @scope.quotelike (try_statement "try" @open.try "catch"? @mid.try.1 - "finally"? @close.try - ) @scope.try + "finally"? @close.try) @scope.try diff --git a/after/queries/python/matchup.scm b/after/queries/python/matchup.scm index 16642fd..8acd92a 100644 --- a/after/queries/python/matchup.scm +++ b/after/queries/python/matchup.scm @@ -1,31 +1,43 @@ (if_statement "if" @open.if - alternative: (elif_clause "elif" @mid.if.1)? - alternative: (else_clause "else" @mid.if.2)?) @scope.if + alternative: (elif_clause + "elif" @mid.if.1)? + alternative: (else_clause + "else" @mid.if.2)?) @scope.if (function_definition "def" @open.function) @scope.function + (return_statement "return" @mid.function.1) + (yield "yield" @mid.function.2) (for_statement - ("for" @open.loop) - alternative: (else_clause "else" @mid.loop.1)?) @scope.loop + "for" @open.loop + alternative: (else_clause + "else" @mid.loop.1)?) @scope.loop + (while_statement - ("while" @open.loop) - alternative: (else_clause "else" @mid.loop.1)?) @scope.loop + "while" @open.loop + alternative: (else_clause + "else" @mid.loop.1)?) @scope.loop + (break_statement "break" @mid.loop.2) + (continue_statement "continue" @mid.loop.3) (try_statement - ("try" @open.try) - (finally_clause "finally" @mid.try.1)? - (except_clause "except" @mid.try.2)? - (else_clause "else" @mid.try.3)?) @scope.try + "try" @open.try + (finally_clause + "finally" @mid.try.1)? + (except_clause + "except" @mid.try.2)? + (else_clause + "else" @mid.try.3)?) @scope.try (string (string_start) @open.quote_all diff --git a/after/queries/ruby/matchup.scm b/after/queries/ruby/matchup.scm index eea6801..45b81e3 100644 --- a/after/queries/ruby/matchup.scm +++ b/after/queries/ruby/matchup.scm @@ -1,49 +1,65 @@ (method "def" @open.def "end" @close.def) @scope.def + (singleton_method "def" @open.def "end" @close.def) @scope.def + (return "return" @mid.def.1) + (yield "yield" @mid.def.2) + (body_statement - (rescue "rescue" @mid.def.1)) + (rescue + "rescue" @mid.def.1)) + (body_statement - (ensure "ensure" @mid.def.2)) + (ensure + "ensure" @mid.def.2)) (class "class" @open.class "end" @close.class) @scope.class + (singleton_class "class" @open.class "end" @close.class) @scope.class (if "if" @open.if - (else "else" @mid.if.2)? + (else + "else" @mid.if.2)? "end" @close.if) @scope.if -(elsif (else "else" @mid.if.2)) -(elsif "elsif" @mid.if.1) + +(elsif + (else + "else" @mid.if.2)) + +(elsif + "elsif" @mid.if.1) (unless "unless" @open.unless - (else "else" @mid.unless.2)? + (else + "else" @mid.unless.2)? "end" @close.unless) @scope.unless (while - "while" @open.loop + "while" @open.loop body: (do "end" @close.loop)) @scope.loop (for - "for" @open.loop + "for" @open.loop body: (do "end" @close.loop)) @scope.loop (next "next" @mid.loop.1)? + (break "break" @mid.loop.2)? @@ -72,12 +88,14 @@ "end" @close.do) @scope.do (if_modifier) @skip + (unless_modifier) @skip + (while_modifier) @skip + (until_modifier) @skip (block_parameters - ("|") @open.block_param + "|" @open.block_param (_) - ("|") @close.block_param -) @scope.block_param + "|" @close.block_param) @scope.block_param diff --git a/after/queries/rust/matchup.scm b/after/queries/rust/matchup.scm index ebd01b4..bedd707 100644 --- a/after/queries/rust/matchup.scm +++ b/after/queries/rust/matchup.scm @@ -10,24 +10,44 @@ ">" @open.typeparams) @scope.typeparams ; --------------- if/else --------------- -(block (if_expression "if" @open.if_) @scope.if_) -(expression_statement (if_expression "if" @open.if_) @scope.if_) -(let_declaration (if_expression "if" @open.if_) @scope.if_) +(block + (if_expression + "if" @open.if_) @scope.if_) + +(expression_statement + (if_expression + "if" @open.if_) @scope.if_) + +(let_declaration + (if_expression + "if" @open.if_) @scope.if_) -(else_clause "else" @mid.if_.1 (block)) (else_clause - "else" @_start (if_expression "if" @_end) - (#make-range! "mid.if_.2" @_start @_end)) + "else" @mid.if_.1 + (block)) + +(else_clause + "else" @mid.if_.2 + (if_expression + "if" @mid.if_.2)) ; --------------- async/await --------------- -(function_item (function_modifiers "async" @open.async)) @scope.async -(async_block "async" @open.async) @scope.async -(await_expression "await" @mid.async.1) +(function_item + (function_modifiers + "async" @open.async)) @scope.async + +(async_block + "async" @open.async) @scope.async + +(await_expression + "await" @mid.async.1) ; --------------- fn/return --------------- (function_item "fn" @open.function) @scope.function + (closure_expression) @scope.function + (return_expression "return" @mid.function.1) @@ -37,10 +57,23 @@ "|" @close.closureparams) @scope.closureparams ; --------------- while/loop/for + break/continue --------------- -(for_expression . "for" @open.loop) @scope.loop -(while_expression . "while" @open.loop) @scope.loop -(loop_expression . "loop" @open.loop) @scope.loop +(for_expression + . + "for" @open.loop) @scope.loop -(break_expression "break" @mid.loop.1 .) -(break_expression "break" @mid.loop.1 .) -(continue_expression "continue" @mid.loop.1 .) +(while_expression + . + "while" @open.loop) @scope.loop + +(loop_expression + . + "loop" @open.loop) @scope.loop + +(break_expression + "break" @mid.loop.1 .) + +(break_expression + "break" @mid.loop.1 .) + +(continue_expression + "continue" @mid.loop.1 .) diff --git a/after/queries/smali/matchup.scm b/after/queries/smali/matchup.scm index 52174d0..5c9e1d5 100644 --- a/after/queries/smali/matchup.scm +++ b/after/queries/smali/matchup.scm @@ -1,6 +1,6 @@ ; inherits: quote -(method_definition +(method_definition ".method" @open.function ".end method" @close.function) @scope.function diff --git a/after/queries/svelte/matchup.scm b/after/queries/svelte/matchup.scm index 7e29e20..dac61f5 100644 --- a/after/queries/svelte/matchup.scm +++ b/after/queries/svelte/matchup.scm @@ -1,12 +1,13 @@ ; inherits: quote [ - (element) - (script_element) - (style_element) + (element) + (script_element) + (style_element) ] @scope.tag -(start_tag (tag_name) @open.tag) +(start_tag + (tag_name) @open.tag) (end_tag (tag_name) @close.tag @@ -17,7 +18,6 @@ "/>" @close.selftag) @scope.selftag ; await - (await_statement (await_start (block_start_tag @@ -36,7 +36,6 @@ (#offset! @mid.await.2 0 -1 0 0)) ; each - (each_statement (each_start (block_start_tag @@ -49,7 +48,6 @@ (#offset! @close.each 0 -1 0 0))) ; if - (if_statement (if_start (block_start_tag @@ -72,7 +70,6 @@ (#offset! @close.if 0 -1 0 0))) ; key - (key_statement (key_start (block_start_tag @@ -85,7 +82,6 @@ (#offset! @close.key 0 -1 0 0))) ; snippet - (snippet_statement (snippet_start (block_start_tag diff --git a/after/queries/templ/matchup.scm b/after/queries/templ/matchup.scm index 7414426..83efc3d 100644 --- a/after/queries/templ/matchup.scm +++ b/after/queries/templ/matchup.scm @@ -1,27 +1,32 @@ ; inherits: quote [ - (component_if_statement) - (conditional_attribute_if_statement) + (component_if_statement) + (conditional_attribute_if_statement) ] @scope.if ; 'else' -("else" @mid.if.1) +"else" @mid.if.1 ; if -("if" @open.if) +"if" @open.if ; switch -(component_switch_statement "switch" @open.switch - (component_switch_expression_case "case" @mid.switch.1) - (component_switch_default_case "default" @mid.switch.2)) @scope.switch +(component_switch_statement + "switch" @open.switch + (component_switch_expression_case + "case" @mid.switch.1) + (component_switch_default_case + "default" @mid.switch.2)) @scope.switch [ - (element) - (script_element) + (element) + (script_element) ] @scope.tag -(tag_start (element_identifier) @open.tag) +(tag_start + (element_identifier) @open.tag) + (tag_end (element_identifier) @close.tag (#offset! @close.tag 0 -1 0 0)) @@ -30,8 +35,7 @@ (style_tag_start) @open.style (#offset! @open.style 0 1 0 -1) (style_tag_end) @close.style - (#offset! @close.style 0 1 0 -1) - ) @scope.style + (#offset! @close.style 0 1 0 -1)) @scope.style (self_closing_tag (element_identifier) @open.selftag diff --git a/after/queries/typescript/matchup.scm b/after/queries/typescript/matchup.scm index 10c5d4f..3311460 100644 --- a/after/queries/typescript/matchup.scm +++ b/after/queries/typescript/matchup.scm @@ -1,9 +1,9 @@ ; inherits: ecma -(type_arguments) @scope.typeargs -(type_arguments) "<" @open.typeargs -(type_arguments) ">" @close.typeargs +(type_arguments + "<" @open.typeargs + ">" @close.typeargs) @scope.typeargs -(type_parameters) @scope.typeparams -(type_parameters) "<" @open.typeparams -(type_parameters) ">" @close.typeparams +(type_parameters + "<" @open.typeparams + ">" @close.typeparams) @scope.typeparams diff --git a/after/queries/vim/matchup.scm b/after/queries/vim/matchup.scm index 848d46c..10f5b06 100644 --- a/after/queries/vim/matchup.scm +++ b/after/queries/vim/matchup.scm @@ -1,22 +1,30 @@ (function_definition "function" @open.function "endfunction" @close.function) @scope.function + (return_statement "return" @mid.function.1) (if_statement "if" @open.if "endif" @close.if) @scope.if + (elseif_statement "elseif" @mid.if.1) + (else_statement "else" @mid.if.2) (for_loop "for" @open.loop "endfor" @close.loop) @scope.loop + (while_loop "while" @open.loop "endwhile" @close.loop) @scope.loop -(continue_statement "continue" @mid.loop.1) -(break_statement "break" @mid.loop.2) + +(continue_statement + "continue" @mid.loop.1) + +(break_statement + "break" @mid.loop.2) diff --git a/after/queries/vue/matchup.scm b/after/queries/vue/matchup.scm index 8f954cc..6a84f86 100644 --- a/after/queries/vue/matchup.scm +++ b/after/queries/vue/matchup.scm @@ -1,13 +1,15 @@ ; inherits: quote [ - (element) - (script_element) - (style_element) - (template_element) + (element) + (script_element) + (style_element) + (template_element) ] @scope.tag -(start_tag (tag_name) @open.tag) +(start_tag + (tag_name) @open.tag) + (end_tag (tag_name) @close.tag (#offset! @close.tag 0 -1 0 0)) diff --git a/after/queries/zig/matchup.scm b/after/queries/zig/matchup.scm index 5ec4bfb..2a04c94 100644 --- a/after/queries/zig/matchup.scm +++ b/after/queries/zig/matchup.scm @@ -2,20 +2,27 @@ (function_declaration "fn" @open.function) @scope.function + (return_expression "return" @mid.function.1) ; 'else' and 'else if' (else_clause - "else" @_start (if_statement "if" @_end)? - (#make-range! "mid.if.1" @_start @_end)) + "else" @mid.if.1 + (if_statement + "if" @mid.if.1)?) ; if ((if_statement "if" @open.if) @scope.if - (#not-has-parent? @scope.if else_clause)) + (#not-has-parent? @scope.if else_clause)) ; Loops -(while_statement "while" @open.loop) @scope.loop -(break_expression "break" @mid.loop.1) -(continue_expression "continue" @mid.loop.2) +(while_statement + "while" @open.loop) @scope.loop + +(break_expression + "break" @mid.loop.1) + +(continue_expression + "continue" @mid.loop.2) diff --git a/autoload/matchup.vim b/autoload/matchup.vim index 5ca4fc4..f159a40 100644 --- a/autoload/matchup.vim +++ b/autoload/matchup.vim @@ -76,6 +76,13 @@ function! s:init_options() call s:init_option('matchup_where_separator', '') call s:init_option('matchup_matchpref', {}) + + call s:init_option('matchup_treesitter_enabled', has('nvim') ? v:true : v:false) + call s:init_option('matchup_treesitter_disabled', {}) + call s:init_option('matchup_treesitter_include_match_words', v:false) + call s:init_option('matchup_treesitter_enable_quotes', v:true) + call s:init_option('matchup_treesitter_disable_virtual_text', v:true) + call s:init_option('matchup_treesitter_stopline', 400) endfunction function! s:init_option(option, default) @@ -408,17 +415,7 @@ function! s:treesitter_init_module() " {{{1 if !matchup#loader#_treesitter_may_be_supported() return endif - - lua require'treesitter-matchup'.init() - - augroup matchup_filetype_query - au! - autocmd FileType query - \ augroup MatchupTreesitter|augroup END - \|autocmd! MatchupTreesitter BufWritePost - \ call v:lua.require('treesitter-matchup.third-party.query') - \.invalidate_query_file(expand('%:p')) - augroup END + " TODO: do something? endfunction "}}}1 diff --git a/autoload/matchup/loader.vim b/autoload/matchup/loader.vim index e78243b..6c28972 100644 --- a/autoload/matchup/loader.vim +++ b/autoload/matchup/loader.vim @@ -25,30 +25,13 @@ function! matchup#loader#init_buffer() abort " {{{1 let [l:no_words, l:filt_words] = [0, 0] if s:ts_may_be_supported && matchup#ts_engine#is_enabled(bufnr('%')) let l:has_ts = 1 - if matchup#ts_engine#get_option(bufnr('%'), 'include_match_words') + if g:matchup_treesitter_include_match_words let l:filt_words = 1 else let l:no_words = 1 endif endif - let l:has_ts_hl = 0 - if s:ts_may_be_supported && matchup#ts_engine#is_hl_enabled(bufnr('%')) - let l:has_ts_hl = 1 - - if matchup#ts_engine#get_option( - \ bufnr('%'), 'additional_vim_regex_highlighting') - if empty(&syntax) - set syntax=ON - else - augroup matchup_syntax - au! - autocmd VimEnter * if empty(&syntax) | set syntax=ON | endif - augroup END - endif - endif - endif - " initialize lists of delimiter pairs and regular expressions " this is the data obtained from parsing b:match_words let b:matchup_delim_lists = s:init_delim_lists(l:no_words, l:filt_words) @@ -58,7 +41,7 @@ function! matchup#loader#init_buffer() abort " {{{1 let b:matchup_delim_re = s:init_delim_regexes() " process b:match_skip - if l:has_ts_hl + if l:has_ts let b:matchup_delim_skip \ = "matchup#ts_syntax#skip_expr(s:effline('.'),s:effcol('.'))" else @@ -93,7 +76,7 @@ function! matchup#loader#_treesitter_may_be_supported() abort endfunction let s:ts_may_be_supported = has('nvim-0.5.0') && exists('*luaeval') - \ && luaeval('pcall(require, "treesitter-matchup")') + \ && luaeval('pcall(require, "treesitter-matchup.internal")') " }}}1 function! matchup#loader#bufwinenter() abort " {{{1 diff --git a/autoload/matchup/matchparen.vim b/autoload/matchup/matchparen.vim index 1eb28a9..b714b14 100644 --- a/autoload/matchup/matchparen.vim +++ b/autoload/matchup/matchparen.vim @@ -1204,8 +1204,7 @@ function! s:add_matches(corrlist, ...) " {{{1 if exists('s:ns_id') if strlen(l:corr.match) == 0 \ && matchup#loader#_treesitter_may_be_supported() - \ && !matchup#ts_engine#get_option( - \ bufnr('%'), 'disable_virtual_text') + \ && !g:matchup_treesitter_disable_virtual_text if hlexists('MatchupVirtualText') let l:group = 'MatchupVirtualText' endif diff --git a/autoload/matchup/ts_engine.vim b/autoload/matchup/ts_engine.vim index 96f788f..ce8c1f5 100644 --- a/autoload/matchup/ts_engine.vim +++ b/autoload/matchup/ts_engine.vim @@ -15,33 +15,12 @@ function! s:forward(fn, ...) endfunction function! matchup#ts_engine#is_enabled(bufnr) abort - if !has('nvim-0.5.0') + if !has('nvim-0.9.0') return 0 endif return +s:forward('is_enabled', a:bufnr) endfunction -function! matchup#ts_engine#is_hl_enabled(bufnr) abort - if !has('nvim-0.5.0') - return 0 - endif - return +s:forward('is_hl_enabled', a:bufnr) -endfunction - -function! matchup#ts_engine#get_option(bufnr, opt_name) abort - return s:forward('get_option', a:bufnr, a:opt_name) -endfunction - -let s:attached = {} - -function! matchup#ts_engine#attach(bufnr, lang) abort - let s:attached[a:bufnr] = a:lang -endfunction - -function! matchup#ts_engine#detach(bufnr) abort - unlet s:attached[a:bufnr] -endfunction - function! matchup#ts_engine#get_delim(opts) abort call matchup#perf#tic('ts_engine.get_delim') diff --git a/doc/matchup.txt b/doc/matchup.txt index e0ae14a..aa8909f 100644 --- a/doc/matchup.txt +++ b/doc/matchup.txt @@ -272,35 +272,21 @@ important special cases. *matchup-treesitter* Tree-sitter integration~ -Note: Currently this feature is possible in neovim only. Only the latest -version of neovim and nvim-treesitter is supported. +Note: Currently this feature is possible in Neovim only. Only the latest +stable version of Neovim is supported. match-up has support for language syntax provided by tree-sitter. The list -of supported languages is available here. This feature requires manual -opt-in in your init.vim and requires nvim-treesitter to be installed. > +of supported languages is available here. - Plug 'nvim-treesitter/nvim-treesitter' - lua < @@ -542,7 +528,7 @@ Options~ *g:matchup_delim_nomids* If set to 1, middle words (like `return`) are not matched to start and - end words for higlighting and motions. + end words for higlighting and motions. > let g:matchup_delim_noskips = 0 < @@ -552,10 +538,82 @@ Options~ When enabled (the default), the plugin will be loaded for all buffers, including ones without a file type set. This allows matching to be done in - new buffers and plain text files but adds a small start-up cost to vim. + new buffers and plain text files but adds a small start-up cost to vim. > + + let g:matchup_delim_start_plaintext = 0 +< Default: 1 +*g:matchup_treesitter_enabled* + + When enabled (the default on Neovim), the plugin will use the builtin + |treesitter| interface to compute matches. > + + let g:matchup_treesitter_enabled = v:true +< + + Default: v:true + + Note: expects either |v:true| or |v:false|. + +*g:matchup_treesitter_disabled* + + List of treesitter languages (not filetypes, see |treesitter-language|) for + which the treesitter engine will be disabled > + + let g:matchup_treesitter_disabled = [] +< + + Default: [] + +*g:matchup_treesitter_include_match_words* + + Additionally include traditional vim regex matches for symbols. For example, + highlights `/* */` comments in C++ which are not supported in tree-sitter + matching. > + + let g:matchup_treesitter_include_match_words = v:false +< + + Default: v:false + + Note: expects either |v:true| or |v:false|. + +*g:matchup_treesitter_disable_virtual_text* + + Do not use virtual text to highlight the virtual end of a block, for + languages without explicit end markers (e.g., Python). > + + let g:matchup_treesitter_disable_virtual_text = v:true +< + + Default: v:true + + Note: expects either |v:true| or |v:false|. + +*g:matchup_treesitter_enable_quotes* + + Include quotes as possible matches. > + + let g:matchup_treesitter_enable_quotes = v:true +< + + Default: v:true + + Note: expects either |v:true| or |v:false|. + +*g:matchup_treesitter_stopline* + + The number of lines to parse using treesitter in either direction while + highlighting matches, using motions and textobjects. Set this + conservatively since high values may cause performance issues. > + + let g:matchup_treesitter_stopline = 400 +< + + Default: 400 + Variables~ *b:match_words* @@ -666,7 +724,7 @@ Module matchparen~ `MatchupStatusOffscreen()` can be used to get the text. `'popup'`: Use a popup window (requires at least vim 8.1.1406) or - a floating window (in neovim) to show the off-screen match. + a floating window (in Neovim) to show the off-screen match. scrolloff~ When enabled, off-screen matches will not be shown in the statusline while @@ -696,7 +754,7 @@ Module matchparen~ Default: 0 border~ - For floating window on neovim only: set to add a border. If the value + For floating window on Neovim only: set to add a border. If the value is the integer 1, default borders are enabled. A list or string can be specified as described in |nvim_open_win()|. @@ -822,7 +880,7 @@ the function |timer_pause|, version 7.4.2180 and after. *g:matchup_matchparen_end_sign* - (neovim only) Configure the virtual symbol shown for closeless matches in languages like + (Neovim only) Configure the virtual symbol shown for closeless matches in languages like C++ and python. if (true) @@ -834,7 +892,7 @@ the function |timer_pause|, version 7.4.2180 and after. *MatchupVirtualText* - (neovim only) You can also configure the color and style of the virtual text. + (Neovim only) You can also configure the color and style of the virtual text. :hi MatchupVirtualText ctermbg=blue guibg=lightblue gui=italic diff --git a/lua/match-up.lua b/lua/match-up.lua index 2815582..4fedc5c 100644 --- a/lua/match-up.lua +++ b/lua/match-up.lua @@ -1,23 +1,124 @@ local M = {} +---@class matchup.DelimConfig +---@field count_fail 0|1 +---@field nomids 0|1 +---@field noskips 0|1|2 +---@field start_plaintext 0|1 +---@field stopline integer + +---@class matchup.HotfixConfig +---@field enabled 0|1 + +---@class matchup.MappingsConfig +---@field enabled 0|1 + +---@class matchup.OffscreenConfig +---@field method 'status'|'status_manual'|'popup' +---@field scrolloff 0|1 +---@field fullwidth 0|1 +--@field highlight string Vim exclusive option. Intentionally excluded from type +--@field syntax_hl 0|1 Vim exclusive option. Intentionally excluded from type +---@field border 1|string|string[] + +---@class matchup.MatchparenConfig +---@field deferred 0|1 +---@field deferred_fade_time integer +---@field deferred_hide_delay integer +---@field deferred_show_delay integer +---@field enabled 0|1 +---@field end_sign string +---@field hi_background 0|1 +---@field hi_surround_always 0|1 +---@field insert_timeout integer +---@field nomode string +---@field offscreen matchup.OffscreenConfig +---@field pumvisible 0|1 +---@field singleton 0|1 +---@field stopline integer +---@field timeout integer + +---@class matchup.MatchprefHtmlConfig +---@field nolists 0|1 +---@field tagnameonly 0|1 + +---@class matchup.MatchprefConfig +---@field html matchup.MatchprefHtmlConfig + +---@class matchup.MotionConfig +---@field cursor_end 0|1 +---@field enabled 0|1 +---@field override_Npercent integer + +---@class matchup.MouseConfig +---@field enabled 0|1 + +---@class matchup.OverrideConfig +---@field vimtex 1|0 + +---@class matchup.SurroundConfig +---@field enabled 0|1 + +---@class matchup.TextObjConfig +---@field enabled 0|1 +---@field linewise_operators string[] + +---@class matchup.TransmuteConfig +---@field enabled 0|1 + +---@class matchup.TreesitterConfig +---@field enabled boolean +---@field disabled string[] +---@field include_match_words boolean +---@field disable_virtual_text boolean +---@field enable_quotes boolean +---@field stopline integer + +---@class matchup.Config +---@field delim matchup.DelimConfig +---@field enabled 0|1 +---@field hotfix matchup.HotfixConfig +---@field mappings matchup.MappingsConfig +---@field matchparen matchup.MatchparenConfig +---@field matchpref matchup.MatchprefConfig +---@field motion matchup.MotionConfig +---@field mouse matchup.MouseConfig +---@field override matchup.OverrideConfig +---@field surround matchup.SurroundConfig +---@field text_obj matchup.TextObjConfig +---@field transmute matchup.TransmuteConfig +---@field treesitter matchup.TreesitterConfig + +---@param opts matchup.Config +---@param validate boolean local function do_setup(opts, validate) - for mod, elem in pairs(opts) do - for key, val in pairs(type(elem) == 'table' and elem or {}) do - local opt = 'matchup_'..mod..'_'..key - if validate and vim.g[opt] == nil then - error(string.format('invalid option name %s.%s', mod, key)) + for mod, elem in pairs(opts --[[@as table>]]) do + if type(elem) == 'table' then + for key, val in pairs(elem) do + local opt = 'matchup_'..mod..'_'..key + if validate and vim.g[opt] == nil then + error(('invalid option name %s.%s'):format(mod, key)) + end + vim.g[opt] = val end - vim.g[opt] = val + else + if validate and vim.g[mod] == nil then + error(('invalid option name %s'):format(mod)) + end + vim.g[mod] = elem end end end +---@param opts matchup.Config|{sync: boolean} function M.setup(opts) local sync = opts.sync if sync then vim.cmd[[runtime! plugin/matchup.vim]] end + opts.sync = nil + ---@cast opts matchup.Config do_setup(opts, sync) end diff --git a/lua/treesitter-matchup.lua b/lua/treesitter-matchup.lua deleted file mode 100644 index cec414f..0000000 --- a/lua/treesitter-matchup.lua +++ /dev/null @@ -1,21 +0,0 @@ -if not pcall(require, 'nvim-treesitter') then - return {init = function() end} -end - -local treesitter = require 'nvim-treesitter' -local queries = require 'nvim-treesitter.query' - -local M = {} - -function M.init() - treesitter.define_modules { - matchup = { - module_path = 'treesitter-matchup.internal', - is_supported = function(lang) - return queries.has_query_files(lang, 'matchup') - end - } - } -end - -return M diff --git a/lua/treesitter-matchup/internal.lua b/lua/treesitter-matchup/internal.lua index 1e12e56..3f66988 100644 --- a/lua/treesitter-matchup/internal.lua +++ b/lua/treesitter-matchup/internal.lua @@ -1,18 +1,10 @@ -if not pcall(require, 'nvim-treesitter') then - return {is_enabled = function(bufnr) return 0 end, - is_hl_enabled = function(bufnr) return 0 end} -end - local vim = vim local api = vim.api -local ts = require'treesitter-matchup.compat' -local configs = require'nvim-treesitter.configs' -local parsers = require'nvim-treesitter.parsers' -local queries = require'treesitter-matchup.third-party.query' -local ts_utils = require'nvim-treesitter.ts_utils' +local ts = vim.treesitter +local memoize = require'treesitter-matchup.third-party.ts-utils'.memoize + local lru = require'treesitter-matchup.third-party.lru' local util = require'treesitter-matchup.util' -local utils2 = require'treesitter-matchup.third-party.utils' local unpack = unpack or table.unpack @@ -20,69 +12,177 @@ local M = {} local cache = lru.new(150) + +---@param lang string +---@param bufnr integer +local function is_enabled(lang, bufnr) + local enabled = vim.g.matchup_treesitter_enabled + local buf_enabled = vim.b[bufnr].matchup_treesitter_enabled + local lang_disabled = vim.list_contains(vim.g.matchup_treesitter_disabled, lang) + + if buf_enabled == false then + return false + end + + return enabled and not lang_disabled +end + +---@param bufnr integer? +---@return boolean function M.is_enabled(bufnr) bufnr = bufnr or api.nvim_get_current_buf() - local lang = parsers.get_buf_lang(bufnr) - return configs.is_enabled('matchup', lang, bufnr) + if not api.nvim_buf_is_loaded(bufnr) then + return false + end + local lang = ts.language.get_lang(vim.bo[bufnr].filetype) + if not lang then + return false + end + local _, err = ts.get_parser(bufnr, nil, {error = false}) + if err then + return false + end + return is_enabled(lang, bufnr) end -function M.is_hl_enabled(bufnr) - bufnr = bufnr or api.nvim_get_current_buf() - local lang = parsers.get_buf_lang(bufnr) - return configs.is_enabled('highlight', lang, bufnr) +---@param bufnr integer +---@param root TSNode +---@param lang string +---@return string +local function buf_root_lang_hash(bufnr, root, lang) + return tostring(bufnr) .. root:id() .. '_' .. lang end -M.get_matches = ts_utils.memoize_by_buf_tick(function(bufnr) - local parser = parsers.get_parser(bufnr) - local matches = {} +---@class matchup.treesitter.MatchInfo +---@field range Range4 +---@field length integer +---@field last_node TSNode +---@field text string + +---@class matchup.treesitter.MatchInfoWrapper +---@field info matchup.treesitter.MatchInfo + +---@class matchup.treesitter.Match +---@field scope? table +---@field open? table +---@field mid? table> +---@field close? table +---@field skip? matchup.treesitter.MatchInfoWrapper + +---@param bufnr integer +---@param root TSNode +---@param lang string +---@return matchup.treesitter.Match[] +local get_memoized_matches = memoize(function(bufnr, root, lang) + local query_name = 'matchup' + local query = ts.query.get(lang, query_name) + + if not query then + return {} + end + + local out = {} ---@type matchup.treesitter.Match[] + for _, match, metadata in query:iter_matches(root, bufnr) do + local match_info = {} + for id, nodes in pairs(match) do + local first = nodes[1] + local last = nodes[#nodes] + + ---@type integer, integer, integer + local start_row, start_col , start_byte = unpack(ts.get_range(first, bufnr, metadata)) + ---@type integer, integer, integer, integer, integer, integer + local _, _, _, end_row, end_col , end_byte = unpack(ts.get_range(last, bufnr, metadata)) + local range = { start_row, start_col, end_row, end_col } + local length = end_byte - start_byte + + if end_col == 0 then + if start_row == end_row then + start_col = -1 + start_row = start_row - 1 + end + end_col = -1 + end_row = end_row - 1 + end + local lines = api.nvim_buf_get_text(bufnr, start_row, start_col, end_row, end_col, {}) + local text = table.concat(lines, '\n') + + local name = query.captures[id] + local path = vim.split(name, '.', { plain = true }) + + local current = match_info ---@type table> + for _, segment in ipairs(path) do + current[segment] = current[segment] or {} + current = current[segment] + end + current.info = { + range = range, + length = length, + last_node = last, + text = text, + } + end + table.insert(out, match_info) + end + + return out +end, buf_root_lang_hash) + +---@param bufnr integer +---@return matchup.treesitter.Match[] +M.get_matches = function(bufnr) + local parser = ts.get_parser(bufnr) + local matches = {} ---@type matchup.treesitter.Match[] if parser then + -- NOTE: assummes that we are always parsing the current window. May cause + -- issues if that's not always the case + local win = api.nvim_get_current_win() + local cur_row = unpack(api.nvim_win_get_cursor(win)) + local stopline = vim.g.matchup_treesitter_stopline ---@type integer + local start_row = math.max(cur_row - stopline, 0) + local end_row = math.min(cur_row + stopline, api.nvim_buf_line_count(bufnr)) + + parser:parse({start_row, end_row}) parser:for_each_tree(function(tree, lang_tree) if not tree or lang_tree:lang() == 'comment' then return end local lang = lang_tree:lang() - local group_results = queries.collect_group_results( - bufnr, 'matchup', tree:root(), lang) or {} + local group_results = get_memoized_matches(bufnr, tree:root(), lang) vim.list_extend(matches, group_results) end) end return matches -end) +end local function _time() - local s, u = vim.loop.gettimeofday() + local s, u = vim.uv.gettimeofday() return s * 1000 + u * 1e-3 end ---- Returns a (mostly) unique id for this node --- Also supports nvim-treesitter's range object -local function _node_id(node) - if not node then - return nil - end - if node:type() == 'nvim-treesitter-range' then - return string.format('range_%d_%d_%d_%d', node:range()) - end - return node:id() +--- Returns a (mostly) unique id for this range +---@param range Range4 +---@return string +function M.range_id(range) + return ('range_%d_%d_%d_%d'):format(unpack(range)) end --- Get all nodes belonging to defined scopes (organized by key) -M.get_scopes = ts_utils.memoize_by_buf_tick(function(bufnr) +---@param bufnr integer +---@return table> +M.get_scopes = function(bufnr) local matches = M.get_matches(bufnr) - local scopes = {} + local scopes = {} ---@type table> for _, match in ipairs(matches) do if match.scope then for key, scope in pairs(match.scope) do - local id = _node_id(scope.node) - if scope.node then - if not scopes[key] then - scopes[key] = {} - end + if scope.info then + local id = M.range_id(scope.info.range) + scopes[key] = scopes[key] or {} scopes[key][id] = true end end @@ -90,39 +190,41 @@ M.get_scopes = ts_utils.memoize_by_buf_tick(function(bufnr) end return scopes -end) +end -M.get_active_nodes = ts_utils.memoize_by_buf_tick(function(bufnr) - -- TODO: why do we need to force a parse? - if not pcall(function() parsers.get_parser():parse() end) then - -- TODO workaround a crash due to tree-sitter parsing - return {{ open={}, mid={}, close={} }, {}} - end +---@class matchup.treesitter.Matches +---@field open matchup.treesitter.MatchInfo[] +---@field mid matchup.treesitter.MatchInfo[] +---@field close matchup.treesitter.MatchInfo[] +---@param bufnr integer +---@return [matchup.treesitter.Matches, table] +M.get_active_matches = function(bufnr) local matches = M.get_matches(bufnr) - local nodes = { open = {}, mid = {}, close = {} } + ---@type matchup.treesitter.Matches + local info = { open = {}, mid = {}, close = {} } + ---@type table local symbols = {} + local enable_quotes = vim.g.matchup_treesitter_enable_quotes for _, match in ipairs(matches) do if match.open then for key, open in pairs(match.open) do - local reject = key:find('quote') - and not M.get_option(bufnr, 'enable_quotes') - local id = _node_id(open.node) - if not reject and open.node and symbols[id] == nil then - table.insert(nodes.open, open.node) + local reject = key:find('quote') and not enable_quotes + local id = M.range_id(open.info.range) + if not reject and open.info and symbols[id] == nil then + table.insert(info.open, open.info) symbols[id] = key end end end if match.close then for key, close in pairs(match.close) do - local reject = key:find('quote') - and not M.get_option(bufnr, 'enable_quotes') - local id = _node_id(close.node) - if not reject and close.node and symbols[id] == nil then - table.insert(nodes.close, close.node) + local reject = key:find('quote') and not enable_quotes + local id = M.range_id(close.info.range) + if not reject and close.info and symbols[id] == nil then + table.insert(info.close, close.info) symbols[id] = key end end @@ -130,9 +232,9 @@ M.get_active_nodes = ts_utils.memoize_by_buf_tick(function(bufnr) if match.mid then for key, mid_group in pairs(match.mid) do for _, mid in pairs(mid_group) do - local id = _node_id(mid.node) - if mid.node and symbols[id] == nil then - table.insert(nodes.mid, mid.node) + local id = M.range_id(mid.info.range) + if mid.info and symbols[id] == nil then + table.insert(info.mid, mid.info) symbols[id] = key end end @@ -140,19 +242,25 @@ M.get_active_nodes = ts_utils.memoize_by_buf_tick(function(bufnr) end end - return {nodes, symbols} -end) + return {info, symbols} +end -function M.containing_scope(node, bufnr, key) +---@param info matchup.treesitter.MatchInfo? +---@param bufnr integer? +---@param key string +---@return TSNode|nil +function M.containing_scope(info, bufnr, key) bufnr = bufnr or api.nvim_get_current_buf() local scopes = M.get_scopes(bufnr) - if not node or not scopes or not scopes[key] then return end + if not info or not scopes or not scopes[key] then return end - local iter_node = node + ---@type TSNode|nil + local iter_node = info.last_node while iter_node ~= nil do - if scopes[key][_node_id(iter_node)] then + ---@diagnostic disable-next-line: missing-fields LuaLS bug + if scopes[key][M.range_id({iter_node:range()})] then return iter_node end iter_node = iter_node:parent() @@ -161,27 +269,36 @@ function M.containing_scope(node, bufnr, key) return nil end -local function _node_text(node, bufnr) - local text = ts.get_node_text(node, bufnr) +---@param info matchup.treesitter.MatchInfo +---@return string +local function text_until_newline(info) + local text = info.text return text:match("([^\n]+).*") end --- Fill in a match result based on a seed node -function M.do_node_result(initial_node, bufnr, opts, side, key) +---@param info matchup.treesitter.MatchInfo +---@param bufnr integer +---@param opts table +---@param side matchup.Side? +---@param key string? +function M.do_match_result(info, bufnr, opts, side, key) if not side or not key then return nil end - local scope = M.containing_scope(initial_node, bufnr, key) + local scope = M.containing_scope(info, bufnr, key) if not scope then return nil end - local row, col, _ = initial_node:start() + ---@type integer, integer + local row, col = unpack(info.range) + ---@class matchup.Delim local result = { type = 'delim_py', - match = _node_text(initial_node, bufnr), + match = text_until_newline(info), side = side, lnum = row + 1, cnum = col + 1, @@ -191,9 +308,9 @@ function M.do_node_result(initial_node, bufnr, opts, side, key) _id = util.uuid4(), } - local info = { + local cached_info = { bufnr = bufnr, - initial_node = initial_node, + info = info, row = row, col = col, key = key, @@ -201,11 +318,36 @@ function M.do_node_result(initial_node, bufnr, opts, side, key) search_range = {scope:range()}, } - cache:set(result._id, info) + cache:set(result._id, cached_info) return result end +---@param info matchup.treesitter.MatchInfo +---@param line integer +---@param col integer +---@return boolean +local function is_in_range(info, line, col) + ---@type integer, integer, integer, integer + local r_start_row, r_start_col, r_end_row, r_end_col = unpack(info.range) + local p_start_row, p_start_col, p_end_row, p_end_col = line, col, line, col + 1 + + if p_start_row < r_start_row then + return false + elseif p_start_row == r_start_row and p_start_col < r_start_col then + return false + end + + if p_end_row > r_end_row then + return false + elseif p_end_row == r_end_row and p_end_col > r_end_col then + return false + end + + return true +end + +---@type table local side_table = { open = {'open'}, mid = {'mid'}, @@ -215,25 +357,32 @@ local side_table = { open_mid = {'mid', 'open'}, } +---@alias matchup.Side 'open'|'mid'|'close'|'both'|'both_all'|'open_mid' +---@alias matchup.Direction 'current'|'next'|'prev' +---@alias matchup.Type 'delim_text'|'delim_all'|'all' + +---@param bufnr integer +---@param opts {direction: matchup.Direction, side: matchup.Side, type: matchup.Type} function M.get_delim(bufnr, opts) if opts.direction == 'current' then -- get current by query - local active_nodes, symbols = unpack(M.get_active_nodes(bufnr)) + local active_matches, symbols = unpack(M.get_active_matches(bufnr)) local cursor = api.nvim_win_get_cursor(0) local smallest_len = 1e31 + ---@type {info: matchup.treesitter.MatchInfo, side: matchup.Side, key: string}|nil local result_info = nil for _, side in ipairs(side_table[opts.side]) do if not(side == 'mid' and vim.g.matchup_delim_nomids > 0) then - for _, node in ipairs(active_nodes[side]) do - if utils2.is_in_node_range(node, cursor[1]-1, cursor[2]) then - local len = ts_utils.node_length(node) + for _, info in ipairs(active_matches[side] --[=[@as matchup.treesitter.MatchInfo[]]=]) do + if is_in_range(info, cursor[1] - 1, cursor[2]) then + local len = info.length if len < smallest_len then smallest_len = len result_info = { - node = node, + info = info, side = side, - key = symbols[_node_id(node)] + key = symbols[M.range_id(info.range)] } end end @@ -242,7 +391,7 @@ function M.get_delim(bufnr, opts) end if result_info then - return M.do_node_result(result_info.node, bufnr, opts, + return M.do_match_result(result_info.info, bufnr, opts, result_info.side, result_info.key) end @@ -253,16 +402,17 @@ function M.get_delim(bufnr, opts) -- look forwards or backwards for an active node local max_col = 1e5 - local active_nodes, symbols = unpack(M.get_active_nodes(bufnr)) + local active_matches, symbols = unpack(M.get_active_matches(bufnr)) local cursor = api.nvim_win_get_cursor(0) local cur_pos = max_col * (cursor[1]-1) + cursor[2] - local closest_node, closest_dist = nil, 1e31 + local closest_match, closest_dist = nil, 1e31 local result_info = {} for _, side in ipairs(side_table[opts.side]) do - for _, node in ipairs(active_nodes[side]) do - local row, col, _ = node:start() + for _, info in ipairs(active_matches[side]--[=[@as matchup.treesitter.MatchInfo[]]=]) do + ---@type integer, integer + local row, col = unpack(info.range) local pos = max_col * row + col if opts.direction == 'next' and pos >= cur_pos @@ -271,61 +421,66 @@ function M.get_delim(bufnr, opts) local dist = math.abs(pos - cur_pos) if dist < closest_dist then closest_dist = dist - closest_node = node - result_info = { side=side, key=symbols[_node_id(node)] } + closest_match = info + result_info = { side=side, key=symbols[M.range_id(info.range)] } end end end end - if closest_node == nil then + if closest_match == nil then return nil end - return M.do_node_result(closest_node, bufnr, opts, + return M.do_match_result(closest_match, bufnr, opts, result_info.side, result_info.key) end +---@param delim matchup.Delim +---@param down 1|0 +---@param bufnr integer +---@return [string, integer, integer][] function M.get_matching(delim, down, bufnr) - down = down > 0 + local is_down = down > 0 - local info = cache:get(delim._id) or {} - if info.bufnr ~= bufnr then + local cached_info = cache:get(delim._id) or {} + if cached_info.bufnr ~= bufnr then return {} end - local matches = {} + local matches = {} ---@type [string, integer, integer][] - local sides + local sides ---@type ('open'|'mid'|'close')[] if vim.g.matchup_delim_nomids > 0 then - sides = down and {'close'} or {'open'} + sides = is_down and {'close'} or {'open'} else - sides = down and {'mid', 'close'} or {'mid', 'open'} + sides = is_down and {'mid', 'close'} or {'mid', 'open'} end - local active_nodes, symbols = unpack(M.get_active_nodes(bufnr)) + local active_matches, symbols = unpack(M.get_active_matches(bufnr)) local got_close = false - local stop_time = _time() + vim.fn['matchup#perf#timeout']() + local stop_time = _time() + vim.fn['matchup#perf#timeout']() ---@type number for _, side in ipairs(sides) do - for _, node in ipairs(active_nodes[side]) do - local row, col, _ = node:start() + for _, info in ipairs(active_matches[side]--[=[@as matchup.treesitter.MatchInfo[]]=]) do + ---@type integer, integer + local row, col = unpack(info.range) if _time() > stop_time then return {} end - if info.initial_node ~= node and symbols[_node_id(node)] == info.key - and (down and (row > info.row or row == info.row and col > info.col) - or not down and (row < info.row or row == info.row and col < info.col)) - and (row >= info.search_range[1] - and row <= info.search_range[3]) then + if cached_info.info ~= info and symbols[M.range_id(info.range)] == cached_info.key + and (is_down and (row > cached_info.row or row == cached_info.row and col > cached_info.col) + or not is_down and (row < cached_info.row or row == cached_info.row and col < cached_info.col)) + and (row >= cached_info.search_range[1] + and row <= cached_info.search_range[3]) then - local target_scope = M.containing_scope(node, bufnr, info.key) - if info.scope == target_scope then - local text = _node_text(node, bufnr) or '' + local target_scope = M.containing_scope(info, bufnr, cached_info.key) + if cached_info.scope == target_scope then + local text = text_until_newline(info) or '' table.insert(matches, {text, row + 1, col + 1}) if side == 'close' then @@ -342,45 +497,12 @@ function M.get_matching(delim, down, bufnr) end) -- no stop marker is found, use enclosing scope - if down and not got_close then - local row, col, _ = info.scope:end_() + if is_down and not got_close then + local row, col, _ = cached_info.scope:end_() table.insert(matches, {'', row + 1, col + 1}) end return matches end -local function opt_tbl_for_lang(opt, lang) - local is_table = type(opt) == "table" - if opt and (not is_table or vim.tbl_contains(opt, lang)) then - return true - end - return false -end - -function M.get_option(bufnr, opt_name) - local config = configs.get_module('matchup') or {} - local lang = parsers.get_buf_lang(bufnr) - if (opt_name == 'include_match_words' - or opt_name == 'additional_vim_regex_highlighting' - or opt_name == 'disable_virtual_text' - or opt_name == 'enable_quotes') then - return opt_tbl_for_lang(config[opt_name], lang) - end - error('invalid option ' .. opt_name) -end - -function M.attach(bufnr, lang) - if M.get_option(bufnr, 'additional_vim_regex_highlighting') - and api.nvim_buf_get_option(bufnr, 'syntax') == '' then - api.nvim_buf_set_option(bufnr, 'syntax', 'ON') - end - - api.nvim_call_function('matchup#ts_engine#attach', {bufnr, lang}) -end - -function M.detach(bufnr) - api.nvim_call_function('matchup#ts_engine#detach', {bufnr}) -end - return M diff --git a/lua/treesitter-matchup/syntax.lua b/lua/treesitter-matchup/syntax.lua index 1f2f4a7..42728e4 100644 --- a/lua/treesitter-matchup/syntax.lua +++ b/lua/treesitter-matchup/syntax.lua @@ -1,60 +1,37 @@ -if not pcall(require, 'nvim-treesitter') then - return { - is_active = function() return false end, - synID = function(lnum, col, transparent) - return vim.fn.synID(lnum, col, transparent) - end - } -end - local api = vim.api +local vts = vim.treesitter local hl_info = require'treesitter-matchup.third-party.hl-info' -local queries = require'treesitter-matchup.third-party.query' -local ts_utils = require'nvim-treesitter.ts_utils' -local parsers = require'nvim-treesitter.parsers' +local internal = require'treesitter-matchup.internal' local M = {} +---@param bufnr integer? +---@return boolean function M.is_active(bufnr) bufnr = bufnr or api.nvim_get_current_buf() return (hl_info.active() - and api.nvim_buf_get_option(bufnr, 'syntax') == '') + and vim.bo[bufnr].syntax == '') end --- Get all nodes that are marked as skip +---@param bufnr integer function M.get_skips(bufnr) - local matches = queries.get_matches(bufnr, 'matchup') + local matches = internal.get_matches(bufnr) - local skips = {} + local skips = {} ---@type table for _, match in ipairs(matches) do if match.skip then - skips[match.skip.node:id()] = 1 + skips[internal.range_id(match.skip.info.range)] = 1 end end return skips end -local function get_node_at_pos(cursor) - local cursor_range = { cursor[1] - 1, cursor[2] } - - local buf = vim.api.nvim_win_get_buf(0) - local root_lang_tree = parsers.get_parser(buf) - if not root_lang_tree then - return - end - local root = ts_utils.get_root_for_position( - cursor_range[1], cursor_range[2], root_lang_tree) - - if not root then - return - end - - return root:named_descendant_for_range( - cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2]) -end - +---@param lnum integer +---@param col integer +---@return boolean function M.lang_skip(lnum, col) local bufnr = api.nvim_get_current_buf() local skips = M.get_skips(bufnr) @@ -63,17 +40,21 @@ function M.lang_skip(lnum, col) return false end - local node = get_node_at_pos({lnum, col - 1}) + local node = vts.get_node({pos = {lnum - 1, col - 1}}) if not node then return false end - if skips[node:id()] then + ---@diagnostic disable-next-line: missing-fields LuaLS bug + if skips[internal.range_id({node:range()})] then return true end return false end +---@param lnum integer +---@param col integer +---@param transparent 1|0 function M.synID(lnum, col, transparent) if not M.is_active() then return vim.fn.synID(lnum, col, transparent) diff --git a/lua/treesitter-matchup/third-party/query.lua b/lua/treesitter-matchup/third-party/query.lua deleted file mode 100644 index 5c8960a..0000000 --- a/lua/treesitter-matchup/third-party/query.lua +++ /dev/null @@ -1,394 +0,0 @@ --- From https://github.com/nvim-treesitter/nvim-treesitter --- Copyright 2021 --- licensed under the Apache License 2.0 --- See nvim-treesitter.LICENSE-APACHE-2.0 - -local api = vim.api -local ts = require 'treesitter-matchup.compat' -local tsrange = require "nvim-treesitter.tsrange" -local utils = require "nvim-treesitter.utils" -local parsers = require "nvim-treesitter.parsers" -local caching = require "nvim-treesitter.caching" - -local M = {} - -local EMPTY_ITER = function() end - -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 - -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] = ts.get_query(lang, query_name) - end - - return cache[lang][query_name] - end - - --- Invalidates the query file cache. - --- If lang and query_name is both present, will reload for only the lang and query_name. - --- If only lang is present, will reload all query_names for that lang - --- If none are present, will reload everything - ---@param lang string - ---@param query_name string - function M.invalidate_query_cache(lang, query_name) - if lang and query_name then - cache[lang][query_name] = nil - elseif lang and not query_name then - for query_name0, _ in pairs(cache[lang]) do - M.invalidate_query_cache(lang, query_name0) - end - elseif not lang and not query_name then - 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 - -local function get_byte_offset(buf, row, col) - local lines = api.nvim_buf_get_lines(buf, row, row + 1, false) - if #lines < 1 then - return - end - return api.nvim_buf_get_offset(buf, row) + vim.fn.byteidx(lines[1], col) -end - -local 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.TSRange) -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, { all = false }) - - 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 #pred == 4 then - assert(type(pred[2]) == "string") - local path = pred[2] - insert_to_path( - prepared_match, - split(path .. ".node"), - tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]]) - ) - end - if pred[1] == "offset!" then - local path = type(pred[2]) == "string" and pred[2] or query.captures[pred[2]] - - local offset_node = match[pred[2]] - local range = {offset_node:range()} - local start_row_offset = pred[3] or 0 - local start_col_offset = pred[4] or 0 - local end_row_offset = pred[5] or 0 - local end_col_offset = pred[6] or 0 - - range[1] = range[1] + start_row_offset - range[2] = range[2] + start_col_offset - range[3] = range[3] + end_row_offset - range[4] = range[4] + end_col_offset - - insert_to_path(prepared_match, split(path..'.node'), - TSRange_from_table(bufnr, range)) - 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 - ----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 diff --git a/lua/treesitter-matchup/third-party/ts-utils.lua b/lua/treesitter-matchup/third-party/ts-utils.lua new file mode 100644 index 0000000..41db334 --- /dev/null +++ b/lua/treesitter-matchup/third-party/ts-utils.lua @@ -0,0 +1,28 @@ +-- From https://github.com/nvim-treesitter/nvim-treesitter +-- Copyright 2021 +-- licensed under the Apache License 2.0 +-- See nvim-treesitter.LICENSE-APACHE-2.0 + +local M = {} + +---Memoize a function using hash_fn to hash the arguments. +---@generic F: function +---@param fn F +---@param hash_fn fun(...): any +---@return F +function M.memoize(fn, hash_fn) + local cache = setmetatable({}, { __mode = 'kv' }) ---@type table + + 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 + +return M diff --git a/test/new/Makefile b/test/new/Makefile index 3b0283b..4b30c36 100644 --- a/test/new/Makefile +++ b/test/new/Makefile @@ -5,6 +5,7 @@ COVER = covimerage -q run --append --no-report \ --source $(CURDIR)/../../plugin MYVIM ?= nvim --headless MAKEFLAGS += --no-print-directory +VENV = . env/bin/activate; TESTS := $(wildcard test-*) @@ -19,27 +20,24 @@ sysinfo: @echo "**** SYSTEM INFORMATION ****" $(TESTS): env - @. env/bin/activate - mkdir -p cov.tmp - MYVIM="$(COVER) $(MYVIM)" $(MAKE) -C $@ + $(VENV) mkdir -p cov.tmp + $(VENV) MYVIM="$(COVER) $(MYVIM)" $(MAKE) -C $@ coverage: coverage.xml cov.tmp/coverage_covimerage: $(wildcard cov.tmp/_*) - coverage combine $^ + $(VENV) coverage combine $^ coverage.xml: env cov.tmp/coverage_covimerage - . env/bin/activate - coverage report -m - coverage html - coverage xml + $(VENV) coverage report -m + $(VENV) coverage html + $(VENV) coverage xml env: env/pyvenv.cfg env/pyvenv.cfg: python3 -m venv env - . env/bin/activate - pip install -r requirements.txt + $(VENV) pip install -r requirements.txt ifndef MAKECMDGOALS test: sysinfo diff --git a/test/new/common/bootstrap.vim b/test/new/common/bootstrap.vim index 49c773e..3987022 100644 --- a/test/new/common/bootstrap.vim +++ b/test/new/common/bootstrap.vim @@ -3,23 +3,6 @@ set packpath-=~/.config/nvim packpath-=~/.config/nvim/after let &rtp = '../../..,' . &rtp let &rtp = &rtp . ',../../../after' -if $TESTS_ENABLE_TREESITTER - let s:path = simplify(expand(':h').'/../../..') - let &rtp = s:path.'/test/vader/plugged/nvim-treesitter,' . &rtp - let &rtp .= ','.s:path.'/test/vader/plugged/nvim-treesitter/after' - - runtime! plugin/nvim-treesitter.vim - runtime! plugin/nvim-treesitter.lua - - lua < 0 call matchup#test#finished() endif -let s:expect_ts_engine = +$TESTS_ENABLE_TREESITTER +let g:matchup_treesitter_enabled = v:false +let s:expect_ts_engine = $MODE == 0 ? 0 : +$TESTS_ENABLE_TREESITTER if $MODE == 1 - lua <