Merge remote-tracking branch 'eraserhd/history-api'
This commit is contained in:
commit
43dc494e5c
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -219,4 +219,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
1
test/compose/history/cmd
Normal file
1
test/compose/history/cmd
Normal file
|
@ -0,0 +1 @@
|
|||
Amiddle<esc><a-f>dd
|
1
test/compose/history/in
Normal file
1
test/compose/history/in
Normal file
|
@ -0,0 +1 @@
|
|||
start
|
1
test/compose/history/kak_quoted_history
Normal file
1
test/compose/history/kak_quoted_history
Normal file
|
@ -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'
|
7
test/compose/history/rc
Normal file
7
test/compose/history/rc
Normal file
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user