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:
M | command.ha | | | 136 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
M | parse.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(®ex);
+
+ for (let i = a; i <= b; i += 1) {
+ const line = s.buf.lines[i];
+ if (regex::test(®ex, 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(®ex);
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(®ex, 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();
};