commit 2cd2cb220b526a5b35743ff8e505fd5d06c309eb
parent a907f4c2d1a1f39a4656c2a4a811d7eece717458
Author: Byron Torres <b@torresjrjr.com>
Date: Tue, 16 Jan 2024 23:49:25 +0000
add cmd_undo(), history.ha, full undo history impl
Diffstat:
6 files changed, 88 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,6 +10,7 @@ source=\
execute.ha \
file.ha \
global.ha \
+ history.ha \
interaction.ha \
parse.ha \
print.ha \
diff --git a/buffer.ha b/buffer.ha
@@ -8,6 +8,7 @@ type Buffer = struct{
filename: str,
lines: []*Line,
trash: []*Line,
+ hist: []ChangeSet,
cursor: size,
modified: bool,
written: bool,
@@ -29,16 +30,26 @@ fn buf_insert(buf: *Buffer, n: size, ls: *Line...) void = {
// for (let i = 0z; i < len(ls); i += 1) {
// debug("buf_insert(): ls[{}].text='{}'", i, ls[i].text);
// };
+
insert(buf.lines[n], ls...);
+
if (buf.written)
buf.modified = true;
+
+ hist_append(buf, (n, len(ls)): Addition);
+
+ debug("buf_insert(): len('last changeset')={}", len(buf.hist[len(buf.hist) - 1]));
+
+// let change = Change{ kind = true, n = n, z = len(ls) };
+// let activeset = buf.hist[buf.activehist];
+// append(activeset, change);
};
fn buf_deleteall(buf: *Buffer) void = {
// buf_wipetrash(buf);
if (len(buf.lines) > 1) {
- insert(buf.trash[0], buf.lines[1..]...);
+ append(buf.trash, buf.lines[1..]...);
delete(buf.lines[1..]);
};
if (buf.written)
@@ -57,10 +68,15 @@ fn buf_delete(buf: *Buffer, a: size, b: size) void = {
// buf.lines[i].globalmark = false;
//};
- insert(buf.trash[0], buf.lines[a..b+1]...);
+ append(buf.trash, buf.lines[a..b+1]...);
delete(buf.lines[a..b+1]);
+
if (buf.written)
buf.modified = true;
+
+ hist_append(buf, (a, (b + 1 - a)): Deletion);
+
+ debug("buf_delete(): len('last changeset')={}", len(buf.hist[len(buf.hist) - 1]));
};
fn buf_read(buf: *Buffer, src: io::handle, a: size) ((size, size) | io::error | encoding::utf8::invalid) = {
diff --git a/command.ha b/command.ha
@@ -44,6 +44,7 @@ type CmdError = !(
| NoPrevShCmd
| NoPrevGlobalSubCmd
| InvalidGlobalSubCmd
+ | NoHistory
| regex::error
| fs::error
| os::exec::error
@@ -120,6 +121,7 @@ fn cmd_append(s: *Session, cmd: *Command) (void | Error) = {
const n = get_linenum(cmd.linenums, s.buf.cursor);
+ hist_newset(s.buf);
for (let i = 0z; i < len(cmd.textinput); i += 1) {
const line = alloc(Line{ text = cmd.textinput[i], ... });
buf_insert(s.buf, n + 1 + i, line);
@@ -174,6 +176,7 @@ fn cmd_delete(s: *Session, cmd: *Command) (void | Error) = {
)?;
assert_nonzero(s, a)?;
+ hist_newset(s.buf);
buf_delete(s.buf, a, b);
s.buf.cursor =
@@ -566,7 +569,13 @@ fn cmd_copy(s: *Session, cmd: *Command) (void | Error) = {
printmode(s, cmd)?;
};
-fn cmd_undo(s: *Session, cmd: *Command) (void | Error) = void;
+fn cmd_undo(s: *Session, cmd: *Command) (void | Error) = {
+ s.warned = false;
+
+ assert_noaddrs(s, cmd.linenums)?;
+
+ hist_undo(s.buf)?;
+};
fn cmd_vglobal(s: *Session, cmd: *Command) (void | Error) = {
global(s, cmd, false)?;
diff --git a/error.ha b/error.ha
@@ -54,6 +54,8 @@ fn strerror(err: Error) str = {
return "No previous global subcommand";
case let e: InvalidGlobalSubCmd =>
return "Invalid interactive global subcommand"; // TODO: append 'e'?
+ case NoHistory =>
+ return "Nothing to undo"; // TODO: append 'e'?
case let e: regex::error =>
return regex::strerror(e);
case let e: fs::error =>
diff --git a/history.ha b/history.ha
@@ -0,0 +1,54 @@
+type History = []ChangeSet;
+
+type ChangeSet = []Change;
+
+type Change = (Addition | Deletion);
+
+type Addition = (size, size);
+
+type Deletion = (size, size);
+
+type NoHistory = !void;
+
+fn hist_newset(buf: *Buffer) void = {
+debug("hist_newset(): begin");
+ append(buf.hist, []: ChangeSet);
+debug("hist_newset(): end");
+};
+
+fn hist_append(buf: *Buffer, change: Change) void = {
+ append(buf.hist[len(buf.hist) - 1], change);
+};
+
+fn hist_undo(buf: *Buffer) (void | NoHistory) = {
+debug("hist_undo(): len(buf.hist)={}", len(buf.hist));
+ if (len(buf.hist) == 0)
+ return NoHistory;
+
+ let lastset = buf.hist[ len(buf.hist) - 1 ];
+debug("hist_undo(): len(lastset)={}", len(lastset));
+
+ for (let i = len(lastset) - 1; i < len(lastset); i -= 1) {
+ let change = lastset[i];
+ match (change) {
+ case let deln: Deletion =>
+ let (n, m) = deln;
+debug("hist_undo(): deln=({}, {})", n, m);
+ let lentrash = len(buf.trash);
+debug("hist_undo(): lentrash={}", lentrash);
+ let lines = buf.trash[(lentrash - m)..];
+debug("hist_undo(): len(lines)={}", len(lines));
+ insert(buf.lines[n], lines...);
+ delete(buf.trash[(lentrash - m)..]);
+ case let addn: Addition =>
+ let (n, m) = addn;
+debug("hist_undo(): addn=({}, {})", n, m);
+ let lines = buf.lines[n..(n + m)];
+debug("hist_undo(): len(lines)={}", len(lines));
+ append(buf.trash, lines...);
+ delete(buf.lines[n..(n + m)]);
+ };
+ };
+
+ delete(buf.hist[ len(buf.hist) - 1 ]);
+};
diff --git a/main.ha b/main.ha
@@ -36,8 +36,9 @@ export fn main() void = {
defer bufio::finish(&input);
const buffer = Buffer{
- lines = *alloc([ alloc(Line{ ... }) ]),
- trash = *alloc([]: []*Line),
+ lines = alloc([ alloc(Line{ ... }) ]...),
+ trash = alloc([]: []*Line...),
+ hist = alloc([]: History...),
...
};
defer buffer_finish(&buffer);