From b03b51d27ace58cc17eb8a3aec99fa20dd3209ab Mon Sep 17 00:00:00 2001 From: Jason Felice Date: Mon, 31 Dec 2018 12:40:49 -0500 Subject: [PATCH] Add 'history' and 'uncommitted_modifications' expansions --- doc/pages/expansions.asciidoc | 21 ++++++++++++ src/buffer.cc | 18 +++-------- src/buffer.hh | 42 ++++++++++++++++-------- src/buffer_utils.cc | 43 +++++++++++++++++++++++++ src/buffer_utils.hh | 5 +++ src/main.cc | 8 +++++ test/compose/history/cmd | 1 + test/compose/history/in | 1 + test/compose/history/kak_quoted_history | 1 + test/compose/history/rc | 7 ++++ 10 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 test/compose/history/cmd create mode 100644 test/compose/history/in create mode 100644 test/compose/history/kak_quoted_history create mode 100644 test/compose/history/rc diff --git a/doc/pages/expansions.asciidoc b/doc/pages/expansions.asciidoc index cda11e73b..5156e2936 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 parameter (or the previous 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 ``), 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 f7eeee318..c2a04c2e3 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 0700acb9d..73e5b370b 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; + + struct HistoryNode : UseMemoryDomain + { + HistoryNode(HistoryId parent); + + HistoryId parent; + HistoryId redo_child = HistoryId::Invalid; + TimePoint committed; + UndoGroup undo_group; + }; + + const Vector& 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; - - struct HistoryNode : UseMemoryDomain - { - HistoryNode(HistoryId parent); - - HistoryId parent; - HistoryId redo_child = HistoryId::Invalid; - TimePoint timepoint; - UndoGroup undo_group; - }; - Vector 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 4b6c37637..1d3d350d7 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(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& history, Quoting quoting) +{ + auto format_history_node = [&](const Buffer::HistoryNode& node) { + auto seconds = std::chrono::duration_cast(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 a3e43068b..a7589864a 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& 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 84c880120..621b53501 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 000000000..13d716263 --- /dev/null +++ b/test/compose/history/cmd @@ -0,0 +1 @@ +Amiddledd diff --git a/test/compose/history/in b/test/compose/history/in new file mode 100644 index 000000000..d44e18fb9 --- /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 000000000..acd0fedc1 --- /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 000000000..931d0d1cc --- /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 'ghfec$timestamp2fec$timestamp' tmp + mv tmp kak_quoted_history + } +}