summaryrefslogtreecommitdiff
path: root/src/sam_edit.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 19:03:48 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit4cea4751960b359561997cb040f05179923cb80e (patch)
tree19834dde787a2fc4315211f55c20421f183b79d7 /src/sam_edit.zig
parent8ce3e3c62a22d0e70df4f241ff28de6f6cae78d3 (diff)
downloadpardes-4cea4751960b359561997cb040f05179923cb80e.tar.gz
pardes-4cea4751960b359561997cb040f05179923cb80e.zip
A write of an Edit block reaches Edit whole, on a pane's ctl, the root's and exec
Every write was split into lines, so a { group or an a/c/i text block written to ctl or exec became lines of their own: i/.../ and } ran as shell command panes that exited 127. An Edit line now takes the lines after it while its group is open or its text block waits for its . line (sam_edit.needsMore, asking sam's parser), on all three; the root ctl takes Edit at the active pane. s/re/text/N counts as sN/re/text/ does. The docs and the skill show a block written by heredoc or env printf, since bash's printf writes line by line, and take round 5's doc list: get refusing once, the (xN) on a repeated err, limit bounding only a forward search's end, line:col's clamping, and $NINE_MOUNT under 9ns --unix. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/sam_edit.zig')
-rw-r--r--src/sam_edit.zig36
1 files changed, 35 insertions, 1 deletions
diff --git a/src/sam_edit.zig b/src/sam_edit.zig
index e8e90b67..679823da 100644
--- a/src/sam_edit.zig
+++ b/src/sam_edit.zig
@@ -82,6 +82,19 @@ pub fn run(arena: std.mem.Allocator, text: []const u8, dot: Range, name: []const
return .{ .ops = ex.ops.items, .dot = d, .printed = ex.out.items };
}
+/// Whether `command` stops inside a `{` group or an `a`, `c` or `i` text
+/// block still waiting for its `.` line: a writer of lines (a ctl, exec)
+/// hands an Edit the lines after it until it does not.
+pub fn needsMore(arena: std.mem.Allocator, command: []const u8) bool {
+ var why: Why = .{};
+ const src = std.fmt.allocPrint(arena, "{s}\n", .{command}) catch return false;
+ var ps: Parser = .{ .arena = arena, .src = src, .why = &why };
+ while (true) {
+ const c = ps.parse(0) catch return std.mem.eql(u8, why.text(), "unmatched `{'") or ps.open_text;
+ if (c == null) return ps.open_text;
+ }
+}
+
fn fail(why: *Why, comptime fmt: []const u8, args: anytype) Failure {
why.len = if (std.fmt.bufPrint(&why.buf, fmt, args)) |s| s.len else |_| 0;
return error.Edit;
@@ -97,6 +110,8 @@ const Parser = struct {
lastpat: []const u8 = "",
/// The last `parse` that answered null met a `}`, not the end.
braced: bool = false,
+ /// A text block of `a`, `c` or `i` ran out before its `.` line.
+ open_text: bool = false,
why: *Why,
fn getch(ps: *Parser) ?u8 {
@@ -250,6 +265,7 @@ const Parser = struct {
try s.appendSlice(ps.arena, line);
try s.append(ps.arena, '\n');
}
+ ps.open_text = true;
return s.items;
}
const delim = ps.getch() orelse return "";
@@ -292,7 +308,11 @@ const Parser = struct {
if (ps.nextc() == 'g') {
ps.i += 1;
cmd.global = true;
- }
+ } else if (ps.nextc()) |d| if (d >= '1' and d <= '9') {
+ // `s/a/b/2`, the count after, as some write it for
+ // sam's `s2/a/b/`.
+ cmd.num = ps.getnum(1);
+ };
}
try ps.atnl();
},
@@ -636,6 +656,7 @@ test "sam's classic commands" {
try expectEdit("aaa\n", ",s/a/b/g", "bbb\n");
try expectEdit("aaa\n", ",s/a/b/", "baa\n");
try expectEdit("aaa\n", ",s2/a/b/", "aba\n");
+ try expectEdit("aaa\n", ",s/a/b/2", "aba\n");
try expectEdit("cat dog\n", ",s/[a-z]+/<&>/g", "<cat> <dog>\n");
// x within x, g within x.
try expectEdit("int a;\nfloat b;\nint c;\n", ",x/.*\\n/g/^int/x/[a-z];/c/X;/", "int X;\nfloat b;\nint X;\n");
@@ -676,6 +697,19 @@ test "an Edit that fails halfway changes nothing, and says why in acme's words"
}
}
+test "an Edit wants the lines after it until its braces and text blocks close" {
+ var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena_state.deinit();
+ const a = arena_state.allocator();
+ try std.testing.expect(needsMore(a, ",x/foo/{"));
+ try std.testing.expect(needsMore(a, ",x/foo/{\ni/[/"));
+ try std.testing.expect(!needsMore(a, ",x/foo/{\ni/[/\n}"));
+ try std.testing.expect(needsMore(a, "$a\nline one"));
+ try std.testing.expect(!needsMore(a, "$a\nline one\n."));
+ try std.testing.expect(!needsMore(a, ",s/{/x/"));
+ try std.testing.expect(!needsMore(a, ",x/foo/c/bar/"));
+}
+
test "the dot an Edit leaves selects what a change put where it stood" {
const ops = [_]Op{.{ .q0 = 2, .q1 = 4, .text = "XYZ" }};
try std.testing.expectEqual(Range{ .q0 = 2, .q1 = 5 }, moveDot(.{ .q0 = 2, .q1 = 2 }, &ops));