From 46ea86e28cecceabe0f3137720bf848c0898f640 Mon Sep 17 00:00:00 2001 From: vqn <85911372+vqns@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:54:00 +0000 Subject: [PATCH] fix cursors positions when deleting multiple selections (#1393) * correctly handle overlapping selections merge cursors in Doc:raw_remove --- data/core/commands/doc.lua | 4 ++-- data/core/doc/init.lua | 48 +++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index ebaf698c..eca876ea 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -323,7 +323,7 @@ local commands = { end, ["doc:delete"] = function(dv) - for idx, line1, col1, line2, col2 in dv.doc:get_selections() do + for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then dv.doc:remove(line1, col1, line1, math.huge) end @@ -333,7 +333,7 @@ local commands = { ["doc:backspace"] = function(dv) local _, indent_size = dv.doc:get_indent_info() - for idx, line1, col1, line2, col2 in dv.doc:get_selections() do + for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do if line1 == line2 and col1 == col2 then local text = dv.doc:get_text(line1, 1, line1, col1) if #text >= indent_size and text:find("^ *$") then diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 26fc2a52..7fdd896c 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -427,19 +427,51 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) local before = self.lines[line1]:sub(1, col1 - 1) local after = self.lines[line2]:sub(col2) - -- splice line into line array - common.splice(self.lines, line1, line2 - line1 + 1, { before .. after }) + local line_removal = line2 - line1 + local col_removal = col2 - col1 - -- move all cursors back if they share a line with the removed text + -- splice line into line array + common.splice(self.lines, line1, line_removal + 1, { before .. after }) + + local merge = false + + -- keep selections in correct positions: each pair (line, col) + -- * remains unchanged if before the deleted text + -- * is set to (line1, col1) if in the deleted text + -- * is set to (line1, col - col_removal) if on line2 but out of the deleted text + -- * is set to (line - line_removal, col) if after line2 for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do - if cline1 < line2 then break end - local line_removal = line2 - line1 - local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0 - self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal) + if cline2 < line1 then break end + local l1, c1, l2, c2 = cline1, ccol1, cline2, ccol2 + + if cline1 > line1 or (cline1 == line1 and ccol1 > col1) then + if cline1 > line2 then + l1 = l1 - line_removal + else + l1 = line1 + c1 = (cline1 == line2 and ccol1 > col2) and c1 - col_removal or col1 + end + end + + if cline2 > line1 or (cline2 == line2 and ccol2 > col1) then + if cline2 > line2 then + l2 = l2 - line_removal + else + l2 = line1 + c2 = (cline2 == line2 and ccol2 > col2) and c2 - col_removal or col1 + end + end + + if l1 == line1 and c1 == col1 then merge = true end + self:set_selections(idx, l1, c1, l2, c2) + end + + if merge then + self:merge_cursors() end -- update highlighter and assure selection is in bounds - self.highlighter:remove_notify(line1, line2 - line1) + self.highlighter:remove_notify(line1, line_removal) self:sanitize_selection() end