ed

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

commit c6c939e39da87ef9e4e3627a2fd7819de2c6adc9
parent db68192e4315d5f2b5501ee0c124329c654aced3
Author: Byron Torres <b@torresjrjr.com>
Date:   Thu, 28 Dec 2023 16:50:36 +0000

use Error again; draft global commands

Diffstat:
Mcommand.ha | 136++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mparse.ha | 26+++++++++++++++++++++-----
2 files changed, 118 insertions(+), 44 deletions(-)

diff --git a/command.ha b/command.ha @@ -1,3 +1,4 @@ +use bufio; use errors; use fmt; use fs; @@ -20,7 +21,7 @@ type Command = struct { subcmds: []Command, }; -type CommandFn = *fn(*Session, *Command) (void | CmdError); +type CommandFn = *fn(*Session, *Command) (void | Error); type PrintMode = enum { NONE, @@ -60,7 +61,7 @@ fn lookupcmd(name: rune) CommandFn = { case 'E' => return &cmd_edit_forced; case 'f' => return &cmd_filename; case 'g' => return &cmd_global; - case 'G' => return &cmd_global_manual; + case 'G' => return &cmd_global_interative; case 'h' => return &cmd_help; case 'H' => return &cmd_helpmode; case 'i' => return &cmd_insert; @@ -78,7 +79,7 @@ fn lookupcmd(name: rune) CommandFn = { case 't' => return &cmd_copy; case 'u' => return &cmd_undo; case 'v' => return &cmd_invglobal; - case 'V' => return &cmd_invglobal_manual; + case 'V' => return &cmd_invglobal_interative; case 'w' => return &cmd_write; case '=' => return &cmd_linenumber; case '!' => return &cmd_shellescape; @@ -87,7 +88,7 @@ fn lookupcmd(name: rune) CommandFn = { }; }; -fn cmd_append(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_append(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum(cmd.linenums, s.buf.cursor); for (let i = 0z; i < len(cmd.input); i += 1) { @@ -99,7 +100,7 @@ fn cmd_append(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = n + len(cmd.input); }; -fn cmd_change(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_change(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -132,7 +133,7 @@ fn cmd_change(s: *Session, cmd: *Command) (void | CmdError) = { }; }; -fn cmd_delete(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_delete(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -151,7 +152,7 @@ fn cmd_delete(s: *Session, cmd: *Command) (void | CmdError) = { }; }; -fn cmd_edit(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_edit(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; if (s.buf.modified && !s.warned) { @@ -184,9 +185,9 @@ fn cmd_edit(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = len(s.buf.lines) - 1; }; -fn cmd_edit_forced(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_edit_forced(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_filename(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_filename(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; if (len(cmd.arg) != 0) { s.buf.filename = cmd.arg; @@ -197,26 +198,83 @@ fn cmd_filename(s: *Session, cmd: *Command) (void | CmdError) = { fmt::println(s.buf.filename)!; }; -fn cmd_global(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_global(s: *Session, cmd: *Command) (void | Error) = { + const (a, b) = get_range( + s, + &cmd.linenums, + addr_linenum(&s.buf, 1)?, + addr_lastline(&s.buf), + )?; + assert_nonzero(s, a)?; + + void; +}; -fn cmd_global_manual(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_global_interative(s: *Session, cmd: *Command) (void | Error) = { + 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.arg)?; defer regex::finish(&regex); + + for (let i = a; i <= b; i += 1) { + const line = s.buf.lines[i]; + if (regex::test(&regex, line.text)) { + line.globalmark = true; + }; + }; + + 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; + }; + }; + const line = s.buf.lines[n]; + + fmt::println(line.text)!; + s.buf.cursor = n; + + let cmd = Command { ... }; + + const rawinput = match (bufio::read_line(os::stdin)?) { + case let bs: []u8 => + yield bs; + case io::EOF => + abort(); // TODO: ? + }; + defer free(rawinput); + const input = strings::fromutf8(rawinput)!; + + parse(&cmd, input)?; + execute(s, &cmd)?; + }; +}; -fn cmd_help(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_help(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; - if (s.lasterror is CmdError) { - fmt::println(strerror(s.lasterror as CmdError))!; + if (s.lasterror is Error) { + fmt::println(strerror(s.lasterror as Error))!; }; }; -fn cmd_helpmode(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_helpmode(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; s.helpmode = !s.helpmode; - if (s.helpmode && s.lasterror is CmdError) { - fmt::println(strerror(s.lasterror as CmdError))!; + if (s.helpmode && s.lasterror is Error) { + fmt::println(strerror(s.lasterror as Error))!; }; }; -fn cmd_insert(s: *Session, cmd: *Command) (void | CmdError) = { +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; @@ -233,7 +291,7 @@ fn cmd_insert(s: *Session, cmd: *Command) (void | CmdError) = { }; }; -fn cmd_join(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_join(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -267,7 +325,7 @@ fn cmd_join(s: *Session, cmd: *Command) (void | CmdError) = { buf_insert(&s.buf, a, newline); }; -fn cmd_mark(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_mark(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum(cmd.linenums, s.buf.cursor); assert_nonzero(s, n)?; @@ -304,7 +362,7 @@ fn cmd_mark(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.lines[n].mark = mark; }; -fn cmd_list(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_list(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -318,7 +376,7 @@ fn cmd_list(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = b; }; -fn cmd_move(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_move(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -364,7 +422,7 @@ fn cmd_move(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = dest - 1 + len(ls); }; -fn cmd_number(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_number(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -378,7 +436,7 @@ fn cmd_number(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = b; }; -fn cmd_print(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_print(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -391,16 +449,16 @@ fn cmd_print(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = b; }; -fn cmd_prompt(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_prompt(s: *Session, cmd: *Command) (void | Error) = { assert_noaddrs(s, cmd.linenums)?; s.promptmode = !s.promptmode; }; -fn cmd_quit(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_quit(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_quit_forced(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_quit_forced(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_read(s: *Session, cmd: *Command) (void | CmdError) = { +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; @@ -423,7 +481,7 @@ fn cmd_read(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = len(s.buf.lines) - 1; }; -fn cmd_substitute(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -433,11 +491,11 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | CmdError) = { assert_nonzero(s, a)?; const replacement = cmd.arg2; - const re = regex::compile(cmd.arg)?; defer regex::finish(&re); + const regex = regex::compile(cmd.arg)?; defer regex::finish(&regex); for (let i = a; i <= b; i += 1) { const old = s.buf.lines[i].text; - const results = regex::findall(&re, old); + const results = regex::findall(&regex, old); defer regex::result_freeall(results); if (len(results) == 0) { @@ -469,7 +527,7 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | CmdError) = { }; }; -fn cmd_copy(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_copy(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -499,13 +557,13 @@ fn cmd_copy(s: *Session, cmd: *Command) (void | CmdError) = { s.buf.cursor = dest - 1 + len(ls); }; -fn cmd_undo(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_undo(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_invglobal(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_invglobal(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_invglobal_manual(s: *Session, cmd: *Command) (void | CmdError) = void; +fn cmd_invglobal_interative(s: *Session, cmd: *Command) (void | Error) = void; -fn cmd_write(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_write(s: *Session, cmd: *Command) (void | Error) = { const (a, b) = get_range( s, &cmd.linenums, @@ -544,12 +602,12 @@ fn cmd_write(s: *Session, cmd: *Command) (void | CmdError) = { }; }; -fn cmd_linenumber(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_linenumber(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum(cmd.linenums, addr_lastline(&s.buf)); fmt::println(n)!; }; -fn cmd_shellescape(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_shellescape(s: *Session, cmd: *Command) (void | Error) = { let iter = strings::iter(cmd.arg); let new = memio::dynamic(); defer io::close(&new)!; let preview = false; @@ -613,7 +671,7 @@ fn cmd_shellescape(s: *Session, cmd: *Command) (void | CmdError) = { s.prev_shcmd = shcmdline; }; -fn cmd_null(s: *Session, cmd: *Command) (void | CmdError) = { +fn cmd_null(s: *Session, cmd: *Command) (void | Error) = { const n = get_linenum( cmd.linenums, addr_nextline(&s.buf, s.buf.cursor), diff --git a/parse.ha b/parse.ha @@ -3,7 +3,7 @@ use regex; use strconv; use strings; -type ParseError = ( +type ParseError = !( UnknownCommand | UnexpectedSuffix | TrailingCharacters @@ -109,6 +109,26 @@ fn parse(cmd: *Command, input: str) (bool | ParseError) = { scan_end_assert(&iter)?; return true; + // ./<regex>/<cmdlist...> + case 'g', 'v' => + const delim = match (strings::next(&iter)) { + case void => + return ExpectedArgument; + case let r: rune => + yield if (r == ' ') { + return InvalidDelimiter; + } else { + yield r; + }; + }; + cmd.arg = scan_item(&iter, delim); + strings::next(&iter); // scan delimiter if exists + cmd.arg2 = scan_rest(&iter); + if (strings::prev(&iter) as rune == '\\') { + return false; + }; + return true; + // s/<regex>/[<replace>[/[<flags>]]] case 's' => const delim = match (strings::next(&iter)) { @@ -136,10 +156,6 @@ fn parse(cmd: *Command, input: str) (bool | ParseError) = { cmd.arg3 = scan_rest(&iter); // TODO: scan properly here? return true; - // ./<regex>/<cmdlist...> - case 'g', 'v' => - abort("TODO: parse: global, invglobal"); - case => abort(); };