diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 19:03:48 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 4cea4751960b359561997cb040f05179923cb80e (patch) | |
| tree | 19834dde787a2fc4315211f55c20421f183b79d7 /src/sam_edit.zig | |
| parent | 8ce3e3c62a22d0e70df4f241ff28de6f6cae78d3 (diff) | |
| download | pardes-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.zig | 36 |
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)); |
