diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 9fb4182d1b..a16e291510 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -201,6 +201,8 @@ end ---@returns string local function buf_range_get_text(buf, range) local start_row, start_col, end_row, end_col = M._range.unpack4(range) + local append_newline = end_col == 0 and start_row ~= end_row + if end_col == 0 then if start_row == end_row then start_col = -1 @@ -209,7 +211,12 @@ local function buf_range_get_text(buf, range) end_col = -1 end_row = end_row - 1 end + local lines = api.nvim_buf_get_text(buf, start_row, start_col, end_row, end_col, {}) + if append_newline then + table.insert(lines, '') + end + return table.concat(lines, '\n') end diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 6da92e97d1..77f890ab01 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -1025,7 +1025,9 @@ end) ---@return string? # resolved parser name local function resolve_lang(alias) -- normalize: treesitter language names are always lower case and use underscores - alias = alias and alias:lower():gsub('%-', '_') + -- Language name (from `get_text_node`) may end with "\n". + alias = alias and alias:gsub('%s+', ''):lower():gsub('%-', '_') + -- validate that `alias` is a legal language if not (alias and alias:match('[%w_]+') == alias) then return diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 57748fffb3..08391da9e8 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -706,10 +706,6 @@ local directive_handlers = { local start_row, start_col, end_row, end_col = node:range() local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n') - if end_col == 0 then - -- get_node_text() will ignore the last line if the node ends at column 0 - node_text[#node_text + 1] = '' - end local end_idx = #node_text local start_idx = 1 diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index ac930b434d..6cba8db81c 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -227,6 +227,32 @@ describe('treesitter node API', function() eq({ 0, 0, 2, 3 }, lua_eval('range')) end) + it( + 'get_node_text() includes trailing newline for buffer source when node end_col == 0', + function() + insert('local x = 1') + + exec_lua(function() + _G.node = vim.treesitter.get_parser(0, 'lua'):parse()[1]:root() + end) + + eq('local x = 1\n', lua_eval('vim.treesitter.get_node_text(_G.node, 0)')) + end + ) + + it( + 'get_node_text() includes trailing newline for string source when node end_col == 0', + function() + exec_lua(function() + local source = 'local x = 1\n' + _G.source = source + _G.node = vim.treesitter.get_string_parser(source, 'lua'):parse()[1]:root() + end) + + eq('local x = 1\n', lua_eval('vim.treesitter.get_node_text(_G.node, _G.source)')) + end + ) + it('tree:root() is idempotent', function() insert([[ function x() diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index c26ff56e6b..e8f4fe5e57 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -441,7 +441,7 @@ describe('treesitter parser API', function() local tree = parser:parse()[1] return vim.treesitter.get_node_text(tree:root(), 0) end) - eq(t.dedent(test_text), res) + eq(t.dedent(test_text) .. '\n', res) local res2 = exec_lua(function() local parser = vim.treesitter.get_parser(0, 'c')