From 885657d48bd5d0c581a331939ad72e1de88227cf Mon Sep 17 00:00:00 2001 From: Julien Vincent Date: Fri, 18 Oct 2024 10:33:31 +0100 Subject: [PATCH] Swap both elements of a pair together Currently when performing a swap, each element in a pair is swapped with it's corresponding paired element. This works for simple cases but breaks when the number of lines are different between the two pairs as the ranges the nodes represent have now changed from previous edits. This fixes the issue by finding the full range represented by both elements in a pair and swapping this entire range in one go. --- lua/nvim-paredit/api/dragging.lua | 44 ++++++++++++++++++++++++--- lua/nvim-paredit/api/text.lua | 40 ++++++++++++++++++++++++ lua/nvim-paredit/treesitter/pairs.lua | 2 +- tests/nvim-paredit/motion_spec.lua | 25 +++++---------- tests/nvim-paredit/pair_drag_spec.lua | 38 +++++++++++++++++++++++ 5 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 lua/nvim-paredit/api/text.lua diff --git a/lua/nvim-paredit/api/dragging.lua b/lua/nvim-paredit/api/dragging.lua index ed0dac2..789480d 100644 --- a/lua/nvim-paredit/api/dragging.lua +++ b/lua/nvim-paredit/api/dragging.lua @@ -3,6 +3,7 @@ local ts_forms = require("nvim-paredit.treesitter.forms") local ts_pairs = require("nvim-paredit.treesitter.pairs") local traversal = require("nvim-paredit.utils.traversal") local common = require("nvim-paredit.utils.common") +local text_api = require("nvim-paredit.api.text") local ts = require("nvim-treesitter.ts_utils") local config = require("nvim-paredit.config") @@ -76,13 +77,46 @@ local function drag_node_in_pair(current_node, nodes, opts) return end - local buf = vim.api.nvim_get_current_buf() - if pair[2] and corresponding_pair[2] then - ts.swap_nodes(pair[2], corresponding_pair[2], buf, true) + local left_pair, right_pair + if direction == 1 then + left_pair = pair + right_pair = corresponding_pair + else + left_pair = corresponding_pair + right_pair = pair + end + + if not left_pair[1] or not left_pair[2] then + return + end + + if not right_pair[1] or not right_pair[2] then + return end - if pair[1] and corresponding_pair[1] then - ts.swap_nodes(pair[1], corresponding_pair[1], buf, true) + + local left_range_1 = { left_pair[1]:range() } + local left_range_2 = { left_pair[2]:range() } + -- stylua: ignore + local left_range = { + left_range_1[1], left_range_1[2], + left_range_2[3], left_range_2[4] + } + + local right_range_1 = { right_pair[1]:range() } + local right_range_2 = { right_pair[2]:range() } + -- stylua: ignore + local right_range = { + right_range_1[1], right_range_1[2], + right_range_2[3], right_range_2[4] + } + + local buf = vim.api.nvim_get_current_buf() + local cursor_pos = 1 + if opts.reversed then + cursor_pos = 0 end + + text_api.swap_ranges(buf, left_range, right_range, cursor_pos) end local function drag_pair(opts) diff --git a/lua/nvim-paredit/api/text.lua b/lua/nvim-paredit/api/text.lua new file mode 100644 index 0000000..4f1c0ea --- /dev/null +++ b/lua/nvim-paredit/api/text.lua @@ -0,0 +1,40 @@ +local M = {} + +-- Swap the text in the given buffer represented by left_range and right_range +function M.swap_ranges(buf, left_range, right_range, cursor_pos) + -- stylua: ignore + local left_text = vim.api.nvim_buf_get_text( + buf, + left_range[1], left_range[2], + left_range[3], left_range[4], + {} + ) + -- stylua: ignore + local right_text = vim.api.nvim_buf_get_text(buf, + right_range[1], right_range[2], + right_range[3], right_range[4], + {} + ) + + -- stylua: ignore + vim.api.nvim_buf_set_text(buf, + right_range[1], right_range[2], + right_range[3], right_range[4], + left_text + ) + if cursor_pos == 1 then + vim.api.nvim_win_set_cursor(0, { right_range[1] + 1, right_range[2] }) + end + + -- stylua: ignore + vim.api.nvim_buf_set_text(buf, + left_range[1], left_range[2], + left_range[3], left_range[4], + right_text + ) + if cursor_pos == 0 then + vim.api.nvim_win_set_cursor(0, { left_range[1] + 1, left_range[2] }) + end +end + +return M diff --git a/lua/nvim-paredit/treesitter/pairs.lua b/lua/nvim-paredit/treesitter/pairs.lua index 443d89f..a01e255 100644 --- a/lua/nvim-paredit/treesitter/pairs.lua +++ b/lua/nvim-paredit/treesitter/pairs.lua @@ -10,7 +10,7 @@ local M = {} -- matched nodes. function M.find_pairwise_nodes(target_node, opts) local root_node = ts_utils.find_local_root(target_node) - local enclosing_form = ts_forms.find_nearest_form(target_node, opts) + local enclosing_form = ts_forms.get_node_root(target_node, opts):parent() if not enclosing_form then return end diff --git a/tests/nvim-paredit/motion_spec.lua b/tests/nvim-paredit/motion_spec.lua index 663efaa..c2a2760 100644 --- a/tests/nvim-paredit/motion_spec.lua +++ b/tests/nvim-paredit/motion_spec.lua @@ -268,52 +268,43 @@ describe("motions :: ", function() it("should move to parent form start", function() -- (aa (bb) @(|cc) #{1}) prepare_buffer({ - content = "(aa (bb) @(cc) #{1})", - cursor = { 1, 11 }, + "(aa (bb) @(|cc) #{1})" }) - -- (aa (bb) @|(cc) #{1}) internal_api.move_to_parent_form_start() expect({ - cursor = { 1, 10 }, + "(aa (bb) @|(cc) #{1})" }) - -- |(aa (bb) @(cc) #{1}) internal_api.move_to_parent_form_start() expect({ - cursor = { 1, 0 }, + "|(aa (bb) @(cc) #{1})" }) - -- |(aa (bb) @(cc) #{1}) internal_api.move_to_parent_form_start() expect({ - cursor = { 1, 0 }, + "|(aa (bb) @(cc) #{1})" }) end) it("should move to parent form end", function() - -- (aa (bb) |@(cc) #{1}) prepare_buffer({ - content = "(aa (bb) @(cc) #{1})", - cursor = { 1, 9 }, + "(aa (bb) |@(cc) #{1})", }) - -- (aa (bb) @(cc|) #{1}) internal_api.move_to_parent_form_end() expect({ - cursor = { 1, 13 }, + "(aa (bb) @(cc|) #{1})", }) - -- (aa (bb) @(cc) #{1}|) internal_api.move_to_parent_form_end() expect({ - cursor = { 1, 19 }, + "(aa (bb) @(cc) #{1}|)", }) - -- (aa (bb) @(cc) #{1}|) internal_api.move_to_parent_form_end() expect({ - cursor = { 1, 19 }, + "(aa (bb) @(cc) #{1}|)", }) end) end) diff --git a/tests/nvim-paredit/pair_drag_spec.lua b/tests/nvim-paredit/pair_drag_spec.lua index 7a904a1..aea161b 100644 --- a/tests/nvim-paredit/pair_drag_spec.lua +++ b/tests/nvim-paredit/pair_drag_spec.lua @@ -30,6 +30,44 @@ describe("pair dragging ::", function() }) end) + it("should drag multiline pairs", function() + prepare_buffer({ + "{:a |{:a1 1", + " :a2 2}", + " :b {:b1 1", + " :b2 2}}", + }) + paredit.drag_element_forwards({ + dragging = { + auto_drag_pairs = true, + }, + }) + expect({ + "{:b {:b1 1", + " :b2 2}", + " |:a {:a1 1", + " :a2 2}}", + }) + end) + + it("should drag pairs with differing line counts", function() + prepare_buffer({ + "{:a |{:a1 1", + " :a2 2}", + " :b {:b1 1 :b2 2}}", + }) + paredit.drag_element_forwards({ + dragging = { + auto_drag_pairs = true, + }, + }) + expect({ + "{:b {:b1 1 :b2 2}", + " |:a {:a1 1", + " :a2 2}}", + }) + end) + it("should drag map pairs backwards", function() prepare_buffer({ content = "{:a 1 :b 2}",