diff --git a/src/context.cc b/src/context.cc index fe9bd346..0c1b8aad 100644 --- a/src/context.cc +++ b/src/context.cc @@ -204,9 +204,11 @@ void Context::SelectionHistory::end_edition() if (m_history_id != HistoryId::Invalid and current_history_node().selections == m_staging->selections) { - auto& sels = m_history[(size_t)m_history_id].selections; - sels.force_timestamp(m_staging->selections.timestamp()); - sels.set_main_index(m_staging->selections.main_index()); + // No change, except maybe the index of the main selection. + // Update timestamp to potentially improve interaction with content undo. + auto& node = current_history_node(); + node.selections.force_timestamp(m_staging->selections.timestamp()); + node.selections.set_main_index(m_staging->selections.main_index()); } else { @@ -216,53 +218,36 @@ void Context::SelectionHistory::end_edition() m_staging.reset(); } +template void Context::SelectionHistory::undo() { + static constexpr bool backward = direction == Backward; if (in_edition()) throw runtime_error("selection undo is only supported at top-level"); kak_assert(not empty()); - begin_edition(); - auto end = on_scope_end([&] { - kak_assert(current_history_node().selections == m_staging->selections); - end_edition(); - }); - HistoryId parent = current_history_node().parent; - if (parent == HistoryId::Invalid) - throw runtime_error("no selection change to undo"); - auto select_parent = [&, parent] { - HistoryId before_undo = m_history_id; - m_history_id = parent; - current_history_node().redo_child = before_undo; - m_staging = current_history_node(); - }; - if (&history_node(parent).selections.buffer() == &m_context.buffer()) - select_parent(); - else - m_context.change_buffer(history_node(parent).selections.buffer(), { std::move(select_parent) }); - // }); -} - -void Context::SelectionHistory::redo() -{ - if (in_edition()) - throw runtime_error("selection redo is only supported at top-level"); - kak_assert(not empty()); - begin_edition(); - auto end = on_scope_end([&] { - kak_assert(current_history_node().selections == m_staging->selections); - end_edition(); - }); - HistoryId child = current_history_node().redo_child; - if (child == HistoryId::Invalid) - throw runtime_error("no selection change to redo"); - auto select_child = [&, child] { - m_history_id = child; - m_staging = current_history_node(); - }; - if (&history_node(child).selections.buffer() == &m_context.buffer()) - select_child(); - else - m_context.change_buffer(history_node(child).selections.buffer(), { std::move(select_child) }); + SelectionList old_selections = selections(); + HistoryId next; + do + { + if constexpr (backward) + next = current_history_node().parent; + else + next = current_history_node().redo_child; + if (next == HistoryId::Invalid) + throw runtime_error(backward ? "no selection change to undo" : "no selection change to redo"); + auto select_next = [&, next] { + HistoryId previous_id = m_history_id; + m_history_id = next; + if constexpr (backward) + current_history_node().redo_child = previous_id; + }; + Buffer& destination_buffer = history_node(next).selections.buffer(); + if (&destination_buffer == &m_context.buffer()) + select_next(); + else + m_context.change_buffer(destination_buffer, { std::move(select_next) }); + } + while (selections() == old_selections); } void Context::SelectionHistory::forget_buffer(Buffer& buffer) @@ -376,15 +361,13 @@ SelectionList& Context::selections(bool update) return m_selection_history.selections(update); } +template void Context::undo_selection_change() { - m_selection_history.undo(); -} - -void Context::redo_selection_change() -{ - m_selection_history.redo(); + m_selection_history.undo(); } +template void Context::undo_selection_change(); +template void Context::undo_selection_change(); SelectionList& Context::selections_write_only() { diff --git a/src/context.hh b/src/context.hh index 3fe28dc2..34bb9e12 100644 --- a/src/context.hh +++ b/src/context.hh @@ -19,6 +19,8 @@ class DisplayLine; class KeymapManager; class AliasRegistry; +enum Direction { Backward = -1, Forward = 1 }; + struct JumpList { void push(SelectionList jump, Optional index = {}); @@ -91,8 +93,8 @@ public: SelectionList& selections_write_only(); void end_selection_edition() { m_selection_history.end_edition(); } + template void undo_selection_change(); - void redo_selection_change(); void change_buffer(Buffer& buffer, Optional> set_selection = {}); void forget_buffer(Buffer& buffer); @@ -169,8 +171,8 @@ private: void end_edition(); bool in_edition() const { return m_in_edition; } + template void undo(); - void redo(); void forget_buffer(Buffer& buffer); private: enum class HistoryId : size_t { First = 0, Invalid = (size_t)-1 }; diff --git a/src/normal.cc b/src/normal.cc index 1142d871..7dc14382 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1417,8 +1417,6 @@ void select_object(Context& context, NormalParams params) {{alt(';')}, "run command in object context"}})); } -enum Direction { Backward = -1, Forward = 1 }; - template void scroll(Context& context, NormalParams params) { @@ -2039,18 +2037,12 @@ void move_in_history(Context& context, NormalParams params) history_id, max_history_id)); } +template void undo_selection_change(Context& context, NormalParams params) { int count = std::max(1, params.count); while (count--) - context.undo_selection_change(); -} - -void redo_selection_change(Context& context, NormalParams params) -{ - int count = std::max(1, params.count); - while (count--) - context.redo_selection_change(); + context.undo_selection_change(); } void exec_user_mappings(Context& context, NormalParams params) @@ -2378,8 +2370,8 @@ static constexpr HashMap { {alt('u')}, {"move backward in history", move_in_history} }, { {alt('U')}, {"move forward in history", move_in_history} }, - { {ctrl('h')}, {"undo selection change", undo_selection_change} }, - { {ctrl('k')}, {"redo selection change", redo_selection_change} }, + { {ctrl('h')}, {"undo selection change", undo_selection_change} }, + { {ctrl('k')}, {"redo selection change", undo_selection_change} }, { {alt('i')}, {"select inner object", select_object} }, { {alt('a')}, {"select whole object", select_object} }, diff --git a/test/normal/selection-undo/fold-redundant-entries/cmd b/test/normal/selection-undo/fold-redundant-entries/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/normal/selection-undo/fold-redundant-entries/cmd @@ -0,0 +1 @@ + diff --git a/test/normal/selection-undo/fold-redundant-entries/in b/test/normal/selection-undo/fold-redundant-entries/in new file mode 100644 index 00000000..94ebaf90 --- /dev/null +++ b/test/normal/selection-undo/fold-redundant-entries/in @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/test/normal/selection-undo/fold-redundant-entries/out b/test/normal/selection-undo/fold-redundant-entries/out new file mode 100644 index 00000000..239c8675 --- /dev/null +++ b/test/normal/selection-undo/fold-redundant-entries/out @@ -0,0 +1,3 @@ +2 +3 +here4 diff --git a/test/normal/selection-undo/fold-redundant-entries/script b/test/normal/selection-undo/fold-redundant-entries/script new file mode 100644 index 00000000..456f92f2 --- /dev/null +++ b/test/normal/selection-undo/fold-redundant-entries/script @@ -0,0 +1,2 @@ +ui_out -ignore 4 +ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "gjgkxdihere" ] }' diff --git a/test/normal/selection-undo/redo/cmd b/test/normal/selection-undo/redo/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/normal/selection-undo/redo/cmd @@ -0,0 +1 @@ + diff --git a/test/normal/selection-undo/redo/in b/test/normal/selection-undo/redo/in new file mode 100644 index 00000000..94ebaf90 --- /dev/null +++ b/test/normal/selection-undo/redo/in @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/test/normal/selection-undo/redo/out b/test/normal/selection-undo/redo/out new file mode 100644 index 00000000..76bef247 --- /dev/null +++ b/test/normal/selection-undo/redo/out @@ -0,0 +1,4 @@ +1 +2 +here3 +4 diff --git a/test/normal/selection-undo/redo/script b/test/normal/selection-undo/redo/script new file mode 100644 index 00000000..c92d82d7 --- /dev/null +++ b/test/normal/selection-undo/redo/script @@ -0,0 +1,2 @@ +ui_out -ignore 4 +ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "2jjihere" ] }' diff --git a/test/normal/selection-undo/undo/cmd b/test/normal/selection-undo/undo/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/normal/selection-undo/undo/cmd @@ -0,0 +1 @@ + diff --git a/test/normal/selection-undo/undo/in b/test/normal/selection-undo/undo/in new file mode 100644 index 00000000..94ebaf90 --- /dev/null +++ b/test/normal/selection-undo/undo/in @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/test/normal/selection-undo/undo/out b/test/normal/selection-undo/undo/out new file mode 100644 index 00000000..00a7e5fd --- /dev/null +++ b/test/normal/selection-undo/undo/out @@ -0,0 +1,4 @@ +here1 +2 +3 +4 diff --git a/test/normal/selection-undo/undo/script b/test/normal/selection-undo/undo/script new file mode 100644 index 00000000..2b00d75e --- /dev/null +++ b/test/normal/selection-undo/undo/script @@ -0,0 +1,2 @@ +ui_out -ignore 4 +ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "j2jihere" ] }' diff --git a/test/normal/selection-undo/windisplay-hook/cmd b/test/normal/selection-undo/windisplay-hook/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/normal/selection-undo/windisplay-hook/cmd @@ -0,0 +1 @@ + diff --git a/test/normal/selection-undo/windisplay-hook/in b/test/normal/selection-undo/windisplay-hook/in new file mode 100644 index 00000000..01e79c32 --- /dev/null +++ b/test/normal/selection-undo/windisplay-hook/in @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/test/normal/selection-undo/windisplay-hook/out b/test/normal/selection-undo/windisplay-hook/out new file mode 100644 index 00000000..92243f2b --- /dev/null +++ b/test/normal/selection-undo/windisplay-hook/out @@ -0,0 +1,3 @@ +1 +2 +here3 diff --git a/test/normal/selection-undo/windisplay-hook/rc b/test/normal/selection-undo/windisplay-hook/rc new file mode 100644 index 00000000..b157a2ce --- /dev/null +++ b/test/normal/selection-undo/windisplay-hook/rc @@ -0,0 +1 @@ +hook global WinDisplay .*/out %{exec j} diff --git a/test/normal/selection-undo/windisplay-hook/script b/test/normal/selection-undo/windisplay-hook/script new file mode 100644 index 00000000..d2fd1c40 --- /dev/null +++ b/test/normal/selection-undo/windisplay-hook/script @@ -0,0 +1,2 @@ +ui_out -ignore 4 +ui_in '{ "jsonrpc": "2.0", "method": "keys", "params": [ "j:buffer *debug*ihere" ] }'