ed

[hare] The standard editor
Log | Files | Refs | README | LICENSE

commit 88cd4e19d8f90212946de3caa7f172e9625c8d31
parent a8daa8d62cc7693f30ce906db8c523c789ab392f
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun, 21 Jan 2024 01:08:40 +0000

impl POSIX undo behaivour

Diffstat:
Mcommand.ha | 38+++++++++++++++++++++++++++++---------
Mexecute.ha | 3+++
Mhistory.ha | 30+++++++++++++++++++++++++++++-
Mmain.ha | 2++
4 files changed, 63 insertions(+), 10 deletions(-)

diff --git a/command.ha b/command.ha @@ -121,8 +121,10 @@ fn cmd_append(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum(cmd.linenums, s.buf.cursor); - if (len(cmd.textinput) > 0) + if (len(cmd.textinput) > 0) { hist_newseq(s.buf); + s.redolastchange = false; + }; for (let i = 0z; i < len(cmd.textinput); i += 1) { const line = alloc(Line{ text = cmd.textinput[i], ... }); @@ -149,7 +151,7 @@ fn cmd_change(s: *Session, cmd: *Command) (void | Error) = { }; }; - hist_newseq(s.buf); + hist_newseq(s.buf); s.redolastchange = false; buf_delete(s.buf, a, b); for (let i = 0z; i < len(cmd.textinput); i += 1) { @@ -179,7 +181,7 @@ fn cmd_delete(s: *Session, cmd: *Command) (void | Error) = { )?; assert_nonzero(s, a)?; - hist_newseq(s.buf); + hist_newseq(s.buf); s.redolastchange = false; buf_delete(s.buf, a, b); s.buf.cursor = @@ -246,8 +248,10 @@ fn cmd_insert(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum(cmd.linenums, s.buf.cursor); const n = if (n == 0) 1z else n; - if (len(cmd.textinput) > 0) + if (len(cmd.textinput) > 0) { hist_newseq(s.buf); + s.redolastchange = false; + }; for (let i = 0z; i < len(cmd.textinput); i += 1) { const line = alloc(Line{ text = cmd.textinput[i], ... }); @@ -291,7 +295,7 @@ fn cmd_join(s: *Session, cmd: *Command) (void | Error) = { ... }); - hist_newseq(s.buf); + hist_newseq(s.buf); s.redolastchange = false; buf_delete(s.buf, a, b); buf_insert(s.buf, a, newline); @@ -383,7 +387,7 @@ fn cmd_move(s: *Session, cmd: *Command) (void | Error) = { const lines = alloc(s.buf.lines[a..b+1]...); defer free(lines); // TODO: mem? - hist_newseq(s.buf); + hist_newseq(s.buf); s.redolastchange = false; buf_delete(s.buf, a, b); buf_insert(s.buf, dest, lines...); @@ -471,7 +475,9 @@ fn cmd_read(s: *Session, cmd: *Command) (void | Error) = { const (sz, _) = buf_read(s.buf, rd, n)?; if (sz == 0) - hist_discardseq(s.buf); + hist_discardseq(s.buf) + else + s.redolastchange = false; if (!s.suppressmode) fmt::println(sz)!; @@ -549,6 +555,8 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = { if (changes == 0) { hist_discardseq(s.buf); return NoMatch; + } else { + s.redolastchange = false; }; printmode(s, cmd)?; @@ -582,7 +590,7 @@ fn cmd_copy(s: *Session, cmd: *Command) (void | Error) = { const lines = alloc(s.buf.lines[a..b+1]...); defer free(lines); // TODO: ? - hist_newseq(s.buf); + hist_newseq(s.buf); s.redolastchange = false; buf_insert(s.buf, dest, lines...); s.buf.cursor = dest - 1 + len(lines); @@ -594,7 +602,19 @@ fn cmd_undo(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; - hist_undo(s.buf)?; // TODO: redo last undo too + switch (s.undomode) { + case UndoMode::POSIX => + if (s.redolastchange) { + hist_redo(s.buf)?; + s.redolastchange = false; + } else { + hist_undo(s.buf)?; + s.redolastchange = true; + }; + case UndoMode::FULL => + hist_undo(s.buf)?; + hist_discardseq(s.buf); + }; }; fn cmd_vglobal(s: *Session, cmd: *Command) (void | Error) = { diff --git a/execute.ha b/execute.ha @@ -12,6 +12,9 @@ fn execute(s: *Session, cmd: *Command) (void | Error) = { errormsg(s, e); // TODO: move up into caller? return e; }; + + for (let i = 0z; i < len(s.buf.trash); i += 1) + debug("trash[{}]={}", i, s.buf.trash[i].text); }; fn exec_addrs(s: *Session, cmd: *Command) (void | CmdError) = { diff --git a/history.ha b/history.ha @@ -13,6 +13,12 @@ type Addition = (size, size); // The deletion of a buffer chunk (start position, chunk size). type Deletion = (size, size); +// The effect of the undo command. +type UndoMode = enum{ + POSIX, // undoes the last buffer change, including that of 'u' + FULL, // undoes the last buffer change, exlucding that of 'u' +}; + // There is no history to undo. type NoHistory = !void; @@ -34,6 +40,7 @@ fn hist_append(buf: *Buffer, change: Change) void = { }; fn hist_undo(buf: *Buffer) (void | NoHistory) = { + debug("hist_undo()"); if (len(buf.hist) == 0) return NoHistory; @@ -53,6 +60,27 @@ fn hist_undo(buf: *Buffer) (void | NoHistory) = { insert(buf.trash[0], lines...); delete(buf.lines[n..(n + m)]); }; +}; + +fn hist_redo(buf: *Buffer) (void | NoHistory) = { + debug("hist_redo()"); + if (len(buf.hist) == 0) + return NoHistory; - delete(buf.hist[ len(buf.hist) - 1 ]); + let seq = buf.hist[ len(buf.hist) - 1 ]; + + for (let j = len(seq) - 1; j < len(seq); j -= 1) + match (seq[j]) { + case let addn: Addition => + let (n, m) = addn; + let lentrash = len(buf.trash); + let lines = buf.trash[(lentrash - m)..]; + insert(buf.lines[n], lines...); + delete(buf.trash[(lentrash - m)..]); + case let deln: Deletion => + let (n, m) = deln; + let lines = buf.lines[n..(n + m)]; + append(buf.trash, lines...); + delete(buf.lines[n..(n + m)]); + }; }; diff --git a/main.ha b/main.ha @@ -14,11 +14,13 @@ type Session = struct{ promptmode: bool, suppressmode: bool, helpmode: bool, + undomode: UndoMode, warned: bool, lasterror: (void | Error), lastregex: (void | str), lastshcmd: (void | str), + redolastchange: bool, }; def proghelp: [_]getopt::help = [