Editor refactoring, merge undo and batch management
This commit is contained in:
parent
333e87dedd
commit
04a37d8895
|
@ -6,24 +6,9 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct scoped_undo_group
|
|
||||||
{
|
|
||||||
scoped_undo_group(Buffer& buffer)
|
|
||||||
: m_buffer(buffer) { m_buffer.begin_undo_group(); }
|
|
||||||
|
|
||||||
~scoped_undo_group() { m_buffer.end_undo_group(); }
|
|
||||||
private:
|
|
||||||
Buffer& m_buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Editor::Editor(Buffer& buffer)
|
Editor::Editor(Buffer& buffer)
|
||||||
: m_buffer(buffer),
|
: m_buffer(buffer),
|
||||||
m_batch_level(0)
|
m_edition_level(0)
|
||||||
{
|
{
|
||||||
m_selections.push_back(SelectionList());
|
m_selections.push_back(SelectionList());
|
||||||
selections().push_back(Selection(buffer.begin(), buffer.begin()));
|
selections().push_back(Selection(buffer.begin(), buffer.begin()));
|
||||||
|
@ -31,69 +16,30 @@ Editor::Editor(Buffer& buffer)
|
||||||
|
|
||||||
void Editor::erase()
|
void Editor::erase()
|
||||||
{
|
{
|
||||||
if (not is_in_batch())
|
scoped_edition edition(*this);
|
||||||
{
|
|
||||||
scoped_undo_group undo_group(m_buffer);
|
|
||||||
erase_noundo();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
erase_noundo();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::erase_noundo()
|
|
||||||
{
|
|
||||||
check_invariant();
|
|
||||||
for (auto& sel : selections())
|
for (auto& sel : selections())
|
||||||
m_buffer.modify(Modification::make_erase(sel.begin(), sel.end()));
|
m_buffer.modify(Modification::make_erase(sel.begin(), sel.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::insert(const String& string)
|
void Editor::insert(const String& string)
|
||||||
{
|
{
|
||||||
if (not is_in_batch())
|
scoped_edition edition(*this);
|
||||||
{
|
|
||||||
scoped_undo_group undo_group(m_buffer);
|
|
||||||
insert_noundo(string);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
insert_noundo(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::insert_noundo(const String& string)
|
|
||||||
{
|
|
||||||
for (auto& sel : selections())
|
for (auto& sel : selections())
|
||||||
m_buffer.modify(Modification::make_insert(sel.begin(), string));
|
m_buffer.modify(Modification::make_insert(sel.begin(), string));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::append(const String& string)
|
void Editor::append(const String& string)
|
||||||
{
|
{
|
||||||
if (not is_in_batch())
|
scoped_edition edition(*this);
|
||||||
{
|
|
||||||
scoped_undo_group undo_group(m_buffer);
|
|
||||||
append_noundo(string);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
append_noundo(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::append_noundo(const String& string)
|
|
||||||
{
|
|
||||||
for (auto& sel : selections())
|
for (auto& sel : selections())
|
||||||
m_buffer.modify(Modification::make_insert(sel.end(), string));
|
m_buffer.modify(Modification::make_insert(sel.end(), string));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::replace(const std::string& string)
|
void Editor::replace(const std::string& string)
|
||||||
{
|
{
|
||||||
if (not is_in_batch())
|
scoped_edition edition(*this);
|
||||||
{
|
erase();
|
||||||
scoped_undo_group undo_group(m_buffer);
|
insert(string);
|
||||||
erase_noundo();
|
|
||||||
insert_noundo(string);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
erase_noundo();
|
|
||||||
insert_noundo(string);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::push_selections()
|
void Editor::push_selections()
|
||||||
|
@ -236,35 +182,30 @@ CandidateList Editor::complete_filterid(const std::string& prefix,
|
||||||
return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
|
return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::begin_batch()
|
void Editor::begin_edition()
|
||||||
{
|
{
|
||||||
++m_batch_level;
|
++m_edition_level;
|
||||||
|
|
||||||
if (m_batch_level == 1)
|
if (m_edition_level == 1)
|
||||||
{
|
|
||||||
m_buffer.begin_undo_group();
|
m_buffer.begin_undo_group();
|
||||||
on_begin_batch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::end_batch()
|
void Editor::end_edition()
|
||||||
{
|
{
|
||||||
assert(m_batch_level > 0);
|
assert(m_edition_level > 0);
|
||||||
if (m_batch_level == 1)
|
if (m_edition_level == 1)
|
||||||
{
|
|
||||||
on_end_batch();
|
|
||||||
m_buffer.end_undo_group();
|
m_buffer.end_undo_group();
|
||||||
}
|
|
||||||
--m_batch_level;
|
--m_edition_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
IncrementalInserter::IncrementalInserter(Editor& editor, Mode mode)
|
IncrementalInserter::IncrementalInserter(Editor& editor, Mode mode)
|
||||||
: m_editor(editor)
|
: m_editor(editor), m_edition(editor)
|
||||||
{
|
{
|
||||||
m_editor.begin_batch();
|
m_editor.on_incremental_insertion_begin();
|
||||||
|
|
||||||
if (mode == Mode::Change)
|
if (mode == Mode::Change)
|
||||||
editor.erase_noundo();
|
editor.erase();
|
||||||
|
|
||||||
for (auto& sel : m_editor.selections())
|
for (auto& sel : m_editor.selections())
|
||||||
{
|
{
|
||||||
|
@ -297,7 +238,7 @@ IncrementalInserter::IncrementalInserter(Editor& editor, Mode mode)
|
||||||
IncrementalInserter::~IncrementalInserter()
|
IncrementalInserter::~IncrementalInserter()
|
||||||
{
|
{
|
||||||
move_cursors(BufferCoord(0, -1));
|
move_cursors(BufferCoord(0, -1));
|
||||||
m_editor.end_batch();
|
m_editor.on_incremental_insertion_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncrementalInserter::apply(Modification&& modification) const
|
void IncrementalInserter::apply(Modification&& modification) const
|
||||||
|
|
|
@ -55,31 +55,40 @@ public:
|
||||||
CandidateList complete_filterid(const std::string& prefix,
|
CandidateList complete_filterid(const std::string& prefix,
|
||||||
size_t cursor_pos = std::string::npos);
|
size_t cursor_pos = std::string::npos);
|
||||||
|
|
||||||
void begin_batch();
|
bool is_editing() const { return m_edition_level!= 0; }
|
||||||
void end_batch();
|
|
||||||
bool is_in_batch() const { return m_batch_level != 0; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void erase_noundo();
|
friend class scoped_edition;
|
||||||
void insert_noundo(const String& string);
|
void begin_edition();
|
||||||
void append_noundo(const String& string);
|
void end_edition();
|
||||||
|
|
||||||
|
int m_edition_level;
|
||||||
|
|
||||||
SelectionList& selections() { return m_selections.back(); }
|
SelectionList& selections() { return m_selections.back(); }
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
|
||||||
friend class IncrementalInserter;
|
friend class IncrementalInserter;
|
||||||
int m_batch_level;
|
virtual void on_incremental_insertion_begin() {}
|
||||||
|
virtual void on_incremental_insertion_end() {}
|
||||||
virtual void on_begin_batch() {}
|
|
||||||
virtual void on_end_batch() {}
|
|
||||||
|
|
||||||
|
|
||||||
Buffer& m_buffer;
|
Buffer& m_buffer;
|
||||||
std::vector<SelectionList> m_selections;
|
std::vector<SelectionList> m_selections;
|
||||||
idvaluemap<std::string, FilterFunc> m_filters;
|
idvaluemap<std::string, FilterFunc> m_filters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct scoped_edition
|
||||||
|
{
|
||||||
|
scoped_edition(Editor& editor)
|
||||||
|
: m_editor(editor)
|
||||||
|
{ m_editor.begin_edition(); }
|
||||||
|
|
||||||
|
~scoped_edition()
|
||||||
|
{ m_editor.end_edition(); }
|
||||||
|
private:
|
||||||
|
Editor& m_editor;
|
||||||
|
};
|
||||||
|
|
||||||
// An IncrementalInserter manage insert mode
|
// An IncrementalInserter manage insert mode
|
||||||
class IncrementalInserter
|
class IncrementalInserter
|
||||||
{
|
{
|
||||||
|
@ -109,6 +118,7 @@ private:
|
||||||
void apply(Modification&& modification) const;
|
void apply(Modification&& modification) const;
|
||||||
|
|
||||||
Editor& m_editor;
|
Editor& m_editor;
|
||||||
|
scoped_edition m_edition;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1054,8 +1054,8 @@ void exec_keys(const KeyList& keys,
|
||||||
Editor& editor = context.has_window() ? static_cast<Editor&>(context.window())
|
Editor& editor = context.has_window() ? static_cast<Editor&>(context.window())
|
||||||
: static_cast<Editor&>(batch_editor);
|
: static_cast<Editor&>(batch_editor);
|
||||||
|
|
||||||
editor.begin_batch();
|
|
||||||
auto end_batch = on_scope_end([&]() { editor.end_batch(); });
|
scoped_edition edition(editor);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while(pos < keys.size())
|
while(pos < keys.size())
|
||||||
|
|
|
@ -154,12 +154,12 @@ std::string Window::status_line() const
|
||||||
oss << " [+]";
|
oss << " [+]";
|
||||||
oss << " -- " << cursor.line+1 << "," << cursor.column+1
|
oss << " -- " << cursor.line+1 << "," << cursor.column+1
|
||||||
<< " -- " << selections().size() << " sel -- ";
|
<< " -- " << selections().size() << " sel -- ";
|
||||||
if (is_in_batch())
|
if (is_editing())
|
||||||
oss << "[Insert]";
|
oss << "[Insert]";
|
||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::on_end_batch()
|
void Window::on_incremental_insertion_end()
|
||||||
{
|
{
|
||||||
push_selections();
|
push_selections();
|
||||||
hooks_manager().run_hook("InsertEnd", "", Context(*this));
|
hooks_manager().run_hook("InsertEnd", "", Context(*this));
|
||||||
|
|
|
@ -50,7 +50,7 @@ private:
|
||||||
Window(Buffer& buffer);
|
Window(Buffer& buffer);
|
||||||
Window(const Window&) = delete;
|
Window(const Window&) = delete;
|
||||||
|
|
||||||
void on_end_batch();
|
void on_incremental_insertion_end();
|
||||||
|
|
||||||
void scroll_to_keep_cursor_visible_ifn();
|
void scroll_to_keep_cursor_visible_ifn();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user