nvim-treesitter / nvim-treesitter-textobjects

Apache License 2.0
2.15k stars 192 forks source link

[main]: `parameter.inner`: selected smaller range than expected #700

Open przepompownia opened 5 days ago

przepompownia commented 5 days ago

Sometimes inner parameter selection ignores the current scope and selects the first parameter in the nested scope instead. I'm not sure if it's a bug of this plugin.

To Reproduce Steps to reproduce the behavior: init.lua:

local thisInitFile = debug.getinfo(1).source:match('@?(.*)')
local cwd = vim.fs.dirname(thisInitFile)
local appname = vim.env.NVIM_APPNAME or 'nvim'

vim.env.XDG_CONFIG_HOME = cwd
vim.env.XDG_DATA_HOME = vim.fs.joinpath(cwd, '.xdg', 'data')
vim.env.XDG_STATE_HOME = vim.fs.joinpath(cwd, '.xdg', 'state')
vim.env.XDG_CACHE_HOME = vim.fs.joinpath(cwd, '.xdg', 'cache')
vim.fn.mkdir(vim.fs.joinpath(vim.env.XDG_CACHE_HOME, appname), 'p')
local stdPathConfig = vim.fn.stdpath('config')

vim.opt.runtimepath:prepend(stdPathConfig)
vim.opt.packpath:prepend(stdPathConfig)

local function gitClone(url, installPath, branch)
  if vim.fn.isdirectory(installPath) ~= 0 then
    return
  end

  local command = {'git', 'clone', '--', url, installPath}
  if branch then
    table.insert(command, 3, '--branch')
    table.insert(command, 4, branch)
  end

  vim.notify(('Cloning %s dependency into %s...'):format(url, installPath), vim.log.levels.INFO, {})
  local sysObj = vim.system(command, {}):wait()
  if sysObj.code ~= 0 then
    error(sysObj.stderr)
  end
  vim.notify(sysObj.stdout)
  vim.notify(sysObj.stderr, vim.log.levels.WARN)
end

local pluginsPath = vim.fs.joinpath(cwd, 'nvim/pack/plugins/opt')
vim.fn.mkdir(pluginsPath, 'p')
pluginsPath = vim.uv.fs_realpath(pluginsPath)

--- @type table<string, {url:string, branch: string?}>
local plugins = {
  ['nvim-treesitter-textobjects'] = {url = 'https://github.com/nvim-treesitter/nvim-treesitter-textobjects', branch = 'main'},
}

for name, repo in pairs(plugins) do
  local installPath = vim.fs.joinpath(pluginsPath, name)
  gitClone(repo.url, installPath, repo.branch)
  vim.cmd.packadd({args = {name}, bang = true})
end

vim.keymap.set({'x', 'o'}, 'ip', function ()
  require 'nvim-treesitter-textobjects.select'.select_textobject('@parameter.inner', 'textobjects')
end)
vim.go.hlsearch = false

local function foo()
  vim.print(1, tonumber('1'), {'x'})
end

vim.api.nvim_feedkeys(vim.keycode('/tonumber<CR>'), 'n', false)
  1. nvim --clean -u init.lua init.lua
  2. type vip (for me '1' is selected)
  3. escape visual mode, go to space before {x} and type vip again (for me 'x' is selected).

Expected behavior Selected:

Output of :checkhealth vim.treesitter

vim.treesitter: require("vim.treesitter.health").check() - Nvim runtime ABI version: 14 - OK Parser: c ABI: 14, path: .../nvim-from-src/lib/nvim/parser/c.so - OK Parser: lua ABI: 14, path: .../nvim-from-src/lib/nvim/parser/lua.so - OK Parser: markdown ABI: 14, path: .../nvim-from-src/lib/nvim/parser/markdown.so - OK Parser: markdown_inline ABI: 14, path: .../nvim-from-src/lib/nvim/parser/markdown_inline.so - OK Parser: query ABI: 14, path: .../nvim-from-src/lib/nvim/parser/query.so - OK Parser: vim ABI: 14, path: .../nvim-from-src/lib/nvim/parser/vim.so - OK Parser: vimdoc ABI: 14, path: .../nvim-from-src/lib/nvim/parser/vimdoc.so - Can load WASM parsers: false

Output of nvim --version

v0.11.0-dev-941+gcd8e15e33
Juhan280 commented 21 hours ago

The master branch works as expected.

But when I try your script to reproduce, the command vip gives the following error:

E5108: Error executing lua: ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:91: attempt t
o index a nil value
stack traceback:
        ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:91: in function 'fn'
        ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:39: in function 'get_query_matche
s'
        ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:167: in function 'fn'
        ...r/share/nvim/runtime/lua/vim/treesitter/languagetree.lua:491: in function 'for_each_tree'
        ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:164: in function 'get_capture_ran
ges_recursively'
        ...r-textobjects/lua/nvim-treesitter-textobjects/shared.lua:359: in function 'textobject_at_p
oint'
        ...r-textobjects/lua/nvim-treesitter-textobjects/select.lua:155: in function 'select_textobje
ct'
        /data/data/com.termux/files/usr/tmp/init.lua:51: in function 
Press ENTER or type command to continue

nvim --version

NVIM v0.10.2
Build type: Release
LuaJIT 2.1.1727870382
przepompownia commented 12 hours ago

@Juhan280 I doubt that the main branch is intended to be compatible with Nvim 0.10.*, although calling iter_matches with {all = true} (default in master on Nvim) allows at least to avoid the error (not checked if it works in general and whether it makes any sense).

przepompownia commented 12 hours ago

From the :InspectTree perspective the query https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/aad2c8e6b029ada30621e67780801420ee9debe9/queries/lua/textobjects.scm#L84-L87 looks good:

Image

przepompownia commented 9 hours ago

https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/aad2c8e6b029ada30621e67780801420ee9debe9/lua/nvim-treesitter-textobjects/shared.lua#L228-L242

Image

Having vim.print(1, tonumber('1')) as the only line we get false from is_in_range({0, 11, 0, 26}, 0, 26).

I tried

diff --git a/lua/nvim-treesitter-textobjects/shared.lua b/lua/nvim-treesitter-textobjects/shared.lua
index 69a804c..69bd750 100644
--- a/lua/nvim-treesitter-textobjects/shared.lua
+++ b/lua/nvim-treesitter-textobjects/shared.lua
@@ -236,7 +236,7 @@ local function is_in_range(range, row, col)
   end
   local is_before_end_col_if_needed = true
   if end_row == row then
-    is_before_end_col_if_needed = col <= end_col
+    is_before_end_col_if_needed = col - 1 <= end_col
   end
   return is_in_rows and is_after_start_col_if_needed and is_before_end_col_if_needed
 end

but it breaks selection when the cursor is on the closing brace in vim.keymap.set({'x', 'o'}, 'ip', function () end) (I haven't check yet how matched ranges look in that case).