ed

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

commit 1778577cd71cc38d122376684360a0ce1520d402
parent 66828105d090f28900ccf2703d05d112c4f33abb
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun,  7 Jan 2024 01:07:17 +0000

add cmd_invglobal(), global.ha

Diffstat:
MMakefile | 1+
Mcommand.ha | 189++++---------------------------------------------------------------------------
Aglobal.ha | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 199 insertions(+), 181 deletions(-)

diff --git a/Makefile b/Makefile @@ -7,6 +7,7 @@ source=\ buffer.ha \ command.ha \ execute.ha \ + global.ha \ interaction.ha \ parse.ha \ util.ha \ diff --git a/command.ha b/command.ha @@ -1,4 +1,3 @@ -use bufio; use errors; use fmt; use fs; @@ -8,7 +7,6 @@ use os; use os::exec; use regex; use strings; -use types; type Command = struct{ addrs: []Address, @@ -253,186 +251,11 @@ fn cmd_filename(s: *Session, cmd: *Command) (void | Error) = { }; fn cmd_global(s: *Session, cmd: *Command) (void | Error) = { - s.warned = false; - - const (a, b) = get_range( - s, - &cmd.linenums, - addr_linenum(s.buf, 1)?, - addr_lastline(s.buf), - )?; - assert_nonzero(s, a)?; - - 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.name=<{}>", subcmd.name); - - // TODO: write new parse_global() function instead - // as to avoid reading multiple lines in the case of - // 'a', 'c', 'i' ? - switch (subcmd.name) { - case 'g', 'G', 'v', 'V', '!' => - // TODO: implement? - errormsg(s, subcmd.name: 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{ name = '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; - }; + global(s, cmd, true)?; }; fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { - s.warned = false; - - const (a, b) = get_range( - s, - &cmd.linenums, - addr_linenum(s.buf, 1)?, - addr_lastline(s.buf), - )?; - assert_nonzero(s, a)?; - - 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 prevsubcmd: (void | Command) = void; // TODO: handle mem - - 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]; - - fmt::println(line.text)!; - - // TODO: handle termination by SIGINT signal - - // TODO: handle mem - // TODO: just use 'parse()?' when compiler allows - //let subcmd = parse(s.input)?; - let subcmd = match (parse(s.input)) { - case let c: Command => - yield c; - case let e: ParseError => - return e; - case let e: InteractionError => - return e; - }; - - // TODO: write new parse_global_interactive() function instead - // as to avoid reading multiple lines in the case of - // 'a', 'c', 'i' ? - switch (subcmd.name) { - case 'a', 'c', 'i', 'g', 'G', 'v', 'V' => - errormsg(s, subcmd.name: InvalidGlobalSubCmd); - continue; - case NUL => - line.globalmark = false; - continue; - case '&' => - match (prevsubcmd) { - case void => - return NoPrevGlobalSubCmd; - case let prevsubcmd: Command => - subcmd = prevsubcmd; - }; - case 'u' => - // TODO: undo behaviour - continue; - case => - prevsubcmd = subcmd; - }; - - execute(s, &subcmd)?; - - line.globalmark = false; - }; + global_interactive(s, cmd, true)?; }; fn cmd_help(s: *Session, cmd: *Command) (void | Error) = { @@ -771,9 +594,13 @@ fn cmd_copy(s: *Session, cmd: *Command) (void | Error) = { fn cmd_undo(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_invglobal(s: *Session, cmd: *Command) (void | Error) = void; +fn cmd_invglobal(s: *Session, cmd: *Command) (void | Error) = { + global(s, cmd, false)?; +}; -fn cmd_invglobal_interative(s: *Session, cmd: *Command) (void | Error) = void; +fn cmd_invglobal_interative(s: *Session, cmd: *Command) (void | Error) = { + global_interactive(s, cmd, false)?; +}; fn cmd_write(s: *Session, cmd: *Command) (void | Error) = { s.warned = false; diff --git a/global.ha b/global.ha @@ -0,0 +1,190 @@ +use bufio; +use fmt; +use io; +use memio; +use regex; +use strings; +use types; + +fn global(s: *Session, cmd: *Command, matched: bool) (void | Error) = { + s.warned = false; + + const (a, b) = get_range( + s, + &cmd.linenums, + addr_linenum(s.buf, 1)?, + addr_lastline(s.buf), + )?; + assert_nonzero(s, a)?; + + 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 (matched ^^ !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.name=<{}>", subcmd.name); + + // TODO: write new parse_global() function instead + // as to avoid reading multiple lines in the case of + // 'a', 'c', 'i' ? + switch (subcmd.name) { + case 'g', 'G', 'v', 'V', '!' => + // TODO: undefined specification. implement? + errormsg(s, subcmd.name: 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{ name = '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 global_interactive(s: *Session, cmd: *Command, matched: bool) (void | Error) = { + s.warned = false; + + const (a, b) = get_range( + s, + &cmd.linenums, + addr_linenum(s.buf, 1)?, + addr_lastline(s.buf), + )?; + assert_nonzero(s, a)?; + + 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 (matched ^^ !regex::test(&regex, line.text)) { + line.globalmark = true; + }; + }; + + let prevsubcmd: (void | Command) = void; // TODO: handle mem + + 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]; + + fmt::println(line.text)!; + + // TODO: handle termination by SIGINT signal + + // TODO: handle mem + // TODO: just use 'parse()?' when compiler allows + //let subcmd = parse(s.input)?; + let subcmd = match (parse(s.input)) { + case let c: Command => + yield c; + case let e: ParseError => + return e; + case let e: InteractionError => + return e; + }; + + // TODO: write new parse_global_interactive() function instead + // as to avoid reading multiple lines in the case of + // 'a', 'c', 'i' ? + switch (subcmd.name) { + case 'a', 'c', 'i', 'g', 'G', 'v', 'V' => + errormsg(s, subcmd.name: InvalidGlobalSubCmd); + continue; + case NUL => + line.globalmark = false; + continue; + case '&' => + match (prevsubcmd) { + case void => + return NoPrevGlobalSubCmd; + case let prevsubcmd: Command => + subcmd = prevsubcmd; + }; + case 'u' => + // TODO: undo behaviour + continue; + case => + prevsubcmd = subcmd; + }; + + execute(s, &subcmd)?; + + line.globalmark = false; + }; +};