summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.asciidoc2
-rw-r--r--doc/pages/expansions.asciidoc16
-rw-r--r--doc/pages/keys.asciidoc6
-rw-r--r--src/buffer.cc181
-rw-r--r--src/buffer.hh23
-rw-r--r--src/buffer_utils.cc10
-rw-r--r--src/normal.cc25
-rw-r--r--test/compose/history/kak_quoted_history2
-rw-r--r--test/compose/history/rc2
9 files changed, 81 insertions, 186 deletions
diff --git a/README.asciidoc b/README.asciidoc
index 561be22a..93b6d93e 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -540,9 +540,7 @@ Changes
* `<a-!>`: append command output after each selection
* `u`: undo last change
- * `<a-u>`: move backward in history
* `U`: redo last change
- * `<a-U>`: move forward in history
* `&`: align selections, align the cursor of selections by inserting
spaces before the first character of the selection
diff --git a/doc/pages/expansions.asciidoc b/doc/pages/expansions.asciidoc
index 81ccd7b7..191e588b 100644
--- a/doc/pages/expansions.asciidoc
+++ b/doc/pages/expansions.asciidoc
@@ -285,15 +285,13 @@ The following expansions are supported (with required context _in italics_):
*%val{history}*::
_in buffer, window scope_ +
- the full change history of the buffer, including undo forks, in terms
- of `parent committed redo_child modification0 modification1 ...`
- entries, where _parent_ is the index of the entry's predecessor (entry
- 0, which is the root of the history tree, will always have `-` here),
- _committed_ is a count in seconds from Kakoune's steady clock's epoch
- of the creation of the history entry, _redo_child_ is the index of the
- child which will be visited for `U` (always `-` at the leaves of the
- history), and each _modification_ is presented as in
- `%val{uncommitted_modifications}`.
+ the full change history of the buffer, as a list of
+ `committed modification0 modification1 ...` entries.
+ _committed_ is a count in seconds from Kakoune's steady clock's epoch of
+ the creation of the history entry, and each _modification_ is presented
+ as in `%val{uncommitted_modifications}`.
+ Boundaries in the list can be identified knowing that _committed_ is a
+ plain integer, and each _modification_ starts with `+` or `-`.
*%val{history_id}*::
_in buffer, window scope_ +
diff --git a/doc/pages/keys.asciidoc b/doc/pages/keys.asciidoc
index 060eebd7..2b2645aa 100644
--- a/doc/pages/keys.asciidoc
+++ b/doc/pages/keys.asciidoc
@@ -305,15 +305,9 @@ Yanking (copying) and pasting use the *"* register by default (See <<registers#,
*u*::
undo last change
-*<a-u>*::
- move backward in history
-
*U*::
redo last change
-*<a-U>*::
- move forward in history
-
*<c-h>*::
undo last selection change
diff --git a/src/buffer.cc b/src/buffer.cc
index c968690f..8baa7423 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -20,8 +20,8 @@
namespace Kakoune
{
-Buffer::HistoryNode::HistoryNode(HistoryId parent)
- : parent{parent}, committed{Clock::now()}
+Buffer::HistoryNode::HistoryNode()
+ : committed{Clock::now()}
{}
Buffer::Buffer(String name, Flags flags, BufferLines lines,
@@ -31,9 +31,9 @@ Buffer::Buffer(String name, Flags flags, BufferLines lines,
m_name{(flags & Flags::File) ? real_path(parse_filename(name)) : std::move(name)},
m_display_name{(flags & Flags::File) ? compact_path(m_name) : m_name},
m_flags{flags | Flags::NoUndo},
- m_history{{HistoryId::Invalid}},
- m_history_id{HistoryId::First},
- m_last_save_history_id{HistoryId::First},
+ m_history{{HistoryNode{}}},
+ m_history_id{0},
+ m_last_save_history_id{0},
m_fs_status{fs_status}
{
#ifdef KAK_DEBUG
@@ -114,7 +114,7 @@ bool Buffer::set_name(String name)
if (m_flags & Buffer::Flags::File and not file_exists(m_name))
{
m_flags |= Buffer::Flags::New;
- m_last_save_history_id = HistoryId::Invalid;
+ m_last_save_history_id = InvalidHistoryId;
}
}
else
@@ -201,9 +201,9 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F
if (not record_undo)
{
// Erase history about to be invalidated history
- m_history_id = HistoryId::First;
- m_last_save_history_id = HistoryId::First;
- m_history = {HistoryNode{HistoryId::Invalid}};
+ m_history_id = 0;
+ m_last_save_history_id = 0;
+ m_history = {HistoryNode{}};
m_changes.push_back({ Change::Erase, {0,0}, line_count() });
static_cast<BufferLines&>(m_lines) = std::move(lines);
@@ -273,12 +273,20 @@ void Buffer::commit_undo_group()
if (m_current_undo_group.empty())
return;
- const HistoryId id = next_history_id();
- m_history.push_back({m_history_id});
+ // walk back to current position in history, and append reverse events
+ for (size_t current = m_history.size() - 1; current != m_history_id ; --current)
+ {
+ m_history.emplace_back();
+ m_history.back().undo_group = m_history[current].undo_group
+ | reverse()
+ | transform([](auto& modif) { return modif.inverse(); })
+ | gather<UndoGroup>();
+ }
+ m_history.emplace_back();
m_history.back().undo_group = std::move(m_current_undo_group);
+
m_current_undo_group.clear();
- current_history_node().redo_child = id;
- m_history_id = id;
+ m_history_id = m_history.size() - 1;
}
bool Buffer::undo(size_t count)
@@ -287,15 +295,15 @@ bool Buffer::undo(size_t count)
commit_undo_group();
- if (current_history_node().parent == HistoryId::Invalid)
+ if ((m_history_id - 1) == InvalidHistoryId)
return false;
- while (count-- != 0 and current_history_node().parent != HistoryId::Invalid)
+ while (count-- != 0 and (m_history_id - 1) != InvalidHistoryId)
{
for (const Modification& modification : current_history_node().undo_group | reverse())
apply_modification(modification.inverse());
- m_history_id = current_history_node().parent;
+ m_history_id--;
}
return true;
@@ -305,79 +313,19 @@ bool Buffer::redo(size_t count)
{
throw_if_read_only();
- if (current_history_node().redo_child == HistoryId::Invalid)
+ if (m_history_id == (m_history.size() - 1))
return false;
kak_assert(m_current_undo_group.empty());
- while (count-- != 0 and current_history_node().redo_child != HistoryId::Invalid)
+ while (count-- != 0 and m_history_id != (m_history.size() - 1))
{
- m_history_id = current_history_node().redo_child;
+ m_history_id++;
for (const Modification& modification : current_history_node().undo_group)
apply_modification(modification);
}
- return true;
-}
-
-bool Buffer::move_to(HistoryId id)
-{
- if (id >= next_history_id())
- return false;
-
- throw_if_read_only();
-
- commit_undo_group();
-
- auto find_lowest_common_parent = [this](HistoryId a, HistoryId b) {
- auto depth_of = [this](HistoryId id) {
- size_t depth = 0;
- for (; history_node(id).parent != HistoryId::Invalid; id = history_node(id).parent)
- ++depth;
- return depth;
- };
- auto depthA = depth_of(a), depthB = depth_of(b);
-
- for (; depthA > depthB; --depthA)
- a = history_node(a).parent;
- for (; depthB > depthA; --depthB)
- b = history_node(b).parent;
-
- while (a != b)
- {
- a = history_node(a).parent;
- b = history_node(b).parent;
- }
-
- kak_assert(a == b and a != HistoryId::Invalid);
- return a;
- };
-
- auto parent = find_lowest_common_parent(m_history_id, id);
-
- // undo up to common parent
- for (auto id = m_history_id; id != parent; id = history_node(id).parent)
- {
- for (const Modification& modification : history_node(id).undo_group | reverse())
- apply_modification(modification.inverse());
- }
-
- static void (*apply_from_parent)(Buffer&, HistoryId, HistoryId) =
- [](Buffer& buffer, HistoryId parent, HistoryId id) {
- if (id == parent)
- return;
- auto& node = buffer.history_node(id);
- apply_from_parent(buffer, parent, node.parent);
-
- buffer.history_node(node.parent).redo_child = id;
-
- for (const Modification& modification : node.undo_group)
- buffer.apply_modification(modification);
- };
-
- apply_from_parent(*this, parent, id);
- m_history_id = id;
return true;
}
@@ -646,7 +594,7 @@ void Buffer::run_hook_in_own_context(Hook hook, StringView param, String client_
Optional<BufferCoord> Buffer::last_modification_coord() const
{
- if (m_history_id == HistoryId::First)
+ if (m_history_id == 0)
return {};
return current_history_node().undo_group.back().coord;
}
@@ -734,50 +682,49 @@ UnitTest test_undo{[]()
buffer.insert(2_line, "tchou\n"); // change 3
buffer.commit_undo_group();
buffer.undo();
- buffer.insert(2_line, "mutch\n"); // change 4
+ buffer.insert(2_line, "mutch\n"); // change 4 (inverse of 3) and 5
buffer.commit_undo_group();
- buffer.erase({2, 1}, {2, 5}); // change 5
+ buffer.erase({2, 1}, {2, 5}); // change 6
buffer.commit_undo_group();
buffer.undo(2);
buffer.redo(2);
buffer.undo();
- buffer.replace(2_line, buffer.end_coord(), "foo"); // change 6
+ buffer.replace(2_line, buffer.end_coord(), "foo"); // change 7 (inverse of 6) and 8
buffer.commit_undo_group();
- kak_assert((int)buffer.line_count() == 3);
- kak_assert(buffer[0_line] == "allo ?\n");
- kak_assert(buffer[1_line] == "mais que fais la police\n");
- kak_assert(buffer[2_line] == "foo\n");
-
- buffer.move_to((Buffer::HistoryId)3);
- kak_assert((int)buffer.line_count() == 5);
- kak_assert(buffer[0_line] == "allo ?\n");
- kak_assert(buffer[1_line] == "mais que fais la police\n");
- kak_assert(buffer[2_line] == "tchou\n");
- kak_assert(buffer[3_line] == " hein ?\n");
- kak_assert(buffer[4_line] == " youpi\n");
-
- buffer.move_to((Buffer::HistoryId)4);
- kak_assert((int)buffer.line_count() == 5);
- kak_assert(buffer[0_line] == "allo ?\n");
- kak_assert(buffer[1_line] == "mais que fais la police\n");
- kak_assert(buffer[2_line] == "mutch\n");
- kak_assert(buffer[3_line] == " hein ?\n");
- kak_assert(buffer[4_line] == " youpi\n");
-
- buffer.move_to(Buffer::HistoryId::First);
- kak_assert((int)buffer.line_count() == 4);
- kak_assert(buffer[0_line] == "allo ?\n");
- kak_assert(buffer[1_line] == "mais que fais la police\n");
- kak_assert(buffer[2_line] == " hein ?\n");
- kak_assert(buffer[3_line] == " youpi\n");
- kak_assert(not buffer.undo());
-
- buffer.move_to((Buffer::HistoryId)5);
- kak_assert(not buffer.redo());
-
- buffer.move_to((Buffer::HistoryId)6);
- kak_assert(not buffer.redo());
+ kak_assert(buffer.history().size() == 9);
+
+ auto check_content = [&](const Vector<StringView>& lines) {
+ kak_assert(buffer.line_count() == lines.size());
+ for (size_t i = 0; i < lines.size(); ++i)
+ kak_assert(buffer[i] == lines[i] + "\n");
+ };
+
+ kak_assert(not buffer.redo(1));
+ check_content({ "allo ?", "mais que fais la police", "foo" , });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", "mutch" , " hein ?", " youpi", });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", "m" , " hein ?", " youpi", });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", "mutch" , " hein ?", " youpi", });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", "tchou" , " hein ?", " youpi", });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , "kanaky", });
+ kak_assert(buffer.undo(1));
+ check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , });
+ kak_assert(not buffer.undo(1));
+
+ kak_assert(buffer.redo(1000));
+ check_content({ "allo ?", "mais que fais la police", "foo" , });
+
+ kak_assert(buffer.undo(1000));
+ check_content({ "allo ?", "mais que fais la police", " hein ?", " youpi" , });
}};
}
diff --git a/src/buffer.hh b/src/buffer.hh
index 7b671c0e..415d32da 100644
--- a/src/buffer.hh
+++ b/src/buffer.hh
@@ -123,8 +123,6 @@ public:
};
friend constexpr bool with_bit_ops(Meta::Type<Flags>) { return true; }
- enum class HistoryId : size_t { First = 0, Invalid = (size_t)-1 };
-
Buffer(String name, Flags flags, BufferLines lines,
ByteOrderMark bom = ByteOrderMark::None,
EolFormat eolformat = EolFormat::Lf,
@@ -150,9 +148,7 @@ public:
void commit_undo_group();
bool undo(size_t count = 1);
bool redo(size_t count = 1);
- bool move_to(HistoryId id);
- HistoryId current_history_id() const noexcept { return m_history_id; }
- HistoryId next_history_id() const noexcept { return (HistoryId)m_history.size(); }
+ size_t current_history_id() const noexcept { return m_history_id; }
String string(BufferCoord begin, BufferCoord end) const;
StringView substr(BufferCoord begin, BufferCoord end) const;
@@ -245,14 +241,14 @@ public:
struct HistoryNode : UseMemoryDomain<MemoryDomain::BufferMeta>
{
- HistoryNode(HistoryId parent);
+ HistoryNode();
- HistoryId parent;
- HistoryId redo_child = HistoryId::Invalid;
TimePoint committed;
UndoGroup undo_group;
};
+ static constexpr size_t InvalidHistoryId = (size_t)-1;
+
const Vector<HistoryNode>& history() const { return m_history; }
const UndoGroup& current_undo_group() const { return m_current_undo_group; }
@@ -263,7 +259,6 @@ private:
BufferCoord do_erase(BufferCoord begin, BufferCoord end);
void apply_modification(const Modification& modification);
- void revert_modification(const Modification& modification);
struct LineList : BufferLines
{
@@ -289,14 +284,12 @@ private:
Flags m_flags;
Vector<HistoryNode> m_history;
- HistoryId m_history_id = HistoryId::Invalid;
- HistoryId m_last_save_history_id = HistoryId::Invalid;
+ size_t m_history_id = InvalidHistoryId;
+ size_t m_last_save_history_id = InvalidHistoryId;
UndoGroup m_current_undo_group;
- HistoryNode& history_node(HistoryId id) { return m_history[(size_t)id]; }
- const HistoryNode& history_node(HistoryId id) const { return m_history[(size_t)id]; }
- HistoryNode& current_history_node() { return m_history[(size_t)m_history_id]; }
- const HistoryNode& current_history_node() const { return m_history[(size_t)m_history_id]; }
+ HistoryNode& current_history_node() { return m_history[m_history_id]; }
+ const HistoryNode& current_history_node() const { return m_history[m_history_id]; }
Vector<Change, MemoryDomain::BufferMeta> m_changes;
diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc
index 47f9a88b..9ff1f4ce 100644
--- a/src/buffer_utils.cc
+++ b/src/buffer_utils.cc
@@ -301,14 +301,6 @@ void write_to_debug_buffer(StringView str)
}
-auto to_string(Buffer::HistoryId id)
-{
- using Result = decltype(to_string(size_t{}));
- if (id == Buffer::HistoryId::Invalid)
- return Result{1, "-"};
- return to_string(static_cast<size_t>(id));
-}
-
static String modification_as_string(const Buffer::Modification& modification)
{
return format("{}{}.{}|{}",
@@ -323,9 +315,7 @@ Vector<String> history_as_strings(const Vector<Buffer::HistoryNode>& history)
for (auto& node : history)
{
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(node.committed.time_since_epoch());
- res.push_back(to_string(node.parent));
res.push_back(to_string(seconds.count()));
- res.push_back(to_string(node.redo_child));
for (auto& modification : node.undo_group)
res.push_back(modification_as_string(modification));
};
diff --git a/src/normal.cc b/src/normal.cc
index 02cea4ef..e3220521 100644
--- a/src/normal.cc
+++ b/src/normal.cc
@@ -2010,29 +2010,6 @@ void redo(Context& context, NormalParams params)
}
template<Direction direction>
-void move_in_history(Context& context, NormalParams params)
-{
- Buffer& buffer = context.buffer();
- size_t timestamp = buffer.timestamp();
- const int count = std::max(1, params.count);
- const int history_id = (size_t)buffer.current_history_id() + direction * count;
- const int max_history_id = (int)buffer.next_history_id() - 1;
- if (buffer.move_to((Buffer::HistoryId)history_id))
- {
- auto ranges = compute_modified_ranges(buffer, timestamp);
- if (not ranges.empty())
- context.selections_write_only() = std::move(ranges);
-
- context.print_status({ format("moved to change #{} ({})",
- history_id, max_history_id),
- context.faces()["Information"] });
- }
- else
- throw runtime_error(format("no such change: #{} ({})",
- history_id, max_history_id));
-}
-
-template<Direction direction>
void undo_selection_change(Context& context, NormalParams params)
{
int count = std::max(1, params.count);
@@ -2362,8 +2339,6 @@ static constexpr HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend>
{ {'u'}, {"undo", undo} },
{ {'U'}, {"redo", redo} },
- { {alt('u')}, {"move backward in history", move_in_history<Direction::Backward>} },
- { {alt('U')}, {"move forward in history", move_in_history<Direction::Forward>} },
{ {ctrl('h')}, {"undo selection change", undo_selection_change<Backward>} },
{ {ctrl('k')}, {"redo selection change", undo_selection_change<Forward>} },
diff --git a/test/compose/history/kak_quoted_history b/test/compose/history/kak_quoted_history
index 5265a633..2e78a548 100644
--- a/test/compose/history/kak_quoted_history
+++ b/test/compose/history/kak_quoted_history
@@ -1 +1 @@
-'-' '$timestamp' '1' '0' '$timestamp' '-' '+0.5|m' '+0.6|i' '+0.7|d' '+0.8|d' '+0.9|l' '+0.10|e' '-0.8|dle'
+'$timestamp' '$timestamp' '+0.5|m' '+0.6|i' '+0.7|d' '+0.8|d' '+0.9|l' '+0.10|e' '-0.8|dle'
diff --git a/test/compose/history/rc b/test/compose/history/rc
index da4c3cd7..f8a37fda 100644
--- a/test/compose/history/rc
+++ b/test/compose/history/rc
@@ -1,6 +1,6 @@
# Make our expansion have a predictable timestamp
hook global ClientClose .* %{
nop %sh{
- kak -f 'ghf<space>lwc$timestamp<esc>3f<space>lwc$timestamp<esc>' kak_quoted_history
+ kak -f 'ghlwc$timestamp<esc>1f<space>lwc$timestamp<esc>' kak_quoted_history
}
}