ed

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

commit db30ac46bf4bd6ce42f8b08495cc5643446ae202
parent 2a3d0336ce771b0ea1a7581eb9d0e650649fc54a
Author: Byron Torres <b@torresjrjr.com>
Date:   Sun, 14 Jan 2024 22:27:49 +0000

cmd_substitute(): handle flags

Diffstat:
Mcommand.ha | 9+++++++--
Mparse.ha | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 62 insertions(+), 23 deletions(-)

diff --git a/command.ha b/command.ha @@ -16,6 +16,8 @@ type Command = struct{ arg1: str, arg2: str, arg3: str, + count: size, + flag_global: bool, textinput: []str, subcmds: []Command, }; @@ -472,10 +474,10 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = { const re = newregex(s, cmd.arg1)?; defer regex::finish(&re); const replacement = cmd.arg2; - const flags = cmd.arg3; // TODO: parse and use substitution flags `cmd.arg3` // use regex::replace ? // TODO: implement '&', '%' + let count = if (cmd.count == 0z) 0z else cmd.count - 1z; for (let n = a; n <= b; n += 1) { const old = s.buf.lines[n].text; @@ -491,12 +493,15 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = { let start = 0z; let lbound = 0z; let rbound = 0z; - for (let i = 0z; i < len(results); i += 1) { + for (let i = count; i < len(results); i += 1) { lbound = results[i][0].start; rbound = results[i][0].end; memio::concat(&new, strings::sub(old, start, lbound))!; memio::concat(&new, replacement)!; start = rbound; + + if (!cmd.flag_global) + break; }; memio::concat(&new, strings::sub(old, rbound, strings::end))!; diff --git a/parse.ha b/parse.ha @@ -141,19 +141,19 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) = // .[s] where 's' is '(l|n|p)' case 'd', 'h', 'H', 'j', 'l', 'n', 'p', 'P', 'u', '=' => - cmd.printmode = scan_suffix(iter); + cmd.printmode = scan_printmode(iter); scan_end_assert(iter)?; return true; // .[s] case 'a', 'c', 'i' => - cmd.printmode = scan_suffix(iter); + cmd.printmode = scan_printmode(iter); scan_end_assert(iter)?; return false; // .[s][ ]<addr> case 'm', 't' => - cmd.printmode = scan_suffix(iter); + cmd.printmode = scan_printmode(iter); cmd.arg1 = scan_rest(iter); return true; @@ -218,7 +218,10 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) = case void => return true; }; - cmd.arg3 = scan_rest(iter); // TODO: scan properly here? + let (count, global, printmode) = scan_substitute_flags(iter); + cmd.count = count; + cmd.flag_global = global; + cmd.printmode = printmode; return true; case '&' => @@ -322,7 +325,7 @@ fn scan_addr(iter: *strings::iterator) (Address | void) = { yield LastLine; case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => strings::prev(iter); - yield scan_uint(iter): size; + yield scan_size(iter): size; case '\'' => yield scan_mark(iter); case '/' => @@ -384,7 +387,7 @@ fn scan_offsets(iter: *strings::iterator) []int = { append(offs, -scan_offset(iter)); } else if (ascii::isdigit(r)) { strings::prev(iter); - append(offs, scan_uint(iter): int); + append(offs, scan_size(iter): int); } else { strings::prev(iter); break; @@ -402,7 +405,7 @@ fn scan_offset(iter: *strings::iterator) int = { case let r: rune => strings::prev(iter); if (ascii::isdigit(r)) { - return scan_uint(iter): int; + return scan_size(iter): int; } else { return 1; }; @@ -430,7 +433,7 @@ fn scan_cmdname(iter: *strings::iterator) (rune | UnknownCommand) = { }; }; -fn scan_suffix(iter: *strings::iterator) PrintMode = { +fn scan_printmode(iter: *strings::iterator) PrintMode = { let r = match (strings::next(iter)) { case void => return PrintMode::NONE; @@ -508,11 +511,42 @@ fn scan_mark(iter: *strings::iterator) rune = { }; }; -// TODO: rename and appropriate to "scan_size()"? -fn scan_uint(iter: *strings::iterator) uint = { +fn scan_substitute_flags(iter: *strings::iterator) (size, bool, PrintMode) = { + let count = 0z; + let global = false; + let printmode = PrintMode::NONE; + + for (true) { + let r = match (strings::next(iter)) { + case void => + break; + case let r: rune => + yield r; + }; + + switch (r) { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => + strings::prev(iter); + count = scan_size(iter); + case 'g' => + global = true; + case 'p' => + printmode = PrintMode::PLAIN; + case 'n' => + printmode = PrintMode::NUMBER; + case 'l' => + printmode = PrintMode::LIST; + case => + break; + }; + }; + + return (count, global, printmode); +}; + +fn scan_size(iter: *strings::iterator) size = { + let begin = *iter; // reimplement this function using another iterator - let num: []rune = []; - defer free(num); for (true) { let r = match (strings::next(iter)) { case void => @@ -521,24 +555,24 @@ fn scan_uint(iter: *strings::iterator) uint = { yield r; }; - if (ascii::isdigit(r)) { - append(num, r); - } else { + if (!ascii::isdigit(r)) { strings::prev(iter); break; }; }; + let num = strings::slice(&begin, iter); + // TODO: return void instead? - if (len(num) == 0) { - return 0; + if (num == "") { + return 0z; }; - match (strconv::stou(strings::fromrunes(num))) { + match (strconv::stoz(num)) { case (strconv::invalid | strconv::overflow) => - abort("Invalid"); - case let u: uint => - return u; + abort("Invalid"); // TODO: propagate? + case let z: size => + return z; }; };