From 0922634d371d45c344a559119ed398f91e399085 Mon Sep 17 00:00:00 2001 From: Michael Lan <44309097+mizlan@users.noreply.github.com> Date: Fri, 6 Jan 2023 17:53:12 -0800 Subject: [PATCH] fix(utils): swap_nodes calculates correct char_delta (#4110) * fix(utils): swap_nodes calculates correct char_delta The char_delta is not calculated correctly right now when there are two treesitter nodes being swapped, one directly following the other. This is rare but can happen for example when attempting to swap "print" and "(1)" in "print(1)". In this case an incorrect char_delta is calculated because of a bug in range comparison. * test(swap_nodes): check cursor * add a regression test (for multiline node swap) * add a test with adjacent swaps that fails when char_delta is not calculated correctly * test(swap_nodes): check text content after swap * test: note language for parser * fix tests * use same not equal for table comparison --- lua/nvim-treesitter/ts_utils.lua | 4 ++-- tests/unit/ts_utils_spec.lua | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lua/nvim-treesitter/ts_utils.lua b/lua/nvim-treesitter/ts_utils.lua index 3d804ccba..bfe6170b1 100644 --- a/lua/nvim-treesitter/ts_utils.lua +++ b/lua/nvim-treesitter/ts_utils.lua @@ -400,12 +400,12 @@ function M.swap_nodes(node_or_range1, node_or_range2, bufnr, cursor_to_second) local line_delta = 0 if range1["end"].line < range2.start.line - or (range1["end"].line == range2.start.line and range1["end"].character < range2.start.character) + or (range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character) then line_delta = #text2 - #text1 end - if range1["end"].line == range2.start.line and range1["end"].character < range2.start.character then + if range1["end"].line == range2.start.line and range1["end"].character <= range2.start.character then if line_delta ~= 0 then --- why? --correction_after_line_change = -range2.start.character diff --git a/tests/unit/ts_utils_spec.lua b/tests/unit/ts_utils_spec.lua index a3b334413..3cc7d4dc8 100644 --- a/tests/unit/ts_utils_spec.lua +++ b/tests/unit/ts_utils_spec.lua @@ -106,3 +106,43 @@ describe("update_selection", function() ) end) end) + +describe("swap_nodes", function() + local function swap(case) + vim.api.nvim_buf_set_lines(0, 0, -1, false, case.lines) + vim.opt.filetype = case.filetype + local a = vim.treesitter.get_node_at_pos(0, case.a[1], case.a[2], {}) + local b = vim.treesitter.get_node_at_pos(0, case.b[1], case.b[2], {}) + tsutils.swap_nodes(a, b, 0, true) + end + + it("works on adjacent nodes", function() + swap { + filetype = "python", + lines = { "print(1)" }, + a = { 0, 0 }, + b = { 0, 5 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "(1)print" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 1, 3 }) + end) + + it("works with multiline nodes", function() + swap { + filetype = "lua", + lines = { "x = { [[", "]], [[", ".....]]}" }, + a = { 0, 6 }, + b = { 1, 4 }, + } + + it("swaps text", function() end) + assert.same(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "x = { [[", ".....]], [[", "]]}" }) + + it("moves the cursor", function() end) + assert.same(vim.api.nvim_win_get_cursor(0), { 2, 9 }) + end) +end)