From fb07b09ff0a9d21868e34035902e883e25105e84 Mon Sep 17 00:00:00 2001 From: TheLeoP Date: Tue, 16 Sep 2025 16:41:59 -0500 Subject: [PATCH 1/2] fix(ai): fix ts_range_to_region for nodes that end with newlines --- lua/mini/ai.lua | 15 +++++++++++++-- tests/mock-treesitter/queries/lua/textobjects.scm | 2 ++ tests/test_ai.lua | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lua/mini/ai.lua b/lua/mini/ai.lua index 99b1feff..374e7c30 100644 --- a/lua/mini/ai.lua +++ b/lua/mini/ai.lua @@ -990,11 +990,22 @@ MiniAi.gen_spec.treesitter = function(ai_captures, opts) -- `row1-col1-byte1-row2-col2-byte2` (i.e. "range six") format. local ts_range_to_region = function(r) -- The `master` branch of 'nvim-treesitter' can return "range four" format - -- if it uses custom directives, like `#make-range!`. Due ot the fact that + -- if it uses custom directives, like `#make-range!`. Due to the fact that -- it doesn't fully mock the `TSNode:range()` method to return "range six". -- TODO: Remove after 'nvim-treesitter' `master` branch support is dropped. local offset = #r == 4 and -1 or 0 - return { from = { line = r[1] + 1, col = r[2] + 1 }, to = { line = r[4 + offset] + 1, col = r[5 + offset] } } + local reg = { from = { line = r[1] + 1, col = r[2] + 1 }, to = { line = r[4 + offset] + 1, col = r[5 + offset] } } + + -- When a node ends with a newline, its end point changes from + -- row-inclusive, col-exclusive to row-exclusive, col-0 + if reg.to.col == 0 then + local buf_id = vim.api.nvim_get_current_buf() + local line = vim.api.nvim_buf_get_lines(buf_id, reg.to.line - 2, reg.to.line - 1, true)[1] + + reg.to.line = reg.to.line - 1 + reg.to.col = #line + end + return reg end return function(ai_type, _, _) diff --git a/tests/mock-treesitter/queries/lua/textobjects.scm b/tests/mock-treesitter/queries/lua/textobjects.scm index 49a394ac..d764fc35 100644 --- a/tests/mock-treesitter/queries/lua/textobjects.scm +++ b/tests/mock-treesitter/queries/lua/textobjects.scm @@ -25,3 +25,5 @@ (string) @string ((string) @string_offset (#offset! @string_offset 0 1 0 -2)) + +(chunk) @chunk.outer diff --git a/tests/test_ai.lua b/tests/test_ai.lua index f97edbbf..97bf3946 100644 --- a/tests/test_ai.lua +++ b/tests/test_ai.lua @@ -844,6 +844,15 @@ T['gen_spec']['treesitter()']['works with quantified captures'] = function() validate_find(lines, { 3, 0 }, { 'a', 'P', { n_times = 3 } }, { { 3, 19 }, { 3, 23 } }) end +T['gen_spec']['treesitter()']['works with row-exclusive, col-0 end range'] = function() + child.lua([[MiniAi.config.custom_textobjects = { + c = MiniAi.gen_spec.treesitter({ a = '@chunk.outer', i = '@chunk.outer' }), + }]]) + + local lines = get_lines() + validate_find(lines, { 1, 0 }, { 'a', 'c' }, { { 1, 1 }, { 13, 8 } }) +end + T['gen_spec']['treesitter()']['respects plugin options'] = function() local lines = get_lines() From 70bfee032a502718972846a635f6195f72ebeb2e Mon Sep 17 00:00:00 2001 From: TheLeoP Date: Tue, 16 Sep 2025 16:43:04 -0500 Subject: [PATCH 2/2] fix(surround): fix ts_range_to_region for nodes that end with newlines --- lua/mini/surround.lua | 15 +++++++++++++-- tests/mock-treesitter/queries/lua/textobjects.scm | 2 +- tests/test_surround.lua | 13 +++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lua/mini/surround.lua b/lua/mini/surround.lua index 4a00c2b9..85264a08 100644 --- a/lua/mini/surround.lua +++ b/lua/mini/surround.lua @@ -1039,11 +1039,22 @@ MiniSurround.gen_spec.input.treesitter = function(captures, opts) -- `row1-col1-byte1-row2-col2-byte2` (i.e. "range six") format. local ts_range_to_region = function(r) -- The `master` branch of 'nvim-treesitter' can return "range four" format - -- if it uses custom directives, like `#make-range!`. Due ot the fact that + -- if it uses custom directives, like `#make-range!`. Due to the fact that -- it doesn't fully mock the `TSNode:range()` method to return "range six". -- TODO: Remove after 'nvim-treesitter' `master` branch support is dropped. local offset = #r == 4 and -1 or 0 - return { from = { line = r[1] + 1, col = r[2] + 1 }, to = { line = r[4 + offset] + 1, col = r[5 + offset] } } + local reg = { from = { line = r[1] + 1, col = r[2] + 1 }, to = { line = r[4 + offset] + 1, col = r[5 + offset] } } + + -- When a node ends with a newline, its end point changes from + -- row-inclusive, col-exclusive to row-exclusive, col-0 + if reg.to.col == 0 then + local buf_id = vim.api.nvim_get_current_buf() + local line = vim.api.nvim_buf_get_lines(buf_id, reg.to.line - 2, reg.to.line - 1, true)[1] + + reg.to.line = reg.to.line - 1 + reg.to.col = #line + end + return reg end return function() diff --git a/tests/mock-treesitter/queries/lua/textobjects.scm b/tests/mock-treesitter/queries/lua/textobjects.scm index d764fc35..1663d92f 100644 --- a/tests/mock-treesitter/queries/lua/textobjects.scm +++ b/tests/mock-treesitter/queries/lua/textobjects.scm @@ -26,4 +26,4 @@ ((string) @string_offset (#offset! @string_offset 0 1 0 -2)) -(chunk) @chunk.outer +(chunk [(function_declaration) (assignment_statement)]* @chunk.inner) @chunk.outer diff --git a/tests/test_surround.lua b/tests/test_surround.lua index e80cbf74..e01693d5 100644 --- a/tests/test_surround.lua +++ b/tests/test_surround.lua @@ -382,6 +382,19 @@ T['gen_spec']['input']['treesitter()']['works with quantified captures'] = funct validate(21, 'function M.a(u, vv)') end +T['gen_spec']['input']['treesitter()']['works with row-exclusive, col-0 end range'] = function() + if child.fn.has('nvim-0.10') == 0 then + MiniTest.skip('`Query:iter_matches()` returning several nodes requires Neovim>=0.10') + end + + child.lua([[MiniSurround.config.custom_surroundings = { + c = { input = MiniSurround.gen_spec.input.treesitter({ outer = '@chunk.outer', inner = '@chunk.inner' }) } + }]]) + + local lines = get_lines() + validate_find(lines, { 4, 0 }, { { 11, 2 }, { 13, 7 }, { 1, 0 }, { 2, 0 } }, type_keys, 'sf', 'c') +end + T['gen_spec']['input']['treesitter()']['respects plugin options'] = function() local lines = get_lines()