summaryrefslogtreecommitdiff
path: root/src/context.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/context.cc')
-rw-r--r--src/context.cc186
1 files changed, 170 insertions, 16 deletions
diff --git a/src/context.cc b/src/context.cc
index 3970a6ca..fe9bd346 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -16,11 +16,11 @@ Context::Context(InputHandler& input_handler, SelectionList selections,
Flags flags, String name)
: m_flags(flags),
m_input_handler{&input_handler},
- m_selections{std::move(selections)},
+ m_selection_history{*this, std::move(selections)},
m_name(std::move(name))
{}
-Context::Context(EmptyContextFlag) {}
+Context::Context(EmptyContextFlag) : m_selection_history{*this} {}
Buffer& Context::buffer() const
{
@@ -164,7 +164,147 @@ void JumpList::forget_buffer(Buffer& buffer)
}
}
-void Context::change_buffer(Buffer& buffer)
+Context::SelectionHistory::SelectionHistory(Context& context) : m_context(context) {}
+
+Context::SelectionHistory::SelectionHistory(Context& context, SelectionList selections)
+ : m_context(context),
+ m_history{HistoryNode{std::move(selections), HistoryId::Invalid}},
+ m_history_id(HistoryId::First) {}
+
+void Context::SelectionHistory::initialize(SelectionList selections)
+{
+ kak_assert(empty());
+ m_history = {HistoryNode{std::move(selections), HistoryId::Invalid}};
+ m_history_id = HistoryId::First;
+}
+
+SelectionList& Context::SelectionHistory::selections(bool update)
+{
+ if (empty())
+ throw runtime_error("no selections in context");
+ auto& sels = m_staging ? m_staging->selections : current_history_node().selections;
+ if (update)
+ sels.update();
+ return sels;
+}
+
+void Context::SelectionHistory::begin_edition()
+{
+ if (not in_edition())
+ m_staging = HistoryNode{selections(), m_history_id};
+ m_in_edition.set();
+}
+
+void Context::SelectionHistory::end_edition()
+{
+ kak_assert(in_edition());
+ m_in_edition.unset();
+ if (in_edition())
+ return;
+
+ if (m_history_id != HistoryId::Invalid and current_history_node().selections == m_staging->selections)
+ {
+ auto& sels = m_history[(size_t)m_history_id].selections;
+ sels.force_timestamp(m_staging->selections.timestamp());
+ sels.set_main_index(m_staging->selections.main_index());
+ }
+ else
+ {
+ m_history_id = next_history_id();
+ m_history.push_back(std::move(*m_staging));
+ }
+ m_staging.reset();
+}
+
+void Context::SelectionHistory::undo()
+{
+ if (in_edition())
+ throw runtime_error("selection undo is only supported at top-level");
+ kak_assert(not empty());
+ begin_edition();
+ auto end = on_scope_end([&] {
+ kak_assert(current_history_node().selections == m_staging->selections);
+ end_edition();
+ });
+ HistoryId parent = current_history_node().parent;
+ if (parent == HistoryId::Invalid)
+ throw runtime_error("no selection change to undo");
+ auto select_parent = [&, parent] {
+ HistoryId before_undo = m_history_id;
+ m_history_id = parent;
+ current_history_node().redo_child = before_undo;
+ m_staging = current_history_node();
+ };
+ if (&history_node(parent).selections.buffer() == &m_context.buffer())
+ select_parent();
+ else
+ m_context.change_buffer(history_node(parent).selections.buffer(), { std::move(select_parent) });
+ // });
+}
+
+void Context::SelectionHistory::redo()
+{
+ if (in_edition())
+ throw runtime_error("selection redo is only supported at top-level");
+ kak_assert(not empty());
+ begin_edition();
+ auto end = on_scope_end([&] {
+ kak_assert(current_history_node().selections == m_staging->selections);
+ end_edition();
+ });
+ HistoryId child = current_history_node().redo_child;
+ if (child == HistoryId::Invalid)
+ throw runtime_error("no selection change to redo");
+ auto select_child = [&, child] {
+ m_history_id = child;
+ m_staging = current_history_node();
+ };
+ if (&history_node(child).selections.buffer() == &m_context.buffer())
+ select_child();
+ else
+ m_context.change_buffer(history_node(child).selections.buffer(), { std::move(select_child) });
+}
+
+void Context::SelectionHistory::forget_buffer(Buffer& buffer)
+{
+ Vector<HistoryId, MemoryDomain::Selections> new_ids;
+ size_t bias = 0;
+ for (size_t i = 0; i < m_history.size(); ++i)
+ {
+ auto& node = history_node((HistoryId)i);
+ HistoryId id;
+ if (&node.selections.buffer() == &buffer)
+ {
+ id = HistoryId::Invalid;
+ ++bias;
+ }
+ else
+ id = (HistoryId)(i - bias);
+ new_ids.push_back(id);
+ }
+ auto new_id = [&new_ids](HistoryId old_id) -> HistoryId {
+ return old_id == HistoryId::Invalid ? HistoryId::Invalid : new_ids[(size_t)old_id];
+ };
+
+ m_history.erase(remove_if(m_history, [&buffer](const auto& node) {
+ return &node.selections.buffer() == &buffer;
+ }), m_history.end());
+
+ for (auto& node : m_history)
+ {
+ node.parent = new_id(node.parent);
+ node.redo_child = new_id(node.redo_child);
+ }
+ m_history_id = new_id(m_history_id);
+ if (m_staging)
+ {
+ m_staging->parent = new_id(m_staging->parent);
+ kak_assert(m_staging->redo_child == HistoryId::Invalid);
+ }
+ kak_assert(m_history_id != HistoryId::Invalid or m_staging);
+}
+
+void Context::change_buffer(Buffer& buffer, Optional<FunctionRef<void()>> set_selections)
{
if (has_buffer() and &buffer == &this->buffer())
return;
@@ -176,12 +316,18 @@ void Context::change_buffer(Buffer& buffer)
{
client().info_hide();
client().menu_hide();
- client().change_buffer(buffer);
+ client().change_buffer(buffer, std::move(set_selections));
}
else
{
m_window.reset();
- m_selections = SelectionList{buffer, Selection{}};
+ if (m_selection_history.empty())
+ m_selection_history.initialize(SelectionList{buffer, Selection{}});
+ else
+ {
+ ScopedSelectionEdition selection_edition{*this};
+ selections_write_only() = SelectionList{buffer, Selection{}};
+ }
}
if (has_input_handler())
@@ -192,14 +338,16 @@ void Context::forget_buffer(Buffer& buffer)
{
m_jump_list.forget_buffer(buffer);
- if (&this->buffer() != &buffer)
- return;
+ if (&this->buffer() == &buffer)
+ {
+ if (is_editing() && has_input_handler())
+ input_handler().reset_normal_mode();
- if (is_editing() && has_input_handler())
- input_handler().reset_normal_mode();
+ auto last_buffer = this->last_buffer();
+ change_buffer(last_buffer ? *last_buffer : BufferManager::instance().get_first_buffer());
+ }
- auto last_buffer = this->last_buffer();
- change_buffer(last_buffer ? *last_buffer : BufferManager::instance().get_first_buffer());
+ m_selection_history.forget_buffer(buffer);
}
Buffer* Context::last_buffer() const
@@ -225,11 +373,17 @@ Buffer* Context::last_buffer() const
SelectionList& Context::selections(bool update)
{
- if (not m_selections)
- throw runtime_error("no selections in context");
- if (update)
- (*m_selections).update();
- return *m_selections;
+ return m_selection_history.selections(update);
+}
+
+void Context::undo_selection_change()
+{
+ m_selection_history.undo();
+}
+
+void Context::redo_selection_change()
+{
+ m_selection_history.redo();
}
SelectionList& Context::selections_write_only()