ed

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

commit 6143f14e22aa10e3bd446858b859c7f6eccf840a
parent 8071aefcd32dcb1d1e909021b30343cac097e255
Author: Byron Torres <b@torresjrjr.com>
Date:   Tue, 13 Dec 2022 04:59:49 +0000

add cmd_move(); handle errors, invalid addresses

Diffstat:
Maddress.ha | 12++++++------
Mbuffer.ha | 9++++++---
Mcommand.ha | 116++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mmain.ha | 16+++++++++++++++-
Mparse.ha | 4++--
5 files changed, 99 insertions(+), 58 deletions(-)

diff --git a/address.ha b/address.ha @@ -12,7 +12,7 @@ type currentline = void; type lastline = void; -type badaddress = !void; +type invalidaddress = !void; fn addr_nextline(buf: *buffer, n: size) size = { if (len(buf.lines) - 1 == n) { @@ -28,9 +28,9 @@ fn addr_prevline(buf: *buffer, n: size) size = { return n - 1; }; -fn addr_linenum(buf: *buffer, n: size) (size | badaddress) = { - if (n > len(buf.lines)) { - return badaddress; +fn addr_linenum(buf: *buffer, n: size) (size | invalidaddress) = { + if (n >= len(buf.lines)) { + return invalidaddress; }; return n; }; @@ -39,12 +39,12 @@ fn addr_lastline(buf: *buffer) size = { return len(buf.lines) - 1z; }; -fn addr_mark(buf: *buffer, mark: rune) (size | badaddress) = { +fn addr_mark(buf: *buffer, mark: rune) (size | invalidaddress) = { for (let n = 1z; n < len(buf.lines); n += 1) { const l = buf.lines[n]; if (l.mark == mark) { return n; }; }; - return badaddress; + return invalidaddress; }; diff --git a/buffer.ha b/buffer.ha @@ -19,11 +19,14 @@ type line = struct { fn buf_insert(buf: *buffer, n: size, ls: *line...) void = { debug("buf_insert(n={}), len(ls)={}", n, len(ls)); +// for (let i = 0z; i < len(ls); i += 1) { +// debug("buf_insert(): ls[{}].text='{}'", i, ls[i].text); +// }; insert(buf.lines[n], ls...); }; fn buf_deleteall(buf: *buffer) void = { - buf_wipetrash(buf); + // buf_wipetrash(buf); if (len(buf.lines) > 1) { insert(buf.trash[0], buf.lines[1..]...); @@ -33,7 +36,7 @@ fn buf_deleteall(buf: *buffer) void = { fn buf_delete(buf: *buffer, a: size, b: size) void = { debug("buf_delete(a={}, b={})", a, b); - buf_wipetrash(buf); + // buf_wipetrash(buf); insert(buf.trash[0], buf.lines[a..b+1]...); delete(buf.lines[a..b+1]); @@ -41,7 +44,7 @@ fn buf_delete(buf: *buffer, a: size, b: size) void = { fn buf_read(buf: *buffer, src: io::handle, a: size) (size, size) = { // TODO: don't call this here, call it at a higher level - buf_wipetrash(buf); + // buf_wipetrash(buf); let ls: []*line = []; defer free(ls); diff --git a/command.ha b/command.ha @@ -25,10 +25,10 @@ type suffix = enum { }; type error = !( - badaddress + invalidaddress + | unexpectedaddress | nofilename | buffermodified - | unexpectedaddress | fs::error ); @@ -41,25 +41,27 @@ type unexpectedaddress = !void; // Executes the session's .cmd command. fn execute(s: *session, cmd: *command) (void | error) = { // TODO: move this into the cmd_* functions? - exec_addrs(s, cmd)?; - defer delete(cmd.linenums[..]); // TODO: write finish_cmd() - - cmd.cmdfn(s, cmd)?; + match (exec_addrs(s, cmd)) { + case void => void; + case let err: error => + delete(cmd.linenums[..]); + return errormsg(s, err); + }; + // TODO: write finish_cmd() + // TODO: finish the command at a higher level (main()) ?. + defer delete(cmd.linenums[..]); + + match (cmd.cmdfn(s, cmd)) { + case void => void; + case let err: error => + return errormsg(s, err); + }; }; fn exec_addrs(s: *session, cmd: *command) (void | error) = { for (let i = 0z; i < len(cmd.addrs); i += 1) { const addr = cmd.addrs[i]; - let n = match (addr.addrtype) { - case let n: size => - yield addr_linenum(&s.buf, n)?; - case currentline => - yield s.buf.cursor; - case lastline => - yield addr_lastline(&s.buf); - case let m: rune => - yield addr_mark(&s.buf, m)?; - }; + let n = exec_addr(s, addr)?; n = n + addr.lineoffset: size; // beware of negatives debug("exec_addrs(): n={}", n); append(cmd.linenums, n); @@ -70,8 +72,22 @@ fn exec_addrs(s: *session, cmd: *command) (void | error) = { }; }; +fn exec_addr(s: *session, addr: address) (size | error) = { + match (addr.addrtype) { + case let n: size => + return addr_linenum(&s.buf, n)?; + case currentline => + return s.buf.cursor; + case lastline => + return addr_lastline(&s.buf); + case let m: rune => + return addr_mark(&s.buf, m)?; + }; +}; -fn get_range(s: *session, lns: *[]size, a: size, b: size) ((size, size) | badaddress) = { + +fn get_range(s: *session, lns: *[]size, a: size, b: size) ((size, size) | invalidaddress) = { + debug("get_range(): len(lns)={}", len(lns)); const (a, b) = if (len(lns) == 0) { yield (a, b); } else if (len(lns) == 1) { @@ -80,9 +96,8 @@ fn get_range(s: *session, lns: *[]size, a: size, b: size) ((size, size) | badadd yield (lns[len(lns)-2], lns[len(lns)-1]); }; debug("get_range(): (a, b)=({}, {})", a, b); - if (a > b) { - errormsg(s, "Invalid address"); - return badaddress; + if (a < 0 || a > b || b >= len(s.buf.lines)) { + return invalidaddress; }; return (a, b); }; @@ -97,15 +112,13 @@ fn get_linenum(lns: []size, n: size) size = { fn assert_noaddrs(s: *session, lns: []size) (void | unexpectedaddress) = { if (len(lns) != 0) { - errormsg(s, "Unexpected address"); return unexpectedaddress; }; }; -fn assert_nonzero(s: *session, n: size) (void | badaddress) = { +fn assert_nonzero(s: *session, n: size) (void | invalidaddress) = { if (n < 1) { - errormsg(s, "Invalid address"); - return badaddress; + return invalidaddress; }; }; @@ -136,7 +149,6 @@ fn cmd_edit(s: *session, cmd: *command) (void | error) = { assert_noaddrs(s, cmd.linenums)?; if (s.buf.modified && !s.warned) { - errormsg(s, "Warning: buffer modified"); s.warned = true; return buffermodified; }; @@ -148,14 +160,12 @@ fn cmd_edit(s: *session, cmd: *command) (void | error) = { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; } else { - errormsg(s, "No current filename"); return nofilename; }; }; const h = match (os::open(fname)) { case let err: fs::error => - errormsg(s, fs::strerror(err)); return err; case let h: io::file => yield h: io::handle; @@ -178,7 +188,6 @@ fn cmd_filename(s: *session, cmd: *command) (void | error) = { s.buf.filename = cmd.arg; }; if (len(s.buf.filename) == 0) { - errormsg(s, "No current filename"); return nofilename; }; fmt::println(s.buf.filename)!; @@ -275,7 +284,33 @@ fn cmd_mark(s: *session, cmd: *command) (void | error) = { fn cmd_list(s: *session, cmd: *command) (void | error) = void; -fn cmd_move(s: *session, cmd: *command) (void | error) = void; +fn cmd_move(s: *session, cmd: *command) (void | error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + s.buf.cursor, + s.buf.cursor, + )?; + assert_nonzero(s, a)?; + + // TODO: parse this properly in parse.ha? + const iter = strings::iter(cmd.arg); + const n = match (scan_addr(&iter)) { + case let addr: address => + yield exec_addr(s, addr)? + 1; + case void => + return invalidaddress; + }; + debug("cmd_move(): n={}", n); + + // TODO: write buf_get(&buf, a, b) ? + const ls = alloc(s.buf.lines[a..b+1]...); defer free(ls); +// for (let i = 0z; i < len(ls); i += 1) { +// debug("cmd_move(): ls[{}].text='{}'", i, ls[i].text); +// }; + buf_delete(&s.buf, a, b); + buf_insert(&s.buf, n, ls...); +}; fn cmd_number(s: *session, cmd: *command) (void | error) = { const (a, b) = get_range( @@ -285,6 +320,7 @@ fn cmd_number(s: *session, cmd: *command) (void | error) = { s.buf.cursor, )?; assert_nonzero(s, a)?; + debug("cmd_number(): (a, b)=({}, {})", a, b); for (let n = a; n <= b; n += 1) { fmt::printfln("{}\t{}", n, s.buf.lines[n].text)!; @@ -300,6 +336,7 @@ fn cmd_print(s: *session, cmd: *command) (void | error) = { s.buf.cursor, )?; assert_nonzero(s, a)?; + for (let n = a; n <= b; n += 1) { fmt::println(s.buf.lines[n].text)!; }; @@ -315,7 +352,7 @@ fn cmd_quit(s: *session, cmd: *command) (void | error) = void; fn cmd_quit_forced(s: *session, cmd: *command) (void | error) = void; -fn cmd_read(s: *session, cmd: *command) void = { +fn cmd_read(s: *session, cmd: *command) (void | error) = { const n = get_linenum(cmd.linenums, s.buf.cursor); const fname = if (len(cmd.arg) != 0) { s.buf.filename = cmd.arg; @@ -324,17 +361,11 @@ fn cmd_read(s: *session, cmd: *command) void = { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; } else { - errormsg(s, "No current filename"); - return; + return nofilename; }; }; - const h = match (os::open(fname)) { - case let err: fs::error => - return errormsg(s, fs::strerror(err)); - case let h: io::file => - yield h: io::handle; - }; + const h = os::open(fname)?: io::handle; defer io::close(h)!; const (sz, _) = buf_read(&s.buf, h, n); @@ -370,7 +401,6 @@ fn cmd_write(s: *session, cmd: *command) (void | error) = { yield if (len(s.buf.filename) != 0) { yield s.buf.filename; } else { - errormsg(s, "No current filename"); return nofilename; }; }; @@ -379,14 +409,8 @@ fn cmd_write(s: *session, cmd: *command) (void | error) = { case let err: fs::error => yield match (err) { case errors::noentry => - yield match (os::create(fname, 0o644)) { - case let h: io::file => - yield h: io::handle; - case let err: fs::error => - return errormsg(s, fs::strerror(err)); - }; + yield os::create(fname, 0o644)?: io::handle; case => - errormsg(s, fs::strerror(err)); return err; }; case let h: io::file => diff --git a/main.ha b/main.ha @@ -1,6 +1,7 @@ use bufio; use encoding::utf8; use fmt; +use fs; use getopt; use io; use os; @@ -122,10 +123,23 @@ export fn main() void = { os::exit(1); }; -fn errormsg(s: *session, msg: str) void = { +fn errormsg(s: *session, err: error) error = { fmt::errorln('?')!; + const msg = match (err) { + case invalidaddress => + yield "Invalid address"; + case nofilename => + yield "No filename"; + case buffermodified => + yield "Buffer modified"; + case unexpectedaddress => + yield "Unexpected address"; + case let e: fs::error => + yield fs::strerror(e); + }; s.lasterror = msg; if (s.helpmode) { fmt::errorfln(msg)!; }; + return err; }; diff --git a/parse.ha b/parse.ha @@ -15,7 +15,7 @@ fn parse(cmd: *command, input: str) bool = { cmd.addrs = scan_addrs(&iter); cmd.cmdfn = scan_cmdfn(&iter); cmd.arg = scan_arg(&iter); - debug("parse(): cmd.arg={}", cmd.arg); +// debug("parse(): cmd.arg={}", cmd.arg); return true; }; @@ -222,7 +222,7 @@ fn scan_cmdfn(iter: *strings::iterator) commandfn = { case 'j' => return &cmd_join; case 'k' => return &cmd_mark; // case 'l' => return &cmd_list; -// case 'm' => return &cmd_move; + case 'm' => return &cmd_move; case 'n' => return &cmd_number; case 'p' => return &cmd_print; // case 'q' => return &cmd_quit;