summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Felice <jason.m.felice@gmail.com>2018-12-31 12:40:49 -0500
committerJason Felice <jason.m.felice@gmail.com>2020-01-01 19:47:29 -0500
commitb03b51d27ace58cc17eb8a3aec99fa20dd3209ab (patch)
treedc9fa1bcf2d259834f8f31cd4c3c8aac9b816892
parent62b4780e07e152e037e29823bfd7d911b9767f2e (diff)
Add 'history' and 'uncommitted_modifications' expansions
-rw-r--r--doc/pages/expansions.asciidoc21
-rw-r--r--src/buffer.cc18
-rw-r--r--src/buffer.hh42
-rw-r--r--src/buffer_utils.cc43
-rw-r--r--src/buffer_utils.hh5
-rw-r--r--src/main.cc8
-rw-r--r--test/compose/history/cmd1
-rw-r--r--test/compose/history/in1
-rw-r--r--test/compose/history/kak_quoted_history1
-rw-r--r--test/compose/history/rc7
10 files changed, 119 insertions, 28 deletions
diff --git a/doc/pages/expansions.asciidoc b/doc/pages/expansions.asciidoc
index cda11e73..5156e293 100644
--- a/doc/pages/expansions.asciidoc
+++ b/doc/pages/expansions.asciidoc
@@ -258,6 +258,18 @@ The following expansions are supported (with required context _in italics_):
the text of the error that cancelled execution of the <commands> parameter
(or the previous <on_error_commands> parameter)
+*%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{uncommited_changes}`.
+
*%val{history_id}*::
_in buffer, window scope_ +
history id of the current buffer, an integer value which refers to a
@@ -353,6 +365,15 @@ The following expansions are supported (with required context _in italics_):
buffer is modified, including undoing and redoing previous modifications
(see also `%val{history_id}`)
+*%val{uncommitted_modifications}*::
+ _in buffer, window scope_ +
+ a list of quoted insertions (in the format `+line.column|text`) and
+ deletions (`-line.column|text`) not yet saved to the history (e.g. typing
+ in insert mode before pressing `<esc>`), where _line_ is the 1-based line
+ of the change, _column_ is the 1-based _byte_ of the change start (see
+ `%val{cursor_column}`), and _text_ is the content of the insertion or
+ deletion (see also `%val{history}`)
+
*%val{user_modes}*::
unquoted list of user modes.
diff --git a/src/buffer.cc b/src/buffer.cc
index f7eeee31..c2a04c2e 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -66,7 +66,7 @@ static void apply_options(OptionManager& options, const ParsedLines& parsed_line
}
Buffer::HistoryNode::HistoryNode(HistoryId parent)
- : parent{parent}, timepoint{Clock::now()}
+ : parent{parent}, committed{Clock::now()}
{}
Buffer::Buffer(String name, Flags flags, StringView data,
@@ -231,20 +231,10 @@ String Buffer::string(BufferCoord begin, BufferCoord end) const
return res;
}
-// A Modification holds a single atomic modification to Buffer
-struct Buffer::Modification
+Buffer::Modification Buffer::Modification::inverse() const
{
- enum Type { Insert, Erase };
-
- Type type;
- BufferCoord coord;
- StringDataPtr content;
-
- Modification inverse() const
- {
- return {type == Insert ? Erase : Insert, coord, content};
- }
-};
+ return {type == Insert ? Erase : Insert, coord, content};
+}
void Buffer::reload(StringView data, timespec fs_timestamp)
{
diff --git a/src/buffer.hh b/src/buffer.hh
index 0700acb9..73e5b370 100644
--- a/src/buffer.hh
+++ b/src/buffer.hh
@@ -230,14 +230,40 @@ public:
void on_unregistered();
void throw_if_read_only() const;
+
+ // A Modification holds a single atomic modification to Buffer
+ struct Modification
+ {
+ enum Type { Insert, Erase };
+
+ Type type;
+ BufferCoord coord;
+ StringDataPtr content;
+
+ Modification inverse() const;
+ };
+
+ using UndoGroup = Vector<Modification, MemoryDomain::BufferMeta>;
+
+ struct HistoryNode : UseMemoryDomain<MemoryDomain::BufferMeta>
+ {
+ HistoryNode(HistoryId parent);
+
+ HistoryId parent;
+ HistoryId redo_child = HistoryId::Invalid;
+ TimePoint committed;
+ UndoGroup undo_group;
+ };
+
+ const Vector<HistoryNode>& history() const { return m_history; }
+ const UndoGroup& current_undo_group() const { return m_current_undo_group; }
+
private:
void on_option_changed(const Option& option) override;
BufferRange do_insert(BufferCoord pos, StringView content);
BufferCoord do_erase(BufferCoord begin, BufferCoord end);
- struct Modification;
-
void apply_modification(const Modification& modification);
void revert_modification(const Modification& modification);
@@ -264,18 +290,6 @@ private:
String m_display_name;
Flags m_flags;
- using UndoGroup = Vector<Modification, MemoryDomain::BufferMeta>;
-
- struct HistoryNode : UseMemoryDomain<MemoryDomain::BufferMeta>
- {
- HistoryNode(HistoryId parent);
-
- HistoryId parent;
- HistoryId redo_child = HistoryId::Invalid;
- TimePoint timepoint;
- UndoGroup undo_group;
- };
-
Vector<HistoryNode> m_history;
HistoryId m_history_id = HistoryId::Invalid;
HistoryId m_last_save_history_id = HistoryId::Invalid;
diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc
index 4b6c3763..1d3d350d 100644
--- a/src/buffer_utils.cc
+++ b/src/buffer_utils.cc
@@ -213,4 +213,47 @@ void write_to_debug_buffer(StringView str)
}
}
+InplaceString<23> to_string(Buffer::HistoryId id)
+{
+ if (id == Buffer::HistoryId::Invalid) {
+ InplaceString<23> res;
+ res.m_data[0] = '-';
+ res.m_length = 1;
+ return res;
+ } else {
+ return to_string(static_cast<size_t>(id));
+ }
+}
+
+String format_modification(const Buffer::Modification& modification, Quoting quoting)
+{
+ auto quote = quoter(quoting);
+ return quote(format("{}{}.{}|{}",
+ modification.type == Buffer::Modification::Type::Insert ? '+' : '-',
+ modification.coord.line, modification.coord.column,
+ modification.content->strview()));
+}
+
+String history_as_string(const Vector<Buffer::HistoryNode>& history, Quoting quoting)
+{
+ auto format_history_node = [&](const Buffer::HistoryNode& node) {
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(node.committed.time_since_epoch());
+ return format("{} {} {}{}{}",
+ node.parent,
+ seconds.count(),
+ node.redo_child,
+ node.undo_group.empty() ? "" : " ",
+ undo_group_as_string(node.undo_group, quoting));
+ };
+ return join(history |transform(format_history_node), ' ', false);
+}
+
+String undo_group_as_string(const Buffer::UndoGroup& undo_group, Quoting quoting)
+{
+ auto modification_as_string = [&](const Buffer::Modification& modification) {
+ return format_modification(modification, quoting);
+ };
+ return join(undo_group |transform(modification_as_string), ' ', false);
+}
+
}
diff --git a/src/buffer_utils.hh b/src/buffer_utils.hh
index a3e43068..a7589864 100644
--- a/src/buffer_utils.hh
+++ b/src/buffer_utils.hh
@@ -84,6 +84,11 @@ void reload_file_buffer(Buffer& buffer);
void write_to_debug_buffer(StringView str);
+InplaceString<23> to_string(Buffer::HistoryId id);
+String format_modification(const Buffer::Modification& modification, Quoting quoting);
+String history_as_string(const Vector<Buffer::HistoryNode>& history, Quoting quoting);
+String undo_group_as_string(const Buffer::UndoGroup& undo_group, Quoting quoting);
+
}
#endif // buffer_utils_hh_INCLUDED
diff --git a/src/main.cc b/src/main.cc
index 84c88012..621b5350 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -309,6 +309,14 @@ static const EnvVarDesc builtin_env_vars[] = { {
return format("{} {} {} {}", setup.window_pos.line, setup.window_pos.column,
setup.window_range.line, setup.window_range.column);
}
+ }, {
+ "history", false,
+ [](StringView name, const Context& context, Quoting quoting) -> String
+ { return history_as_string(context.buffer().history(), quoting); }
+ }, {
+ "uncommitted_modifications", false,
+ [](StringView name, const Context& context, Quoting quoting) -> String
+ { return undo_group_as_string(context.buffer().current_undo_group(), quoting); }
}
};
diff --git a/test/compose/history/cmd b/test/compose/history/cmd
new file mode 100644
index 00000000..13d71626
--- /dev/null
+++ b/test/compose/history/cmd
@@ -0,0 +1 @@
+Amiddle<esc><a-f>dd
diff --git a/test/compose/history/in b/test/compose/history/in
new file mode 100644
index 00000000..d44e18fb
--- /dev/null
+++ b/test/compose/history/in
@@ -0,0 +1 @@
+start
diff --git a/test/compose/history/kak_quoted_history b/test/compose/history/kak_quoted_history
new file mode 100644
index 00000000..acd0fedc
--- /dev/null
+++ b/test/compose/history/kak_quoted_history
@@ -0,0 +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'
diff --git a/test/compose/history/rc b/test/compose/history/rc
new file mode 100644
index 00000000..931d0d1c
--- /dev/null
+++ b/test/compose/history/rc
@@ -0,0 +1,7 @@
+# Make our expansion have a predictable timestamp
+hook global ClientClose .* %{
+ evaluate-commands %sh{
+ kak -f 'ghf<space>ec$timestamp<esc>2f<space>ec$timestamp<esc>' <kak_quoted_history >tmp
+ mv tmp kak_quoted_history
+ }
+}