fix cursors positions when deleting multiple selections (#1393)

* correctly handle overlapping selections merge cursors in Doc:raw_remove
This commit is contained in:
vqn 2023-04-07 16:54:00 +00:00 committed by George Sokianos
parent acbd8715f4
commit 6de18442be
2 changed files with 42 additions and 10 deletions

View File

@ -323,7 +323,7 @@ local commands = {
end, end,
["doc:delete"] = function(dv) ["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 if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then
dv.doc:remove(line1, col1, line1, math.huge) dv.doc:remove(line1, col1, line1, math.huge)
end end
@ -333,7 +333,7 @@ local commands = {
["doc:backspace"] = function(dv) ["doc:backspace"] = function(dv)
local _, indent_size = dv.doc:get_indent_info() 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 if line1 == line2 and col1 == col2 then
local text = dv.doc:get_text(line1, 1, line1, col1) local text = dv.doc:get_text(line1, 1, line1, col1)
if #text >= indent_size and text:find("^ *$") then if #text >= indent_size and text:find("^ *$") then

View File

@ -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 before = self.lines[line1]:sub(1, col1 - 1)
local after = self.lines[line2]:sub(col2) local after = self.lines[line2]:sub(col2)
-- splice line into line array
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
-- move all cursors back if they share a line with the removed text
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 line_removal = line2 - line1
local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0 local col_removal = col2 - col1
self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal)
-- 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 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 end
-- update highlighter and assure selection is in bounds -- 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() self:sanitize_selection()
end end