From 1e693f06a6556b87ed4f56a3635a0c10640b1f92 Mon Sep 17 00:00:00 2001 From: mitchell <70453897+orbitalquark@users.noreply.github.com> Date: Sun, 11 Apr 2021 22:51:59 -0400 Subject: Save/restore view state when undoing/redoing full-buffer changes. For example external code formatting commands that replace buffer contents. --- modules/textadept/history.lua | 21 ++++++++++++++++++--- test/test.lua | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/modules/textadept/history.lua b/modules/textadept/history.lua index da9f5885..05081ddd 100644 --- a/modules/textadept/history.lua +++ b/modules/textadept/history.lua @@ -33,20 +33,35 @@ local view_history = setmetatable({}, { end }) +local restore_position, first_visible_line = false, nil +-- Restore position after a full-buffer undo/redo operation, e.g. after replacing buffer contents +-- with a formatting command and then performing an undo. +events.connect(events.UPDATE_UI, function(updated) + if not restore_position or updated & buffer.UPDATE_SELECTION == 0 then return end + restore_position = false + M.back() + view.first_visible_line, first_visible_line = first_visible_line, nil +end) + -- Listens for text insertion and deletion events and records their locations. events.connect(events.MODIFIED, function(position, mod, text, length) local buffer = buffer -- Only interested in text insertion or deletion. if mod & buffer.MOD_INSERTTEXT > 0 then - if length == buffer.length then return end -- ignore file loading + if length == buffer.length then + if mod & buffer.MULTILINEUNDOREDO > 0 then restore_position = true end + return -- ignore file loading or replacing buffer contents + end position = position + length elseif mod & buffer.MOD_DELETETEXT > 0 then if buffer.length == 0 then return end -- ignore replacing buffer contents + elseif mod & (buffer.PERFORMED_UNDO | buffer.PERFORMED_REDO) > 0 and + (mod & buffer.MOD_BEFOREDELETE > 0) and length == buffer.length then + first_visible_line = view.first_visible_line -- save for potential undo before it's lost else return end - -- Ignore undo/redo. - if mod & (buffer.PERFORMED_UNDO | buffer.PERFORMED_REDO) > 0 then return end + if mod & (buffer.PERFORMED_UNDO | buffer.PERFORMED_REDO) > 0 then return end -- ignore undo/redo M.record(nil, buffer:line_from_position(position), buffer.column[position]) end) diff --git a/test/test.lua b/test/test.lua index e9c6bf75..4e6b5441 100644 --- a/test/test.lua +++ b/test/test.lua @@ -2932,6 +2932,33 @@ function test_history_print_buffer() ui.tabs = tabs -- restore end +function test_history_undo_full_buffer_change() + buffer.new() + local lines = {} + for i = 99, 1, -1 do lines[#lines + 1] = tostring(i) end + buffer:add_text(table.concat(lines, '\n')) + buffer:goto_line(50) + buffer:add_text('1') + textadept.editing.filter_through('sort -n') + ui.update() + assert(buffer:get_line(buffer:line_from_position(buffer.current_pos)) ~= '150\n', 'not sorted') + local first_visible_line = view.first_visible_line + buffer:undo() + -- Verify the view state was restored. + ui.update() + if CURSES then events.emit(events.UPDATE_UI, buffer.UPDATE_SELECTION) end + assert_equal(buffer:line_from_position(buffer.current_pos), 50) + assert_equal(buffer:get_line(buffer:line_from_position(buffer.current_pos)), '150\n') + assert_equal(view.first_visible_line, first_visible_line) + buffer:redo() + -- Verify the previous view state was kept. + ui.update() + if CURSES then events.emit(events.UPDATE_UI, buffer.UPDATE_SELECTION) end + assert_equal(buffer:line_from_position(buffer.current_pos), 50) + assert_equal(view.first_visible_line, first_visible_line) + buffer:close(true) +end + function test_macro_record_play_save_load() textadept.macros.save() -- should not do anything textadept.macros.play() -- should not do anything -- cgit v1.2.3