Skip to content
Jason Felice edited this page Dec 30, 2018 · 11 revisions

History API

Goals

  1. Provide all information necessary for a "Persistent Undo" plugin.

  2. Provide all information necessary for a tool like parinfer-rust to inspect all the changes to the buffer since it was last seen.

  3. Enable for "undojoin"-like functionality: easily amend the current history node.

  4. Be easy to parse from POSIX shell.

Nice to have: 1. Use one variable, allowing history to be replaced with set-option. 2. Have the format be compact, to avoid environment variable limits. 3. Able to support cursor positions or timestamps for each node in the future.

Data Items

HistoryNode
  1. Parent

  2. Redo Child

  3. Undo group (a vector of modifications)

Modification
  1. Insert/Erase

  2. Buffer coordinate

  3. Text

Format

The data is represented in ( entity, attribute, value ) triplets. "Entity" values are the ids of history items being described. "Attributes" are the single-character identifiers r for redo child, m for modification, and p for parent. Additonal attributes such as selections or timestamps can be added at a later time.

0 r 1                   # node 1 is the redo child for node 0
0 m '+|1.2|hello world' # node 0 has a modification adding "hello world" at 1.2
0 m '-|2.7|foo'         # node 0 has a modification removing "foo" at 2.7
1 p 0                   # node 1 has parent 0
1 r 2                   # node 2 is the redo child for node 1
1 m '+|2.2|bar'         # node 1 has a modification adding "bar" at 2.2
1 a _                   # 1 is the active history_id
2 p 1
...

While possible to further normalize the data by introducing modification ids and describing modifications with ( entity, attribute, value ) triplets, I figure this inflates the size of the data unnecesarily: the structure of modifications seems more fixed than the structure of history nodes, and since it is only three fields without special cases needing more or fewer attributes (like our history root), let’s not pay the penalty for extensibility.

Modifications are represented as op|coord|text strings. text is at the end so it can contain arbitrary text, including pipe symbols, and still be easily parsed by shell, like so:

op="${change%%|*}"
change="${change#*|}"
coord="${change%%|*}"
text="${change#*|}"
Clone this wiki locally