neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit e5c2ce59058084a1d5c2107d557447e7c5c59ab9
parent c5167ffc185a94da6e227601b28fe2935c6ca1dc
Author: Peter Cardenas <16930781+PeterCardenas@users.noreply.github.com>
Date:   Sat, 19 Jul 2025 11:36:51 -0700

fix(treesitter): ":EditQuery [lang]" with injected languages #34914

Problem:
`:EditQuery` command accepts a language argument, but it doesn't
highlight properly for injected languages.

Solution:
- Fully parse with the root language and then filter the query on the
  child trees that are of the language requested.
- Also support completion (`EditQuery <tab>`).
Diffstat:
Mruntime/doc/treesitter.txt | 3+++
Mruntime/lua/vim/_defaults.lua | 8+++++++-
Mruntime/lua/vim/treesitter/dev.lua | 40+++++++++++++++++++++++++---------------
Mruntime/lua/vim/treesitter/language.lua | 11+++++++++++
Mruntime/lua/vim/treesitter/query.lua | 2++
5 files changed, 48 insertions(+), 16 deletions(-)

diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt @@ -1323,6 +1323,9 @@ edit({lang}) *vim.treesitter.query.edit()* Can also be shown with `:EditQuery`. *:EditQuery* + `:EditQuery <tab>` completes the treesitter parser names in the runtime + path. + If you move the cursor to a capture name ("@foo"), text matching the capture is highlighted in the source buffer. The query editor is a scratch buffer, use `:write` to save it. You can find example queries at diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua @@ -23,7 +23,13 @@ do vim.api.nvim_create_user_command('EditQuery', function(cmd) vim.treesitter.query.edit(cmd.fargs[1]) - end, { desc = 'Edit treesitter query', nargs = '?' }) + end, { + desc = 'Edit treesitter query', + nargs = '?', + complete = function() + return vim.treesitter.language._complete() + end, + }) vim.api.nvim_create_user_command('Open', function(cmd) vim.ui.open(assert(cmd.fargs[1])) diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua @@ -565,7 +565,8 @@ local edit_ns = api.nvim_create_namespace('nvim.treesitter.dev_edit') local function update_editor_highlights(query_win, base_win, lang) local base_buf = api.nvim_win_get_buf(base_win) local query_buf = api.nvim_win_get_buf(query_win) - local parser = assert(vim.treesitter.get_parser(base_buf, lang, { error = false })) + local root_lang = vim.treesitter.language.get_lang(vim.bo[base_buf].filetype) + local parser = assert(vim.treesitter.get_parser(base_buf, root_lang, { error = false })) api.nvim_buf_clear_namespace(base_buf, edit_ns, 0, -1) local query_content = table.concat(api.nvim_buf_get_lines(query_buf, 0, -1, false), '\n') @@ -581,21 +582,30 @@ local function update_editor_highlights(query_win, base_win, lang) end -- Remove the '@' from the cursor word cursor_word = cursor_word:sub(2) - local topline, botline = vim.fn.line('w0', base_win), vim.fn.line('w$', base_win) - for id, node in query:iter_captures(parser:trees()[1]:root(), base_buf, topline - 1, botline) do - local capture_name = query.captures[id] - if capture_name == cursor_word then - local lnum, col, end_lnum, end_col = node:range() - api.nvim_buf_set_extmark(base_buf, edit_ns, lnum, col, { - end_row = end_lnum, - end_col = end_col, - hl_group = 'Visual', - virt_text = { - { capture_name, 'Title' }, - }, - }) + -- Parse buffer including injected languages. + parser:parse(true) + -- Query on the trees of the language requested to highlight captures. + parser:for_each_tree(function(tree, ltree) + if ltree:lang() ~= lang then + return end - end + local root = tree:root() + local topline, botline = vim.fn.line('w0', base_win), vim.fn.line('w$', base_win) + for id, node in query:iter_captures(root, base_buf, topline - 1, botline) do + local capture_name = query.captures[id] + if capture_name == cursor_word then + local lnum, col, end_lnum, end_col = node:range() + api.nvim_buf_set_extmark(base_buf, edit_ns, lnum, col, { + end_row = end_lnum, + end_col = end_col, + hl_group = 'Visual', + virt_text = { + { capture_name, 'Title' }, + }, + }) + end + end + end) end --- @nodoc diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua @@ -186,4 +186,15 @@ function M.inspect(lang) return vim._ts_inspect_language(lang) end +--- Returns available treesitter languages. +function M._complete() + local parsers = vim.api.nvim_get_runtime_file('parser/*', true) + local parser_names_set = {} ---@type table<string, boolean> + for _, parser in ipairs(parsers) do + local parser_name = vim.fn.fnamemodify(parser, ':t:r') + parser_names_set[parser_name] = true + end + return vim.tbl_keys(parser_names_set) +end + return M diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua @@ -1165,6 +1165,8 @@ end --- --- Can also be shown with `:EditQuery`. [:EditQuery]() --- +--- `:EditQuery <tab>` completes the treesitter parser names in the runtime path. +--- --- If you move the cursor to a capture name ("@foo"), text matching the capture is highlighted in --- the source buffer. The query editor is a scratch buffer, use `:write` to save it. You can find --- example queries at `$VIMRUNTIME/queries/`.