diff --git a/src/client.cc b/src/client.cc index bdbcaf78..c9788951 100644 --- a/src/client.cc +++ b/src/client.cc @@ -81,7 +81,7 @@ static void reload_buffer(Context& context, const String& filename) if (not buf) return; Window& win = ClientManager::instance().get_unused_window_for_buffer(*buf); - win.select(cursor_pos); + win.selections() = SelectionList{cursor_pos}; win.set_position(view_pos); context.change_editor(win); context.print_status({ "'" + buf->display_name() + "' reloaded", diff --git a/src/commands.cc b/src/commands.cc index 6518cc01..5eaf0ab8 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -127,7 +127,7 @@ void edit(CommandParameters params, Context& context) int column = param_count > 2 and not parser[2].empty() ? std::max(0, str_to_int(parser[2]) - 1) : 0; - context.editor().select(context.buffer().clamp({ line, column })); + context.editor().selections() = context.buffer().clamp({ line, column }); if (context.has_window()) context.window().center_selection(); } @@ -571,13 +571,13 @@ void context_wrap(CommandParameters params, Context& context, Func func) Editor& editor = real_context->editor(); InputHandler input_handler(editor, real_context->name()); DynamicSelectionList sels{editor.buffer(), editor.selections()}; - auto restore_sels = on_scope_end([&]{ editor.select(sels); }); + auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); }); if (parser.has_option("itersel")) { for (auto& sel : sels) { - editor.select(sel); + editor.selections() = sel; func(parser, input_handler.context()); } } diff --git a/src/debug.cc b/src/debug.cc index af935bb7..7b97b2f0 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -21,10 +21,8 @@ static Buffer& get_or_create_debug_buffer() void write_debug(const String& str) { - Buffer& debug_buffer = get_or_create_debug_buffer(); - Editor editor(debug_buffer); - editor.select(debug_buffer.back_coord()); - editor.insert(str + "\n"); + Buffer& buffer = get_or_create_debug_buffer(); + buffer.insert(buffer.end(), str); } } diff --git a/src/editor.cc b/src/editor.cc index 344acf5f..0863e94c 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -14,7 +14,7 @@ namespace Kakoune Editor::Editor(Buffer& buffer) : m_buffer(&buffer), m_edition_level(0), - m_selections(buffer, { {{},{}} }) + m_selections(buffer, {BufferCoord{}}) {} void Editor::erase() @@ -151,45 +151,6 @@ void Editor::move_selections(LineCount offset, SelectMode mode) m_selections.sort_and_merge_overlapping(); } -void Editor::select(const Selection& selection, SelectMode mode) -{ - if (mode == SelectMode::Replace) - m_selections = SelectionList{ selection }; - else if (mode == SelectMode::Extend) - { - m_selections.main().merge_with(selection); - m_selections = SelectionList{ std::move(m_selections.main()) }; - } - else if (mode == SelectMode::Append) - { - m_selections.push_back(selection); - m_selections.set_main_index(m_selections.size()-1); - m_selections.sort_and_merge_overlapping(); - } - else - kak_assert(false); - check_invariant(); -} - -void Editor::select(SelectionList selections) -{ - if (selections.empty()) - throw runtime_error("no selections"); - m_selections = std::move(selections); - check_invariant(); -} - -struct nothing_selected : public runtime_error -{ - nothing_selected() : runtime_error("nothing was selected") {} -}; - -void Editor::select(const Selector& selector) -{ - selector(*m_buffer, m_selections); - check_invariant(); -} - class ModifiedRangesListener : public BufferChangeListener_AutoRegister { public: diff --git a/src/editor.hh b/src/editor.hh index d98b4ae2..d6884340 100644 --- a/src/editor.hh +++ b/src/editor.hh @@ -57,16 +57,11 @@ public: SelectMode mode = SelectMode::Replace); void move_selections(CharCount move, SelectMode mode = SelectMode::Replace); - void select(BufferCoord c, SelectMode mode = SelectMode::Replace) - { select(Selection{ buffer().clamp(c) }, mode); } - void select(const Selection& sel, - SelectMode mode = SelectMode::Replace); - void select(SelectionList selections); - void select(const Selector& selector); void rotate_selections(int count) { m_selections.rotate_main(count); } const SelectionList& selections() const { return m_selections; } + SelectionList& selections() { return m_selections; } std::vector selections_content() const; bool undo(); diff --git a/src/normal.cc b/src/normal.cc index e2bfbe03..abcbe09f 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -30,45 +30,44 @@ public: void operator() (Context& context, int) { - context.editor().select([this](const Buffer& buffer, SelectionList& selections) + auto& buffer = context.buffer(); + auto& selections = context.editor().selections(); + if (mode == SelectMode::Append) { - if (mode == SelectMode::Append) + auto& sel = selections.main(); + auto res = m_func(buffer, sel); + if (res.captures().empty()) + res.captures() = sel.captures(); + selections.push_back(res); + selections.set_main_index(selections.size() - 1); + } + else if (mode == SelectMode::ReplaceMain) + { + auto& sel = selections.main(); + auto res = m_func(buffer, sel); + sel.first() = res.first(); + sel.last() = res.last(); + if (not res.captures().empty()) + sel.captures() = std::move(res.captures()); + } + else + { + for (auto& sel : selections) { - auto& sel = selections.main(); - auto res = m_func(buffer, sel); - if (res.captures().empty()) - res.captures() = sel.captures(); - selections.push_back(res); - selections.set_main_index(selections.size() - 1); - } - else if (mode == SelectMode::ReplaceMain) - { - auto& sel = selections.main(); - auto res = m_func(buffer, sel); - sel.first() = res.first(); - sel.last() = res.last(); + auto res = m_func(buffer, sel); + if (mode == SelectMode::Extend) + sel.merge_with(res); + else + { + sel.first() = res.first(); + sel.last() = res.last(); + } if (not res.captures().empty()) sel.captures() = std::move(res.captures()); } - else - { - for (auto& sel : selections) - { - auto res = m_func(buffer, sel); - if (mode == SelectMode::Extend) - sel.merge_with(res); - else - { - sel.first() = res.first(); - sel.last() = res.last(); - } - if (not res.captures().empty()) - sel.captures() = std::move(res.captures()); - } - } - selections.sort_and_merge_overlapping(); - selections.check_invariant(); - }); + } + selections.sort_and_merge_overlapping(); + selections.check_invariant(); } private: T m_func; @@ -77,6 +76,18 @@ private: template constexpr Select select(T func) { return Select(func); } +template +void select_coord(BufferCoord coord, SelectionList& selections) +{ + if (mode == SelectMode::Replace) + selections = SelectionList { coord }; + else if (mode == SelectMode::Extend) + { + for (auto& sel : selections) + sel.last() = coord; + selections.sort_and_merge_overlapping(); + } +} template void insert(Context& context, int) @@ -119,7 +130,7 @@ void goto_commands(Context& context, int line) if (line != 0) { context.push_jump(); - context.editor().select(BufferCoord{line - 1, 0}); + select_coord(LineCount{line - 1}, context.editor().selections()); if (context.has_window()) context.window().center_selection(); } @@ -135,7 +146,7 @@ void goto_commands(Context& context, int line) case 'g': case 'k': context.push_jump(); - editor.select(BufferCoord{0,0}, mode); + select_coord(BufferCoord{0,0}, context.editor().selections()); break; case 'l': select(select_to_eol)(context, 0); @@ -146,18 +157,18 @@ void goto_commands(Context& context, int line) case 'j': { context.push_jump(); - editor.select({editor.buffer().line_count() - 1, 0}, mode); + select_coord({editor.buffer().line_count() - 1, 0}, editor.selections()); break; } case 'e': context.push_jump(); - editor.select(editor.buffer().back_coord(), mode); + select_coord(editor.buffer().back_coord(), editor.selections()); break; case 't': if (context.has_window()) { auto line = context.window().position().line; - editor.select({line, 0}, mode); + select_coord(line, editor.selections()); } break; case 'b': @@ -165,7 +176,7 @@ void goto_commands(Context& context, int line) { auto& window = context.window(); auto line = window.position().line + window.dimensions().line - 1; - editor.select({line, 0}, mode); + select_coord(line, editor.selections()); } break; case 'c': @@ -173,7 +184,7 @@ void goto_commands(Context& context, int line) { auto& window = context.window(); auto line = window.position().line + window.dimensions().line / 2; - editor.select({line, 0}, mode); + select_coord(line, editor.selections()); } break; case 'a': @@ -273,8 +284,8 @@ void replace_with_char(Context& context, int) return; Editor& editor = context.editor(); SelectionList sels = editor.selections(); - auto restore_sels = on_scope_end([&]{ editor.select(std::move(sels)); }); - editor.select(std::bind(select_all_matches, _1, _2, Regex{"."})); + auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); }); + select_all_matches(editor.buffer(), editor.selections(), Regex{"."}); editor.insert(codepoint_to_str(key.key), InsertMode::Replace); }, "replace with char", "enter char to replace with\n"); } @@ -359,7 +370,7 @@ void search(Context& context, int) [selections](const String& str, PromptEvent event, Context& context) { try { - context.editor().select(selections); + context.editor().selections() = selections; if (event == PromptEvent::Abort) return; @@ -377,7 +388,7 @@ void search(Context& context, int) else if (str.empty() or not context.options()["incsearch"].get()) return; - context.editor().select(std::bind(select_next_match, _1, _2, ex)); + select_next_match(context.buffer(), context.editor().selections(), ex); } catch (boost::regex_error& err) { @@ -388,7 +399,7 @@ void search(Context& context, int) } catch (runtime_error&) { - context.editor().select(selections); + context.editor().selections() = selections; // only validation should propagate errors, // incremental search should not. if (event == PromptEvent::Validate) @@ -407,7 +418,7 @@ void search_next(Context& context, int param) { Regex ex{str}; do { - context.editor().select(std::bind(select_next_match, _1, _2, ex)); + select_next_match(context.buffer(), context.editor().selections(), ex); } while (--param > 0); } catch (boost::regex_error& err) @@ -534,7 +545,7 @@ void select_regex(Context& context, int) else RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty() and not ex.str().empty()) - context.editor().select(std::bind(select_all_matches, _1, _2, ex)); + select_all_matches(context.buffer(), context.editor().selections(), ex); }); } @@ -546,48 +557,46 @@ void split_regex(Context& context, int) else RegisterManager::instance()['/'] = String{ex.str()}; if (not ex.empty() and not ex.str().empty()) - context.editor().select(std::bind(split_selection, _1, _2, ex)); + split_selections(context.buffer(), context.editor().selections(), ex); }); } void split_lines(Context& context, int) { - context.editor().select([](const Buffer& buffer, - SelectionList& selections) { - SelectionList res; - for (auto& sel : selections) + auto& selections = context.editor().selections(); + auto& buffer = context.buffer(); + SelectionList res; + for (auto& sel : selections) + { + if (sel.first().line == sel.last().line) { - if (sel.first().line == sel.last().line) - { - res.push_back(std::move(sel)); - continue; - } - auto min = sel.min(); - auto max = sel.max(); - res.push_back({min, {min.line, buffer[min.line].length()-1}}); - for (auto line = min.line+1; line < max.line; ++line) - res.push_back({line, {line, buffer[line].length()-1}}); - res.push_back({max.line, max}); + res.push_back(std::move(sel)); + continue; } - selections = std::move(res); - }); + auto min = sel.min(); + auto max = sel.max(); + res.push_back({min, {min.line, buffer[min.line].length()-1}}); + for (auto line = min.line+1; line < max.line; ++line) + res.push_back({line, {line, buffer[line].length()-1}}); + res.push_back({max.line, max}); + } + selections = std::move(res); } void join_select_spaces(Context& context, int) { select(select_whole_lines)(context, 0); select(select_to_eol)(context, 0); - Editor& editor = context.editor(); - editor.select([](const Buffer& buffer, SelectionList& sel) - { - select_all_matches(buffer, sel, Regex{"(\n\\h*)+"}); - // remove last end of line if selected - kak_assert(std::is_sorted(sel.begin(), sel.end(), - [](const Selection& lhs, const Selection& rhs) - { return lhs.min() < rhs.min(); })); - if (not sel.empty() and sel.back().max() == buffer.back_coord()) - sel.pop_back(); - }); + auto& editor = context.editor(); + auto& buffer = context.buffer(); + auto& selections = editor.selections(); + select_all_matches(buffer, editor.selections(), Regex{"(\n\\h*)+"}); + // remove last end of line if selected + kak_assert(std::is_sorted(selections.begin(), selections.end(), + [](const Selection& lhs, const Selection& rhs) + { return lhs.min() < rhs.min(); })); + if (not selections.empty() and selections.back().max() == buffer.back_coord()) + selections.pop_back(); editor.insert(" ", InsertMode::Replace); } @@ -595,7 +604,7 @@ void join(Context& context, int param) { Editor& editor = context.editor(); DynamicSelectionList sels{editor.buffer(), editor.selections()}; - auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); }); + auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); }); join_select_spaces(context, param); } @@ -617,7 +626,7 @@ void keep(Context& context, int) } if (keep.empty()) throw runtime_error("no selections remaining"); - editor.select(std::move(keep)); + editor.selections() = std::move(keep); }); } @@ -627,21 +636,20 @@ void indent(Context& context, int) CharCount indent_width = context.options()["indentwidth"].get(); String indent = indent_width == 0 ? "\t" : String{' ', indent_width}; - Editor& editor = context.editor(); + auto& editor = context.editor(); + auto& buffer = context.buffer(); DynamicSelectionList sels{editor.buffer(), editor.selections()}; - auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); }); - editor.select([&indent](const Buffer& buf, SelectionList& selections) { - SelectionList res; - for (auto& sel : selections) - { - for (auto line = sel.min().line; line < sel.max().line+1; ++line) - { - if (indent_empty or buf[line].length() > 1) - res.emplace_back(line, line); - } - } - selections = std::move(res); - }); + auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); }); + SelectionList res; + for (auto& sel : editor.selections()) + { + for (auto line = sel.min().line; line < sel.max().line+1; ++line) + { + if (indent_empty or buffer[line].length() > 1) + res.emplace_back(line, line); + } + } + editor.selections() = std::move(res); editor.insert(indent, InsertMode::Insert); } @@ -653,41 +661,40 @@ void deindent(Context& context, int) if (indent_width == 0) indent_width = tabstop; - Editor& editor = context.editor(); + auto& editor = context.editor(); + auto& buffer = context.buffer(); DynamicSelectionList sels{editor.buffer(), editor.selections()}; - auto restore_sels = on_scope_end([&]{ editor.select((SelectionList)std::move(sels)); }); + auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); }); - editor.select([indent_width,tabstop](const Buffer& buf, SelectionList& selections) { - SelectionList res; - for (auto& sel : selections) + SelectionList res; + for (auto& sel : editor.selections()) + { + for (auto line = sel.min().line; line < sel.max().line+1; ++line) + { + CharCount width = 0; + auto& content = buffer[line]; + for (auto column = 0_byte; column < content.length(); ++column) { - for (auto line = sel.min().line; line < sel.max().line+1; ++line) + const char c = content[column]; + if (c == '\t') + width = (width / tabstop + 1) * tabstop; + else if (c == ' ') + ++width; + else { - CharCount width = 0; - auto& content = buf[line]; - for (auto column = 0_byte; column < content.length(); ++column) - { - const char c = content[column]; - if (c == '\t') - width = (width / tabstop + 1) * tabstop; - else if (c == ' ') - ++width; - else - { - if (deindent_incomplete and width != 0) - res.emplace_back(line, BufferCoord{line, column-1}); - break; - } - if (width == indent_width) - { - res.emplace_back(line, BufferCoord{line, column}); - break; - } - } + if (deindent_incomplete and width != 0) + res.emplace_back(line, BufferCoord{line, column-1}); + break; + } + if (width == indent_width) + { + res.emplace_back(line, BufferCoord{line, column}); + break; } } - selections = std::move(res); - }); + } + } + editor.selections() = std::move(res); editor.erase(); } @@ -773,7 +780,7 @@ void scroll(Context& context, int) auto cursor_pos = utf8::advance(buffer.iterator_at(position.line), buffer.iterator_at(position.line+1), position.column); - window.select(cursor_pos.coord()); + select_coord(cursor_pos.coord(), window.selections()); window.set_position(position); } @@ -868,7 +875,7 @@ void jump(Context& context, int) auto& manager = ClientManager::instance(); context.change_editor(manager.get_unused_window_for_buffer(buffer)); } - context.editor().select(SelectionList{ jump }); + context.editor().selections() = jump; } void save_selections(Context& context, int) @@ -1051,14 +1058,14 @@ KeyMap keymap = { '.', repeat_insert }, - { '%', [](Context& context, int) { context.editor().select(select_whole_buffer); } }, + { '%', [](Context& context, int) { select_whole_buffer(context.buffer(), context.editor().selections()); } }, { ':', command }, { '|', pipe }, - { ' ', [](Context& context, int count) { if (count == 0) context.editor().select(clear_selections); - else context.editor().select(std::bind(keep_selection, _1, _2, count-1)); } }, - { alt(' '), [](Context& context, int count) { if (count == 0) context.editor().select(flip_selections); - else context.editor().select(std::bind(remove_selection, _1, _2, count-1)); } }, + { ' ', [](Context& context, int count) { if (count == 0) clear_selections(context.buffer(), context.editor().selections()); + else keep_selection(context.editor().selections(), count-1); } }, + { alt(' '), [](Context& context, int count) { if (count == 0) flip_selections(context.editor().selections()); + else remove_selection(context.editor().selections(), count-1); } }, { 'w', repeated(select(select_to_next_word)) }, { 'e', repeated(select(select_to_next_word_end)) }, { 'b', repeated(select(select_to_previous_word)) }, diff --git a/src/selection.hh b/src/selection.hh index cd60f87d..e8278625 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -94,6 +94,7 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs) struct SelectionList : std::vector { SelectionList() = default; + SelectionList(BufferCoord c) : std::vector{Selection{c,c}} {} SelectionList(Selection s) : std::vector{s} {} void update_insert(const Buffer& buffer, BufferCoord begin, BufferCoord end); diff --git a/src/selectors.cc b/src/selectors.cc index d0135f1d..6501fc1f 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -438,8 +438,8 @@ void select_all_matches(const Buffer& buffer, SelectionList& selections, selections = std::move(result); } -void split_selection(const Buffer& buffer, SelectionList& selections, - const Regex& regex) +void split_selections(const Buffer& buffer, SelectionList& selections, + const Regex& regex) { SelectionList result; for (auto& sel : selections) diff --git a/src/selectors.hh b/src/selectors.hh index a44cf830..3d3d8576 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -19,14 +19,14 @@ inline void clear_selections(const Buffer& buffer, SelectionList& selections) selections = SelectionList{ std::move(sel) }; } -inline void flip_selections(const Buffer&, SelectionList& selections) +inline void flip_selections(SelectionList& selections) { for (auto& sel : selections) std::swap(sel.first(), sel.last()); selections.check_invariant(); } -inline void keep_selection(const Buffer&, SelectionList& selections, int index) +inline void keep_selection(SelectionList& selections, int index) { if (index < selections.size()) { @@ -36,7 +36,7 @@ inline void keep_selection(const Buffer&, SelectionList& selections, int index) selections.check_invariant(); } -inline void remove_selection(const Buffer&, SelectionList& selections, int index) +inline void remove_selection(SelectionList& selections, int index) { if (selections.size() > 1 and index < selections.size()) { @@ -282,8 +282,8 @@ void select_next_match(const Buffer& buffer, SelectionList& selections, void select_all_matches(const Buffer& buffer, SelectionList& selections, const Regex& regex); -void split_selection(const Buffer& buffer, SelectionList& selections, - const Regex& separator_regex); +void split_selections(const Buffer& buffer, SelectionList& selections, + const Regex& separator_regex); using CodepointPair = std::pair; Selection select_surrounding(const Buffer& buffer, const Selection& selection, diff --git a/src/unit_tests.cc b/src/unit_tests.cc index 278e0501..d3cda91d 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -76,8 +76,8 @@ void test_editor() { scoped_edition edition{editor}; - editor.select(select_whole_buffer); - editor.select(std::bind(select_all_matches, _1, _2, Regex{"\\n\\h*"})); + select_whole_buffer(buffer, editor.selections()); + select_all_matches(buffer, editor.selections(), Regex{"\\n\\h*"}); for (auto& sel : editor.selections()) { kak_assert(buffer.byte_at(sel.min()) == '\n'); @@ -87,7 +87,7 @@ void test_editor() editor.undo(); Selection sel{ 2_line, buffer.back_coord() }; - editor.select(sel, SelectMode::Replace); + editor.selections() = SelectionList{sel}; editor.insert("",InsertMode::Replace); kak_assert(not buffer.is_end(editor.selections().main().first())); }