commit d63c3dd4a73f41d3a2bd76ea18638ca38eb67428
parent 307231e7325386b4c74130c8d4f65eb44bdd6776
Author: Byron Torres <b@torresjrjr.com>
Date: Mon, 15 Jan 2024 22:06:08 +0000
cmd_substitute(): impl multiline replacement text
Diffstat:
M | command.ha | | | 32 | ++++++++++++++++++++------------ |
M | parse.ha | | | 71 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
2 files changed, 78 insertions(+), 25 deletions(-)
diff --git a/command.ha b/command.ha
@@ -14,6 +14,7 @@ type Command = struct{
name: rune,
suffix: rune,
printmode: PrintMode,
+ delim: rune,
arg1: str,
arg2: str,
arg3: str,
@@ -472,12 +473,13 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = {
)?;
assert_nonzero(s, a)?;
- const re = newregex(s, cmd.arg1)?; defer regex::finish(&re);
- const replacement = cmd.arg2;
- // TODO: parse and use substitution flags `cmd.arg3`
- // use regex::replace ?
// TODO: implement '&', '%'
- let count = if (cmd.count == 0z) 0z else cmd.count - 1z;
+
+ const re = newregex(s, cmd.arg1)?;
+ defer regex::finish(&re);
+ const replacement = strings::join("\n", cmd.textinput...);
+ defer free(replacement);
+ const 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;
@@ -504,16 +506,22 @@ fn cmd_substitute(s: *Session, cmd: *Command) (void | Error) = {
break;
};
memio::concat(&new, strings::sub(old, rbound, strings::end))!;
-
- let newline = alloc(Line{
- text = strings::dup(memio::string(&new)!),
- ...
- });
+ let newtext = memio::string(&new)!;
+ let newtexts = strings::split(newtext, "\n");
buf_delete(s.buf, n, n);
- buf_insert(s.buf, n, newline);
- s.buf.cursor = n;
+ for (let i = 0z; i < len(newtexts); i += 1) {
+ let newtext = newtexts[i];
+ let newline = alloc(Line{
+ text = strings::dup(newtext),
+ ...
+ });
+
+ buf_insert(s.buf, n + i, newline);
+
+ s.buf.cursor = n + i;
+ };
};
printmode(s, cmd)?;
diff --git a/parse.ha b/parse.ha
@@ -45,7 +45,7 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = {
cmd.addrs = scan_addrs(&iter);
cmd.name = scan_cmdname(&iter)?;
- parse_cmdargs(&cmd, &iter)?;
+ let ready = parse_cmdargs(&cmd, &iter)?;
switch :input (cmd.name) {
case => void;
@@ -91,6 +91,44 @@ fn parse(input: *bufio::scanner) (Command | ParseError | InteractionError) = {
append(cmd.textinput,
strings::dup(strings::rtrim(inputline, '\\')));
};
+ case 's' =>
+ if (ready)
+ yield;
+
+ for (true) {
+ let inputline = match (bufio::scan_line(input)?) {
+ case let s: const str =>
+ yield s;
+ case io::EOF =>
+ break;
+ };
+ // workaround scan_line undelimited last line.
+ // note, GNU ed would use "p\n" instead.
+ if (inputline == "") {
+ append(cmd.textinput, "\n");
+ break;
+ };
+
+ let iter = strings::iter(inputline);
+ let (part, seen_delim) = scan_item(&iter, cmd.delim);
+
+ // cmd.textinput holds the replacement text
+ append(cmd.textinput, strings::dup(part));
+
+ if (!seen_delim && strings::hassuffix(inputline, '\\'))
+ continue;
+
+ strings::next(&iter); // skip delim
+ let (count, global, printmode) = scan_substitute_flags(&iter);
+ //debug("count={} global={}", count, global);
+ cmd.count = count;
+ cmd.flag_global = global;
+ cmd.printmode = printmode;
+
+ scan_end_assert(&iter)?;
+
+ break;
+ };
};
for (let i = 0z; i < len(cmd.textinput); i += 1)
@@ -159,7 +197,7 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) =
// ./<regex>[/] where delimiter '/' is arbitrary
case 'G', 'V' =>
- const delim = match (strings::next(iter)) {
+ cmd.delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -169,14 +207,14 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) =
yield r;
};
};
- cmd.arg1 = scan_item(iter, delim);
+ cmd.arg1 = scan_item(iter, cmd.delim).0;
strings::next(iter); // scan delimiter if exists
scan_end_assert(iter)?;
return true;
// ./<regex>/<cmdlist...>
case 'g', 'v' =>
- const delim = match (strings::next(iter)) {
+ cmd.delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -186,7 +224,7 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) =
yield r;
};
};
- cmd.arg1 = scan_item(iter, delim);
+ cmd.arg1 = scan_item(iter, cmd.delim).0;
strings::next(iter); // scan delimiter if exists
cmd.arg2 = scan_rest(iter); // TODO: append to .subcmds?
if (strings::prev(iter) as rune == '\\') {
@@ -196,7 +234,7 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) =
// s/<regex>/[<replace>[/[<flags>]]]
case 's' =>
- const delim = match (strings::next(iter)) {
+ cmd.delim = match (strings::next(iter)) {
case void =>
return ExpectedArgument;
case let r: rune =>
@@ -206,22 +244,26 @@ fn parse_cmdargs(cmd: *Command, iter: *strings::iterator) (bool | ParseError) =
yield r;
};
};
- cmd.arg1 = scan_item(iter, delim);
+ cmd.arg1 = scan_item(iter, cmd.delim).0;
match (strings::next(iter)) {
case rune => void;
case void =>
return ExpectedDelimiter;
};
- cmd.arg2 = scan_item(iter, delim);
+ append(cmd.textinput, scan_item(iter, cmd.delim).0);
match (strings::next(iter)) {
case rune => void;
case void =>
- return true;
+ if (strings::prev(iter) == '\\')
+ return false
+ else
+ return true;
};
let (count, global, printmode) = scan_substitute_flags(iter);
cmd.count = count;
cmd.flag_global = global;
cmd.printmode = printmode;
+ scan_end_assert(iter)?;
return true;
case '&' =>
@@ -330,14 +372,14 @@ fn scan_addr(iter: *strings::iterator) (Address | void) = {
yield scan_mark(iter);
case '/' =>
const rad = RegexAddr{
- expr = scan_item(iter, '/'),
+ expr = scan_item(iter, '/').0,
direction = true,
};
strings::next(iter);
yield rad;
case '?' =>
const rad = RegexAddr{
- expr = scan_item(iter, '?'),
+ expr = scan_item(iter, '?').0,
direction = false,
};
strings::next(iter);
@@ -468,8 +510,9 @@ fn scan_rest(iter: *strings::iterator) str = {
return strings::trim(strings::fromrunes(rs));
};
-fn scan_item(iter: *strings::iterator, delim: rune) str = {
+fn scan_item(iter: *strings::iterator, delim: rune) (str, bool) = {
let rs: []rune = [];
+ let seen_delim = false;
for (true) {
let r = match (strings::next(iter)) {
case void =>
@@ -483,6 +526,7 @@ fn scan_item(iter: *strings::iterator, delim: rune) str = {
break; // TODO: Error here? how?
case let r: rune =>
if (r == delim) {
+ seen_delim = true;
append(rs, r);
} else {
append(rs, ['\\', r]...);
@@ -490,12 +534,13 @@ fn scan_item(iter: *strings::iterator, delim: rune) str = {
continue;
};
} else if (r == delim) {
+ seen_delim = true;
strings::prev(iter);
break;
};
append(rs, r);
};
- return strings::fromrunes(rs);
+ return (strings::fromrunes(rs), seen_delim);
};
fn scan_mark(iter: *strings::iterator) rune = {