Remove IncrementalInserter and move it's code to InputModes::Insert
This commit is contained in:
parent
3e1bb777ce
commit
3e12507636
151
src/client.cc
151
src/client.cc
|
@ -850,7 +850,8 @@ class Insert : public InputMode
|
||||||
public:
|
public:
|
||||||
Insert(Client& client, InsertMode mode)
|
Insert(Client& client, InsertMode mode)
|
||||||
: InputMode(client),
|
: InputMode(client),
|
||||||
m_inserter(context().editor(), mode),
|
m_insert_mode(mode),
|
||||||
|
m_edition(context().editor()),
|
||||||
m_completer(context()),
|
m_completer(context()),
|
||||||
m_idle_timer{Clock::now() + idle_timeout,
|
m_idle_timer{Clock::now() + idle_timeout,
|
||||||
[this](Timer& timer) {
|
[this](Timer& timer) {
|
||||||
|
@ -861,6 +862,7 @@ public:
|
||||||
last_insert().first = mode;
|
last_insert().first = mode;
|
||||||
last_insert().second.clear();
|
last_insert().second.clear();
|
||||||
context().hooks().run_hook("InsertBegin", "", context());
|
context().hooks().run_hook("InsertBegin", "", context());
|
||||||
|
prepare(m_insert_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key) override
|
||||||
|
@ -869,7 +871,7 @@ public:
|
||||||
if (m_mode == Mode::InsertReg)
|
if (m_mode == Mode::InsertReg)
|
||||||
{
|
{
|
||||||
if (key.modifiers == Key::Modifiers::None)
|
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;
|
m_mode = Mode::Default;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -896,38 +898,35 @@ public:
|
||||||
reset_normal_mode();
|
reset_normal_mode();
|
||||||
}
|
}
|
||||||
else if (key == Key::Backspace)
|
else if (key == Key::Backspace)
|
||||||
m_inserter.erase();
|
erase();
|
||||||
else if (key == Key::Left)
|
else if (key == Key::Left)
|
||||||
{
|
{
|
||||||
m_inserter.move_cursors(-1_char);
|
m_edition.editor().move_selections(-1_char, SelectMode::Replace);
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
else if (key == Key::Right)
|
else if (key == Key::Right)
|
||||||
{
|
{
|
||||||
m_inserter.move_cursors(1_char);
|
m_edition.editor().move_selections(1_char, SelectMode::Replace);
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
else if (key == Key::Up)
|
else if (key == Key::Up)
|
||||||
{
|
{
|
||||||
m_inserter.move_cursors(-1_line);
|
m_edition.editor().move_selections(-1_line, SelectMode::Replace);
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
else if (key == Key::Down)
|
else if (key == Key::Down)
|
||||||
{
|
{
|
||||||
m_inserter.move_cursors(1_line);
|
m_edition.editor().move_selections(1_line, SelectMode::Replace);
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
else if (key.modifiers == Key::Modifiers::None)
|
else if (key.modifiers == Key::Modifiers::None)
|
||||||
{
|
insert(key.key);
|
||||||
m_inserter.insert(codepoint_to_str(key.key));
|
|
||||||
context().hooks().run_hook("InsertKey", key_to_str(key), context());
|
|
||||||
}
|
|
||||||
else if (key == ctrl('r'))
|
else if (key == ctrl('r'))
|
||||||
m_mode = Mode::InsertReg;
|
m_mode = Mode::InsertReg;
|
||||||
else if ( key == ctrl('m'))
|
else if ( key == ctrl('m'))
|
||||||
m_inserter.insert(String() + '\n');
|
insert('\n');
|
||||||
else if ( key == ctrl('i'))
|
else if ( key == ctrl('i'))
|
||||||
m_inserter.insert(String() + '\t');
|
insert('\t');
|
||||||
else if ( key == ctrl('n'))
|
else if ( key == ctrl('n'))
|
||||||
{
|
{
|
||||||
m_completer.select(1);
|
m_completer.select(1);
|
||||||
|
@ -957,11 +956,131 @@ public:
|
||||||
KeymapMode keymap_mode() const override { return KeymapMode::Insert; }
|
KeymapMode keymap_mode() const override { return KeymapMode::Insert; }
|
||||||
|
|
||||||
private:
|
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<String> 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 };
|
enum class Mode { Default, Complete, InsertReg };
|
||||||
Mode m_mode = Mode::Default;
|
Mode m_mode = Mode::Default;
|
||||||
IncrementalInserter m_inserter;
|
InsertMode m_insert_mode;
|
||||||
BufferCompleter m_completer;
|
scoped_edition m_edition;
|
||||||
Timer m_idle_timer;
|
BufferCompleter m_completer;
|
||||||
|
Timer m_idle_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
130
src/editor.cc
130
src/editor.cc
|
@ -20,7 +20,7 @@ Editor::Editor(Buffer& buffer)
|
||||||
m_main_sel = 0;
|
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 column = coord.column;
|
||||||
const auto& line = buffer[coord.line];
|
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);
|
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.first());
|
||||||
avoid_eol(buffer, sel.last());
|
avoid_eol(buffer, sel.last());
|
||||||
|
@ -450,130 +450,4 @@ void Editor::end_edition()
|
||||||
--m_edition_level;
|
--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<String> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace InputModes { class Insert; }
|
||||||
|
|
||||||
class Register;
|
class Register;
|
||||||
|
|
||||||
enum class SelectMode
|
enum class SelectMode
|
||||||
|
@ -88,6 +90,7 @@ public:
|
||||||
bool is_editing() const { return m_edition_level!= 0; }
|
bool is_editing() const { return m_edition_level!= 0; }
|
||||||
private:
|
private:
|
||||||
friend struct scoped_edition;
|
friend struct scoped_edition;
|
||||||
|
friend class InputModes::Insert;
|
||||||
void begin_edition();
|
void begin_edition();
|
||||||
void end_edition();
|
void end_edition();
|
||||||
|
|
||||||
|
@ -98,8 +101,6 @@ private:
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
|
||||||
friend class IncrementalInserter;
|
|
||||||
|
|
||||||
safe_ptr<Buffer> m_buffer;
|
safe_ptr<Buffer> m_buffer;
|
||||||
DynamicSelectionList m_selections;
|
DynamicSelectionList m_selections;
|
||||||
size_t m_main_sel;
|
size_t m_main_sel;
|
||||||
|
@ -114,31 +115,15 @@ struct scoped_edition
|
||||||
|
|
||||||
~scoped_edition()
|
~scoped_edition()
|
||||||
{ m_editor.end_edition(); }
|
{ m_editor.end_edition(); }
|
||||||
|
|
||||||
|
Editor& editor() const { return m_editor; }
|
||||||
private:
|
private:
|
||||||
Editor& m_editor;
|
Editor& m_editor;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An IncrementalInserter manage insert mode
|
void avoid_eol(const Buffer& buffer, BufferCoord& coord);
|
||||||
class IncrementalInserter
|
void avoid_eol(const Buffer& buffer, Range& sel);
|
||||||
{
|
void sort_and_merge_overlapping(SelectionList& selections, size_t& main_selection);
|
||||||
public:
|
|
||||||
IncrementalInserter(Editor& editor, InsertMode mode = InsertMode::Insert);
|
|
||||||
~IncrementalInserter();
|
|
||||||
|
|
||||||
void insert(String content);
|
|
||||||
void insert(memoryview<String> 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,30 +92,6 @@ void test_editor()
|
||||||
kak_assert(not buffer.is_end(editor.main_selection().first()));
|
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()
|
void test_utf8()
|
||||||
{
|
{
|
||||||
String str = "maïs mélange bientôt";
|
String str = "maïs mélange bientôt";
|
||||||
|
@ -171,5 +147,4 @@ void run_unit_tests()
|
||||||
test_buffer();
|
test_buffer();
|
||||||
test_undo_group_optimizer();
|
test_undo_group_optimizer();
|
||||||
test_editor();
|
test_editor();
|
||||||
test_incremental_inserter();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user