ed

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

commit 68d687a913ca150afed7877b26343356b1dc87dc
parent 3d06a08091e0de7e3e156e0d359eb3cf7e0a695b
Author: Byron Torres <b@torresjrjr.com>
Date:   Sat,  6 Jan 2024 16:19:41 +0000

impl & repeat command

Diffstat:
Mcommand.ha | 34++++++++++++++++++++++++++--------
Mmain.ha | 5+++++
Mparse.ha | 13++++++++++---
3 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/command.ha b/command.ha @@ -99,7 +99,7 @@ fn lookupcmd(name: rune) CommandFn = { case 'w' => return &cmd_write; case '=' => return &cmd_linenumber; case '!' => return &cmd_shellescape; - case '\x00' => return &cmd_null; + case NUL => return &cmd_null; case => abort(); }; }; @@ -279,6 +279,8 @@ fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { }; }; + let prevcmd: (void | Command) = void; + for :marks (true) { // find next global-marked line let n = 1z; @@ -302,9 +304,22 @@ fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { // 'a', 'c', 'i' ? switch (cmd.cmdname) { case 'a', 'c', 'i', 'g', 'G', 'v', 'V' => - // return new type 'InvalidGlobalInteractiveSubcmd' ? + // TODO: how to handle? + // return !InvalidGlobalInteractiveSubcmd; ? + continue; + case NUL => + line.globalmark = false; continue; - case => void; + case '&' => + match (prevcmd) { + case void => + // TODO: return NoPrevGlobalCmd; + abort("global interactive: &: no previous command"); + case let prevcmd: Command => + cmd = prevcmd; + }; + case => + prevcmd = cmd; }; execute(s, &cmd)?; @@ -370,11 +385,11 @@ fn cmd_join(s: *Session, cmd: *Command) (void | Error) = { }; let ls: []str = []; - let mark = '\x00'; + let mark = NUL; for (let n = a; n <= b; n += 1) { const l = s.buf.lines[n]; append(ls, l.text); - if (mark == '\x00') { + if (mark == NUL) { mark = l.mark; }; }; @@ -408,7 +423,7 @@ fn cmd_mark(s: *Session, cmd: *Command) (void | Error) = { debug("cmd_mark(): search A i={}", i); if (s.buf.trash[i].mark == mark) { debug("cmd_mark(): search A i={} true", i); - s.buf.trash[i].mark = '\x00'; + s.buf.trash[i].mark = NUL; yield :search; }; }; @@ -417,7 +432,7 @@ fn cmd_mark(s: *Session, cmd: *Command) (void | Error) = { debug("cmd_mark(): search B i={}", i); if (s.buf.lines[i].mark == mark) { debug("cmd_mark(): search B i={} true", i); - s.buf.lines[i].mark = '\x00'; + s.buf.lines[i].mark = NUL; yield :search; }; }; @@ -578,8 +593,11 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = { )?; assert_nonzero(s, a)?; - const replacement = cmd.arg2; const regex = regex::compile(cmd.arg1)?; defer regex::finish(&regex); + const replacement = cmd.arg2; + // TODO: parse and use substitution flags `cmd.arg3` + // use regex::replace ? + // TODO: implement '&', '%' for (let i = a; i <= b; i += 1) { const old = s.buf.lines[i].text; diff --git a/main.ha b/main.ha @@ -98,6 +98,11 @@ export fn main() void = { }; defer command_finish(&cmd); + if (cmd.cmdname == '&') { + errormsg(&s, strerror('&': UnknownCommand)); + continue; + }; + match (execute(&s, &cmd)) { case Quit => debug("main() for match (exec) case Quit"); diff --git a/parse.ha b/parse.ha @@ -34,6 +34,8 @@ type InvalidDelimiter = !void; type ExpectedDelimiter = !void; +def NUL = '\x00'; + // Parses inputted commands. Returns true when command is ready. fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { let inputline = match (bufio::scan_line(input)?) { @@ -86,10 +88,11 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { return cmd; }; +// TODO: remove 'bool' returns fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) = { switch (cmd.cmdname) { // [ ] - case '\x00' => + case NUL => return true; // (q|Q) @@ -209,6 +212,10 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) = cmd.arg3 = scan_rest(iter); // TODO: scan properly here? return true; + case '&' => + scan_end_assert(iter)?; + return true; + case => abort(); }; @@ -399,7 +406,7 @@ fn scan_cmdname(iter: *strings::iterator) (rune | UnknownCommand) = { scan_blanks(iter); let r = match (strings::next(iter)) { case void => - return '\x00'; + return NUL; case let r: rune => yield r; }; @@ -408,7 +415,7 @@ fn scan_cmdname(iter: *strings::iterator) (rune | UnknownCommand) = { case 'a', 'c', 'd', 'e', 'E', 'f', 'g', 'G', 'h', 'H', 'i', 'j', 'k', 'l', 'm', 'n', 'p', 'P', 'q', 'Q', - 'r', 's', 't', 'u', 'v', 'V', 'w', '=', '!', + 'r', 's', 't', 'u', 'v', 'V', 'w', '=', '!', '&', => return r; case =>