diff options
| author | Maxime Coste <mawww@kakoune.org> | 2022-11-10 08:56:42 +1100 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2022-11-10 08:56:42 +1100 |
| commit | 91d45a100a39345f06d9789ded9172fe60887c27 (patch) | |
| tree | 4a421164f27f50c6f7707959c71555c19eb00a10 /src/normal.cc | |
| parent | bfa3c568ccdc39910e5f9a0cbcc73ed05275087a (diff) | |
| parent | c2ab5d46940bb01ab4ab2b4ef82b6829ddbadb5c (diff) | |
Merge remote-tracking branch 'krobelus/undo-selection-change'
Diffstat (limited to 'src/normal.cc')
| -rw-r--r-- | src/normal.cc | 98 |
1 files changed, 83 insertions, 15 deletions
diff --git a/src/normal.cc b/src/normal.cc index 5f0512d7..a6cabec1 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -74,6 +74,7 @@ UnitTest test_merge_selection{[] { template<SelectMode mode, typename T> void select(Context& context, T func) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); if (mode == SelectMode::Append) { @@ -140,8 +141,11 @@ void select_and_set_last(Context& context, Func&& func) } template<SelectMode mode = SelectMode::Replace> -void select_coord(Buffer& buffer, BufferCoord coord, SelectionList& selections) +void select_coord(Context& context, BufferCoord coord) { + Buffer& buffer = context.buffer(); + ScopedSelectionEdition selection_edition{context}; + SelectionList& selections = context.selections(); coord = buffer.clamp(coord); if (mode == SelectMode::Replace) selections = SelectionList{ buffer, coord }; @@ -212,7 +216,7 @@ void goto_commands(Context& context, NormalParams params) if (params.count != 0) { context.push_jump(); - select_coord<mode>(context.buffer(), LineCount{params.count - 1}, context.selections()); + select_coord<mode>(context, LineCount{params.count - 1}); if (context.has_window()) context.window().center_line(LineCount{params.count-1}); } @@ -229,7 +233,7 @@ void goto_commands(Context& context, NormalParams params) case 'g': case 'k': context.push_jump(); - select_coord<mode>(buffer, BufferCoord{0,0}, context.selections()); + select_coord<mode>(context, BufferCoord{0,0}); break; case 'l': select<mode, select_to_line_end<true>>(context, {}); @@ -242,17 +246,17 @@ void goto_commands(Context& context, NormalParams params) break; case 'j': context.push_jump(); - select_coord<mode>(buffer, buffer.line_count() - 1, context.selections()); + select_coord<mode>(context, buffer.line_count() - 1); break; case 'e': context.push_jump(); - select_coord<mode>(buffer, buffer.back_coord(), context.selections()); + select_coord<mode>(context, buffer.back_coord()); break; case 't': if (context.has_window()) { auto line = context.window().position().line; - select_coord<mode>(buffer, line, context.selections()); + select_coord<mode>(context, line); } break; case 'b': @@ -260,7 +264,7 @@ void goto_commands(Context& context, NormalParams params) { auto& window = context.window(); auto line = window.position().line + window.dimensions().line - 1; - select_coord<mode>(buffer, line, context.selections()); + select_coord<mode>(context, line); } break; case 'c': @@ -268,7 +272,7 @@ void goto_commands(Context& context, NormalParams params) { auto& window = context.window(); auto line = window.position().line + window.dimensions().line / 2; - select_coord<mode>(buffer, line, context.selections()); + select_coord<mode>(context, line); } break; case 'a': @@ -328,7 +332,7 @@ void goto_commands(Context& context, NormalParams params) throw runtime_error("no last modification position"); if (*pos >= buffer.back_coord()) pos = buffer.back_coord(); - select_coord<mode>(buffer, *pos, context.selections()); + select_coord<mode>(context, *pos); break; } default: @@ -420,6 +424,7 @@ void replace_with_char(Context& context, NormalParams) if (not cp or key == Key::Escape) return; ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; Buffer& buffer = context.buffer(); context.selections().for_each([&](size_t index, Selection& sel) { CharCount count = char_length(buffer, sel); @@ -440,6 +445,7 @@ void for_each_codepoint(Context& context, NormalParams) using Utf8It = utf8::iterator<BufferIterator>; ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; Buffer& buffer = context.buffer(); context.selections().for_each([&](size_t index, Selection& sel) { @@ -553,7 +559,8 @@ void pipe(Context& context, NormalParams params) prompt, {}, default_command, context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete, - [default_command](StringView cmdline, PromptEvent event, Context& context) + [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)] + (StringView cmdline, PromptEvent event, Context& context) { if (event != PromptEvent::Validate) return; @@ -635,6 +642,7 @@ void erase_selections(Context& context, NormalParams params) RegisterManager::instance()[reg].set(context, context.selections_content()); } ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; context.selections().erase(); } @@ -681,6 +689,7 @@ void paste(Context& context, NormalParams params) auto& buffer = context.buffer(); ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; context.selections().for_each([&](size_t index, Selection& sel) { auto& str = strings[std::min(strings.size()-1, index)]; auto& min = sel.min(); @@ -717,6 +726,7 @@ void paste_all(Context& context, NormalParams params) Buffer& buffer = context.buffer(); Vector<Selection> result; + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); { ScopedEdition edition(context); @@ -749,7 +759,8 @@ void insert_output(Context& context, NormalParams params) prompt, {}, default_command, context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete, - [default_command](StringView cmdline, PromptEvent event, Context& context) + [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)] + (StringView cmdline, PromptEvent event, Context& context) { if (event != PromptEvent::Validate) return; @@ -821,7 +832,8 @@ void regex_prompt(Context& context, String prompt, char reg, T func) [&](auto&& m) { candidates.push_back(m.candidate().str()); return true; }); return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) }; }, - [=, func=T(std::move(func))](StringView str, PromptEvent event, Context& context) mutable { + [=, func=T(std::move(func)), selection_edition=std::make_shared<ScopedSelectionEdition>(context)] + (StringView str, PromptEvent event, Context& context) mutable { try { if (event != PromptEvent::Change and context.has_client()) @@ -941,6 +953,7 @@ void search_next(Context& context, NormalParams params) if (not str.empty()) { Regex regex{str, direction_flags(regex_mode)}; + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); bool main_wrapped = false; do { @@ -1042,6 +1055,7 @@ void split_regex(Context& context, NormalParams params) void split_lines(Context& context, NormalParams params) { const LineCount count{params.count == 0 ? 1 : params.count}; + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); auto& buffer = context.buffer(); Vector<Selection> res; @@ -1068,6 +1082,7 @@ void split_lines(Context& context, NormalParams params) void select_boundaries(Context& context, NormalParams) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); Vector<Selection> res; for (auto& sel : selections) @@ -1083,6 +1098,7 @@ void join_lines_select_spaces(Context& context, NormalParams) { auto& buffer = context.buffer(); Vector<Selection> selections; + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { const LineCount min_line = sel.min().line; @@ -1106,6 +1122,7 @@ void join_lines_select_spaces(Context& context, NormalParams) void join_lines(Context& context, NormalParams params) { + ScopedSelectionEdition selection_edition{context}; SelectionList sels{context.selections()}; auto restore_sels = on_scope_end([&]{ sels.update(); @@ -1123,7 +1140,8 @@ void keep(Context& context, NormalParams params) const char reg = to_lower(params.reg ? params.reg : '/'); regex_prompt(context, prompt.str(), reg, - [reg, saved_reg = RegisterManager::instance()[reg].save(context)] + [reg, saved_reg = RegisterManager::instance()[reg].save(context), + selection_edition=std::make_shared<ScopedSelectionEdition>(context)] (const Regex& regex, PromptEvent event, Context& context) { RegisterManager::instance()[reg].restore(context, saved_reg); if (event == PromptEvent::Abort) @@ -1160,7 +1178,8 @@ void keep_pipe(Context& context, NormalParams params) context.input_handler().prompt( "keep pipe:", {}, default_command, context.faces()["Prompt"], PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete, - [default_command](StringView cmdline, PromptEvent event, Context& context) { + [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)] + (StringView cmdline, PromptEvent event, Context& context) { if (event != PromptEvent::Validate) return; @@ -1206,6 +1225,7 @@ void indent(Context& context, NormalParams params) ScopedEdition edition(context); auto& buffer = context.buffer(); LineCount last_line = 0; + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { for (auto line = std::max(last_line, sel.min().line); line < sel.max().line+1; ++line) @@ -1230,6 +1250,7 @@ void deindent(Context& context, NormalParams params) auto& buffer = context.buffer(); LineCount last_line = 0; + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { for (auto line = std::max(sel.min().line, last_line); @@ -1410,6 +1431,7 @@ void scroll(Context& context, NormalParams params) template<Direction direction> void copy_selections_on_next_lines(Context& context, NormalParams params) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); auto& buffer = context.buffer(); const ColumnCount tabstop = context.options()["tabstop"].get<int>(); @@ -1463,6 +1485,7 @@ template<Direction direction> void rotate_selections(Context& context, NormalParams params) { const int count = params.count ? params.count : 1; + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); const int index = selections.main_index(); const int num = selections.size(); @@ -1480,6 +1503,7 @@ void rotate_selections_content(Context& context, NormalParams params) if (group == 0 or group > (int)strings.size()) group = (int)strings.size(); count = count % group; + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); auto main = strings.begin() + selections.main_index(); for (auto it = strings.begin(); it != strings.end(); ) @@ -1564,6 +1588,7 @@ void replay_macro(Context& context, NormalParams params) auto keys = parse_keys(reg_val[0]); ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; do { for (auto& key : keys) @@ -1581,6 +1606,7 @@ void jump(Context& context, NormalParams params) Buffer* oldbuf = &context.buffer(); Buffer& buffer = const_cast<Buffer&>(jump.buffer()); + ScopedSelectionEdition selection_edition{context}; if (&buffer != oldbuf) context.change_buffer(buffer); context.selections_write_only() = jump; @@ -1595,6 +1621,7 @@ void push_selections(Context& context, NormalParams) void align(Context& context, NormalParams) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); auto& buffer = context.buffer(); const ColumnCount tabstop = context.options()["tabstop"].get<int>(); @@ -1648,6 +1675,7 @@ void copy_indent(Context& context, NormalParams params) { int selection = params.count; auto& buffer = context.buffer(); + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); Vector<LineCount> lines; for (auto sel : selections) @@ -1688,6 +1716,7 @@ void tabs_to_spaces(Context& context, NormalParams params) const ColumnCount tabstop = params.count == 0 ? opt_tabstop : params.count; Vector<Selection> tabs; Vector<String> spaces; + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { for (auto it = buffer.iterator_at(sel.min()), @@ -1712,6 +1741,7 @@ void spaces_to_tabs(Context& context, NormalParams params) const ColumnCount opt_tabstop = context.options()["tabstop"].get<int>(); const ColumnCount tabstop = params.count == 0 ? opt_tabstop : params.count; Vector<Selection> spaces; + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { for (auto it = buffer.iterator_at(sel.min()), @@ -1745,6 +1775,7 @@ void spaces_to_tabs(Context& context, NormalParams params) void trim_selections(Context& context, NormalParams) { auto& buffer = context.buffer(); + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); Vector<int> to_remove; @@ -1773,7 +1804,7 @@ void trim_selections(Context& context, NormalParams) selections.remove(i); } -SelectionList read_selections_from_register(char reg, Context& context) +SelectionList read_selections_from_register(char reg, const Context& context) { if (not is_basic_alpha(reg) and reg != '^') throw runtime_error("selections can only be saved to the '^' and alphabetic registers"); @@ -1868,6 +1899,7 @@ void combine_selections(Context& context, SelectionList list, Func func, StringV return; const auto op = key_to_combine_op(key); + ScopedSelectionEdition selection_edition{context}; auto& sels = context.selections(); list.update(); if (op == CombineOp::Append) @@ -1921,7 +1953,10 @@ void save_selections(Context& context, NormalParams params) }; if (combine and not empty) + { + ScopedSelectionEdition selection_edition{context}; combine_selections(context, read_selections_from_register(reg, context), save_to_reg, "combine selections to register"); + } else save_to_reg(context, context.selections()); } @@ -1932,6 +1967,8 @@ void restore_selections(Context& context, NormalParams params) const char reg = to_lower(params.reg ? params.reg : '^'); auto selections = read_selections_from_register(reg, context); + ScopedSelectionEdition selection_edition{context}; + auto set_selections = [reg](Context& context, SelectionList sels) { auto size = sels.size(); context.selections_write_only() = std::move(sels); @@ -2001,6 +2038,20 @@ void move_in_history(Context& context, NormalParams params) history_id, max_history_id)); } +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(); +} + void exec_user_mappings(Context& context, NormalParams params) { on_next_key_with_autoinfo(context, "user-mapping", KeymapMode::None, @@ -2015,6 +2066,7 @@ void exec_user_mappings(Context& context, NormalParams params) InputHandler::ScopedForceNormal force_normal{context.input_handler(), params}; ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; for (auto& key : mapping.keys) context.input_handler().handle_key(key); }, "user mapping", @@ -2027,6 +2079,7 @@ void add_empty_line(Context& context, NormalParams params) int count = std::max(params.count, 1); String new_lines{'\n', CharCount{count}}; auto& buffer = context.buffer(); + ScopedSelectionEdition selection_edition{context}; auto& sels = context.selections(); ScopedEdition edition{context}; for (int i = 0; i < sels.size(); ++i) @@ -2045,6 +2098,7 @@ public: void operator() (Context& context, NormalParams params) { ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; do { m_func(context, {0, params.reg}); } while(--params.count > 0); } private: @@ -2055,6 +2109,7 @@ template<void (*func)(Context&, NormalParams)> void repeated(Context& context, NormalParams params) { ScopedEdition edition(context); + ScopedSelectionEdition selection_edition{context}; do { func(context, {0, params.reg}); } while(--params.count > 0); } @@ -2064,6 +2119,7 @@ void move_cursor(Context& context, NormalParams params) kak_assert(mode == SelectMode::Replace or mode == SelectMode::Extend); const Type offset{direction * std::max(params.count,1)}; const ColumnCount tabstop = context.options()["tabstop"].get<int>(); + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); for (auto& sel : selections) { @@ -2077,11 +2133,13 @@ void move_cursor(Context& context, NormalParams params) void select_whole_buffer(Context& context, NormalParams) { auto& buffer = context.buffer(); + ScopedSelectionEdition selection_edition{context}; context.selections_write_only() = SelectionList{buffer, {{0,0}, {buffer.back_coord(), max_column}}}; } void keep_selection(Context& context, NormalParams p) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); const int index = p.count ? p.count-1 : selections.main_index(); if (index >= selections.size()) @@ -2093,6 +2151,7 @@ void keep_selection(Context& context, NormalParams p) void remove_selection(Context& context, NormalParams p) { + ScopedSelectionEdition selection_edition{context}; auto& selections = context.selections(); const int index = p.count ? p.count-1 : selections.main_index(); if (index >= selections.size()) @@ -2106,12 +2165,14 @@ void remove_selection(Context& context, NormalParams p) void clear_selections(Context& context, NormalParams) { + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) sel.anchor() = sel.cursor(); } void flip_selections(Context& context, NormalParams) { + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { const BufferCoord tmp = sel.anchor(); @@ -2123,6 +2184,7 @@ void flip_selections(Context& context, NormalParams) void ensure_forward(Context& context, NormalParams) { + ScopedSelectionEdition selection_edition{context}; for (auto& sel : context.selections()) { const BufferCoord min = sel.min(), max = sel.max(); @@ -2134,18 +2196,21 @@ void ensure_forward(Context& context, NormalParams) void merge_consecutive(Context& context, NormalParams params) { + ScopedSelectionEdition selection_edition{context}; ensure_forward(context, params); context.selections().merge_consecutive(); } void merge_overlapping(Context& context, NormalParams params) { + ScopedSelectionEdition selection_edition{context}; ensure_forward(context, params); context.selections().merge_overlapping(); } void duplicate_selections(Context& context, NormalParams params) { + ScopedSelectionEdition selection_edition{context}; SelectionList& sels = context.selections(); Vector<Selection> new_sels; const int count = params.count ? params.count : 2; @@ -2312,6 +2377,9 @@ static constexpr HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend> { {alt('u')}, {"move backward in history", move_in_history<Direction::Backward>} }, { {alt('U')}, {"move forward in history", move_in_history<Direction::Forward>} }, + { {ctrl('h')}, {"undo selection change", undo_selection_change} }, + { {ctrl('k')}, {"redo selection change", redo_selection_change} }, + { {alt('i')}, {"select inner object", select_object<ObjectFlags::ToBegin | ObjectFlags::ToEnd | ObjectFlags::Inner>} }, { {alt('a')}, {"select whole object", select_object<ObjectFlags::ToBegin | ObjectFlags::ToEnd>} }, { {'['}, {"select to object start", select_object<ObjectFlags::ToBegin>} }, |
