diff --git a/src/client.cc b/src/client.cc index d999816f..649b22dc 100644 --- a/src/client.cc +++ b/src/client.cc @@ -850,7 +850,8 @@ class Insert : public InputMode public: Insert(Client& client, InsertMode mode) : InputMode(client), - m_inserter(context().editor(), mode), + m_insert_mode(mode), + m_edition(context().editor()), m_completer(context()), m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) { @@ -861,6 +862,7 @@ public: last_insert().first = mode; last_insert().second.clear(); context().hooks().run_hook("InsertBegin", "", context()); + prepare(m_insert_mode); } void on_key(Key key) override @@ -869,7 +871,7 @@ public: if (m_mode == Mode::InsertReg) { if (key.modifiers == Key::Modifiers::None) - m_inserter.insert(RegisterManager::instance()[key.key].values(context())); + insert(RegisterManager::instance()[key.key].values(context())); m_mode = Mode::Default; return; } @@ -896,38 +898,35 @@ public: reset_normal_mode(); } else if (key == Key::Backspace) - m_inserter.erase(); + erase(); else if (key == Key::Left) { - m_inserter.move_cursors(-1_char); + m_edition.editor().move_selections(-1_char, SelectMode::Replace); moved = true; } else if (key == Key::Right) { - m_inserter.move_cursors(1_char); + m_edition.editor().move_selections(1_char, SelectMode::Replace); moved = true; } else if (key == Key::Up) { - m_inserter.move_cursors(-1_line); + m_edition.editor().move_selections(-1_line, SelectMode::Replace); moved = true; } else if (key == Key::Down) { - m_inserter.move_cursors(1_line); + m_edition.editor().move_selections(1_line, SelectMode::Replace); moved = true; } else if (key.modifiers == Key::Modifiers::None) - { - m_inserter.insert(codepoint_to_str(key.key)); - context().hooks().run_hook("InsertKey", key_to_str(key), context()); - } + insert(key.key); else if (key == ctrl('r')) m_mode = Mode::InsertReg; else if ( key == ctrl('m')) - m_inserter.insert(String() + '\n'); + insert('\n'); else if ( key == ctrl('i')) - m_inserter.insert(String() + '\t'); + insert('\t'); else if ( key == ctrl('n')) { m_completer.select(1); @@ -957,11 +956,131 @@ public: KeymapMode keymap_mode() const override { return KeymapMode::Insert; } private: + void erase() const + { + auto& buffer = m_edition.editor().buffer(); + for (auto& sel : m_edition.editor().selections()) + { + if (sel.last() == BufferCoord{0,0}) + continue; + auto pos = buffer.iterator_at(sel.last()); + buffer.erase(utf8::previous(pos), pos); + } + } + + void insert(memoryview strings) + { + auto& buffer = m_edition.editor().buffer(); + auto& selections = m_edition.editor().selections(); + for (size_t i = 0; i < selections.size(); ++i) + { + size_t index = std::min(i, strings.size()-1); + buffer.insert(buffer.iterator_at(selections[i].last()), + strings[index]); + } + } + + void insert(Codepoint key) + { + auto& buffer = m_edition.editor().buffer(); + for (auto& sel : m_edition.editor().m_selections) + { + auto content = codepoint_to_str(key); + m_edition.editor().filters()(buffer, sel, content); + buffer.insert(buffer.iterator_at(sel.last()), content); + } + context().hooks().run_hook("InsertKey", codepoint_to_str(key), context()); + } + + void prepare(InsertMode mode) + { + Editor& editor = m_edition.editor(); + Buffer& buffer = editor.buffer(); + + for (auto& sel : editor.m_selections) + { + BufferCoord first, last; + switch (mode) + { + case InsertMode::Insert: + first = sel.max(); + last = sel.min(); + break; + case InsertMode::Replace: + first = last = Kakoune::erase(buffer, sel).coord(); + break; + case InsertMode::Append: + first = sel.min(); + last = sel.max(); + // special case for end of lines, append to current line instead + if (last.column != buffer[last.line].length() - 1) + last = buffer.char_next(last); + break; + + case InsertMode::OpenLineBelow: + case InsertMode::AppendAtLineEnd: + first = last = BufferCoord{sel.max().line, buffer[sel.max().line].length() - 1}; + break; + + case InsertMode::OpenLineAbove: + case InsertMode::InsertAtLineBegin: + first = sel.min().line; + if (mode == InsertMode::OpenLineAbove) + first = buffer.char_prev(first); + else + { + auto first_non_blank = buffer.iterator_at(first); + while (*first_non_blank == ' ' or *first_non_blank == '\t') + ++first_non_blank; + if (*first_non_blank != '\n') + first = first_non_blank.coord(); + } + last = first; + break; + case InsertMode::InsertAtNextLineBegin: + kak_assert(false); // not implemented + break; + } + if (buffer.is_end(first)) + first = buffer.char_prev(first); + if (buffer.is_end(last)) + last = buffer.char_prev(last); + sel.first() = first; + sel.last() = last; + } + if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove) + { + insert('\n'); + if (mode == InsertMode::OpenLineAbove) + { + for (auto& sel : editor.m_selections) + { + // special case, the --first line above did nothing, so we need to compensate now + if (sel.first() == buffer.char_next({0,0})) + sel.first() = sel.last() = BufferCoord{0,0}; + } + } + } + sort_and_merge_overlapping(editor.m_selections, editor.m_main_sel); + editor.check_invariant(); + } + + void on_replaced() override + { + for (auto& sel : m_edition.editor().m_selections) + { + if (m_insert_mode == InsertMode::Append and sel.last().column > 0) + sel.last() = m_edition.editor().buffer().char_prev(sel.last()); + avoid_eol(m_edition.editor().buffer(), sel); + } + } + enum class Mode { Default, Complete, InsertReg }; Mode m_mode = Mode::Default; - IncrementalInserter m_inserter; - BufferCompleter m_completer; - Timer m_idle_timer; + InsertMode m_insert_mode; + scoped_edition m_edition; + BufferCompleter m_completer; + Timer m_idle_timer; }; } diff --git a/src/editor.cc b/src/editor.cc index 225d5413..6ee3f433 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -20,7 +20,7 @@ Editor::Editor(Buffer& buffer) m_main_sel = 0; } -static void avoid_eol(const Buffer& buffer, BufferCoord& coord) +void avoid_eol(const Buffer& buffer, BufferCoord& coord) { const auto column = coord.column; const auto& line = buffer[coord.line]; @@ -28,7 +28,7 @@ static void avoid_eol(const Buffer& buffer, BufferCoord& coord) coord.column = line.byte_count_to(line.char_length() - 2); } -static void avoid_eol(const Buffer& buffer, Range& sel) +void avoid_eol(const Buffer& buffer, Range& sel) { avoid_eol(buffer, sel.first()); avoid_eol(buffer, sel.last()); @@ -450,130 +450,4 @@ void Editor::end_edition() --m_edition_level; } -IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode) - : m_editor(editor), m_edition(editor), m_mode(mode) -{ - Buffer& buffer = *editor.m_buffer; - - for (auto& sel : m_editor.m_selections) - { - BufferCoord first, last; - switch (mode) - { - case InsertMode::Insert: - first = sel.max(); - last = sel.min(); - break; - case InsertMode::Replace: - first = last = Kakoune::erase(buffer, sel).coord(); - break; - case InsertMode::Append: - first = sel.min(); - last = sel.max(); - // special case for end of lines, append to current line instead - if (last.column != buffer[last.line].length() - 1) - last = buffer.char_next(last); - break; - - case InsertMode::OpenLineBelow: - case InsertMode::AppendAtLineEnd: - first = last = BufferCoord{sel.max().line, buffer[sel.max().line].length() - 1}; - break; - - case InsertMode::OpenLineAbove: - case InsertMode::InsertAtLineBegin: - first = sel.min().line; - if (mode == InsertMode::OpenLineAbove) - first = buffer.char_prev(first); - else - { - auto first_non_blank = buffer.iterator_at(first); - while (*first_non_blank == ' ' or *first_non_blank == '\t') - ++first_non_blank; - if (*first_non_blank != '\n') - first = first_non_blank.coord(); - } - last = first; - break; - case InsertMode::InsertAtNextLineBegin: - kak_assert(false); // not implemented - break; - } - if (buffer.is_end(first)) - first = buffer.char_prev(first); - if (buffer.is_end(last)) - last = buffer.char_prev(last); - sel.first() = first; - sel.last() = last; - } - if (mode == InsertMode::OpenLineBelow or mode == InsertMode::OpenLineAbove) - { - insert("\n"); - if (mode == InsertMode::OpenLineAbove) - { - for (auto& sel : m_editor.m_selections) - { - // special case, the --first line above did nothing, so we need to compensate now - if (sel.first() == buffer.char_next({0,0})) - sel.first() = sel.last() = BufferCoord{0,0}; - } - } - } - sort_and_merge_overlapping(editor.m_selections, editor.m_main_sel); - editor.check_invariant(); -} - -IncrementalInserter::~IncrementalInserter() -{ - for (auto& sel : m_editor.m_selections) - { - if (m_mode == InsertMode::Append and sel.last().column > 0) - sel.last() = m_editor.buffer().char_prev(sel.last()); - avoid_eol(m_editor.buffer(), sel); - } -} - -void IncrementalInserter::insert(String content) -{ - auto& buffer = m_editor.buffer(); - for (auto& sel : m_editor.m_selections) - { - m_editor.filters()(buffer, sel, content); - buffer.insert(buffer.iterator_at(sel.last()), content); - } -} - -void IncrementalInserter::insert(memoryview strings) -{ - auto& buffer = m_editor.buffer(); - for (size_t i = 0; i < m_editor.m_selections.size(); ++i) - { - size_t index = std::min(i, strings.size()-1); - buffer.insert(buffer.iterator_at(m_editor.m_selections[i].last()), - strings[index]); - } -} - -void IncrementalInserter::erase() -{ - auto& buffer = m_editor.buffer(); - for (auto& sel : m_editor.m_selections) - { - if (sel.last() == BufferCoord{0,0}) - continue; - auto pos = buffer.iterator_at(sel.last()); - buffer.erase(utf8::previous(pos), pos); - } -} - -void IncrementalInserter::move_cursors(CharCount move) -{ - m_editor.move_selections(move, SelectMode::Replace); -} - -void IncrementalInserter::move_cursors(LineCount move) -{ - m_editor.move_selections(move, SelectMode::Replace); -} - } diff --git a/src/editor.hh b/src/editor.hh index 0104d43d..95ef2f52 100644 --- a/src/editor.hh +++ b/src/editor.hh @@ -9,6 +9,8 @@ namespace Kakoune { +namespace InputModes { class Insert; } + class Register; enum class SelectMode @@ -88,6 +90,7 @@ public: bool is_editing() const { return m_edition_level!= 0; } private: friend struct scoped_edition; + friend class InputModes::Insert; void begin_edition(); void end_edition(); @@ -98,8 +101,6 @@ private: void check_invariant() const; - friend class IncrementalInserter; - safe_ptr m_buffer; DynamicSelectionList m_selections; size_t m_main_sel; @@ -114,31 +115,15 @@ struct scoped_edition ~scoped_edition() { m_editor.end_edition(); } + + Editor& editor() const { return m_editor; } private: Editor& m_editor; }; -// An IncrementalInserter manage insert mode -class IncrementalInserter -{ -public: - IncrementalInserter(Editor& editor, InsertMode mode = InsertMode::Insert); - ~IncrementalInserter(); - - void insert(String content); - void insert(memoryview strings); - void erase(); - void move_cursors(CharCount move); - void move_cursors(LineCount move); - - Buffer& buffer() const { return m_editor.buffer(); } - Editor& editor() const { return m_editor; } - -private: - InsertMode m_mode; - Editor& m_editor; - scoped_edition m_edition; -}; +void avoid_eol(const Buffer& buffer, BufferCoord& coord); +void avoid_eol(const Buffer& buffer, Range& sel); +void sort_and_merge_overlapping(SelectionList& selections, size_t& main_selection); } diff --git a/src/unit_tests.cc b/src/unit_tests.cc index e49f7b3a..4a6b149d 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -92,30 +92,6 @@ void test_editor() kak_assert(not buffer.is_end(editor.main_selection().first())); } -void test_incremental_inserter() -{ - Buffer buffer("test", Buffer::Flags::None, { "test\n", "\n", "yoüpi\n", "matin\n" }); - Editor editor(buffer); - - editor.select({0,0}); - { - IncrementalInserter inserter(editor, InsertMode::OpenLineAbove); - kak_assert(editor.is_editing()); - kak_assert(editor.selections().size() == 1); - kak_assert(editor.selections().front().first() == BufferCoord{0 COMMA 0}); - kak_assert(editor.selections().front().last() == BufferCoord{0 COMMA 0}); - kak_assert(*buffer.begin() == L'\n'); - } - // check utf-8 erase - editor.select({3,4}); - { - IncrementalInserter inserter(editor, InsertMode::Insert); - inserter.erase(); - kak_assert(editor.selections().back().last() == BufferCoord{3 COMMA 2}); - } - kak_assert(not editor.is_editing()); -} - void test_utf8() { String str = "maïs mélange bientôt"; @@ -171,5 +147,4 @@ void run_unit_tests() test_buffer(); test_undo_group_optimizer(); test_editor(); - test_incremental_inserter(); }