ed

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

commit 14d833400a367b05161b371ca1e1235faae065d0
parent 85c2713c7dbb77a98d847f1e067578e239c92d12
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun,  7 Jan 2024 00:17:45 +0000

add cmd_global

Diffstat:
Mcommand.ha | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Minteraction.ha | 1+
Mmain.ha | 2++
Mparse.ha | 32++++++++++++++++++++++++--------
4 files changed, 121 insertions(+), 15 deletions(-)

diff --git a/command.ha b/command.ha @@ -3,11 +3,12 @@ use errors; use fmt; use fs; use io; +use memio; use os; use os::exec; use regex; use strings; -use memio; +use types; type Command = struct{ addrs: []Address, @@ -117,7 +118,7 @@ fn cmd_append(s: *Session, cmd: *Command) (void | Error) = { for (let i = 0z; i < len(cmd.textinput); i += 1) { const l = alloc(Line{ text = cmd.textinput[i], ... }); - debug("cmd_append(): l.text={}", l.text); + //debug("cmd_append(): l.text=<{}>", l.text); buf_insert(s.buf, n + 1 + i, l); }; @@ -144,7 +145,7 @@ fn cmd_change(s: *Session, cmd: *Command) (void | Error) = { for (let i = 0z; i < len(cmd.textinput); i += 1) { const l = alloc(Line{ text = cmd.textinput[i], ... }); - debug("cmd_append(): l.text={}", l.text); + //debug("cmd_append(): l.text=<{}>", l.text); buf_insert(s.buf, a + i, l); }; @@ -262,7 +263,93 @@ fn cmd_global(s: *Session, cmd: *Command) (void | Error) = { )?; assert_nonzero(s, a)?; - void; + const regex = regex::compile(cmd.arg1)?; defer regex::finish(&regex); + + for (let i = a; i <= b; i += 1) { + const line = s.buf.lines[i]; + line.globalmark = false; // TODO: is this necessary? + if (regex::test(&regex, line.text)) { + line.globalmark = true; + }; + }; + + let subcmds: []Command = []; + + let inputtext = strings::join("\n", cmd.textinput...); + let inputbytes = strings::toutf8(inputtext); + let inputstream = memio::dynamic_from(inputbytes); + defer io::close(&inputstream)!; + let input = bufio::newscanner(&inputstream, types::SIZE_MAX); + defer bufio::finish(&input); + // TODO: could be less than types::SIZE_MAX ^ ? + + for (true) { + // TODO: handle mem + // TODO: just use 'parse()?' when compiler allows + //let subcmd = parse(s.input)?; + let subcmd = match (parse(&input)) { + case let c: Command => + yield c; + case io::EOF => + break; + case let e: ParseError => + return e; + case let e: InteractionError => + return e; + }; + + debug("cmd_global() for subcmd.cmdname=<{}>", subcmd.cmdname); + + // TODO: write new parse_global() function instead + // as to avoid reading multiple lines in the case of + // 'a', 'c', 'i' ? + switch (subcmd.cmdname) { + case 'g', 'G', 'v', 'V', '!' => + // TODO: implement? + errormsg(s, subcmd.cmdname: InvalidGlobalSubCmd); + continue; + case '&' => + errormsg(s, '&': UnknownCommand); + continue; + case 'u' => + // TODO: undo behaviour + continue; + case => + void; + }; + + append(subcmds, subcmd); + }; + + debug("cmd_global() len(subcmds)={}", len(subcmds)); + + if (len(subcmds) == 0) + append(subcmds, Command{ cmdname = 'p', ... }); + + for :marks (true) { + // find next global-marked line + let n = 1z; + for :lines (true; n += 1) { + if (n >= len(s.buf.lines)) { + break :marks; + }; + if (s.buf.lines[n].globalmark) { + break :lines; + }; + }; + s.buf.cursor = n; + let line = s.buf.lines[n]; + + // TODO: handle termination by SIGINT signal + + for (let i = 0z; i < len(subcmds); i += 1) { + let subcmd = subcmds[i]; + //debug("cmd_global() for :marks execute()ing"); + execute(s, &subcmd)?; + }; + + line.globalmark = false; + }; }; fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { @@ -318,7 +405,7 @@ fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { return e; }; - // write new parse_global_interactive() function instead + // TODO: write new parse_global_interactive() function instead // as to avoid reading multiple lines in the case of // 'a', 'c', 'i' ? switch (subcmd.cmdname) { @@ -375,7 +462,7 @@ fn cmd_insert(s: *Session, cmd: *Command) (void | Error) = { for (let i = 0z; i < len(cmd.textinput); i += 1) { const l = alloc(Line{ text = cmd.textinput[i], ... }); - debug("cmd_insert(): l.text={}", l.text); + //debug("cmd_insert(): l.text=<{}>", l.text); buf_insert(s.buf, n + i, l); }; @@ -471,7 +558,7 @@ fn cmd_list(s: *Session, cmd: *Command) (void | Error) = { s.buf.cursor, )?; assert_nonzero(s, a)?; - debug("cmd_list(): (a, b)=({}, {})", a, b); + //debug("cmd_list(): (a, b)=({}, {})", a, b); printlistlns(s.buf, a, b)?; s.buf.cursor = b; diff --git a/interaction.ha b/interaction.ha @@ -7,6 +7,7 @@ use types; type InteractionError = !( Quit + | io::EOF | UnexpectedEOF | utf8::invalid | io::error diff --git a/main.ha b/main.ha @@ -92,6 +92,8 @@ export fn main() void = { const cmd = match (parse(&input)) { case let cmd: Command => yield cmd; + case io::EOF => + yield Command{ cmdname = 'q', ... }; case let err: ParseError => errormsg(&s, strerror(err)); continue; diff --git a/parse.ha b/parse.ha @@ -42,7 +42,7 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { case let s: const str => yield s; case io::EOF => - return Command{ cmdname = 'q', ... }; + return io::EOF; }; let iter = strings::iter(inputline); @@ -52,7 +52,7 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { cmd.cmdname = scan_cmdname(&iter)?; parse_cmdargs(&cmd, &iter)?; - switch (cmd.cmdname) { + switch :input (cmd.cmdname) { case => void; case 'a', 'c', 'i' => for (true) { @@ -68,9 +68,13 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { append(cmd.textinput, strings::dup(inputline)); }; - case 'q', 'v' => - if (!strings::hassuffix(cmd.arg2, '\\')) - yield; + case 'g', 'v' => + if (!strings::hassuffix(cmd.arg2, '\\')) { + append(cmd.textinput, strings::dup(cmd.arg2)); + yield :input; + }; + append(cmd.textinput, + strings::dup(strings::rtrim(cmd.arg2, '\\'))); for (true) { let inputline = match (bufio::scan_line(input)?) { @@ -79,12 +83,24 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = { case io::EOF => break; }; - - // TODO: parse and append to .subcmds ? - append(cmd.textinput, strings::dup(inputline)); + // workaround scan_line undelimited last line. + // note, GNU ed would use "p\n" instead. + if (inputline == "") { + append(cmd.textinput, strings::dup("\n")); + break; + }; + if (!strings::hassuffix(inputline, '\\')) { + append(cmd.textinput, strings::dup(inputline)); + break; + }; + append(cmd.textinput, + strings::dup(strings::rtrim(inputline, '\\'))); }; }; + for (let i = 0z; i < len(cmd.textinput); i += 1) + debug("parse() cmd.textinput[{}]=<{}>", i, cmd.textinput[i]); + return cmd; };