summaryrefslogtreecommitdiff
path: root/src/sam_edit.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 04:13:46 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit7c59482436656443b698e867c423b27dd365a96c (patch)
treecffb028a9682b8fadf41efd4793f668f2e6151fc /src/sam_edit.zig
parent21f322a119ca688a2bfec6402333432e4ec60d1d (diff)
downloadpardes-7c59482436656443b698e867c423b27dd365a96c.tar.gz
pardes-7c59482436656443b698e867c423b27dd365a96c.zip
An Edit text block is found in linear time: only a . line is parsed again
Finding where an Edit block ends parsed the whole block again for every line, copying it into the scratch arena each time: a 10k-line $a block took minutes and tens of GB. sam_edit.waitsFor says what the block waits for, and while that is a text block's . line, only such a line is parsed again (editEnd, shared by Messages and completeEnd), its copy freed. A 10k-line block now takes well under a second and 50 MB of scratch. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/sam_edit.zig')
-rw-r--r--src/sam_edit.zig19
1 files changed, 19 insertions, 0 deletions
diff --git a/src/sam_edit.zig b/src/sam_edit.zig
index 95d59a9d..604e2b31 100644
--- a/src/sam_edit.zig
+++ b/src/sam_edit.zig
@@ -92,6 +92,25 @@ pub fn run(arena: std.mem.Allocator, text: []const u8, dot: Range, name: []const
/// 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.
+/// What an Edit command still waits for: nothing, the `.` line that ends
+/// an a, c or i text block, or the `}` of a group.
+pub const Wait = enum { none, text, group };
+
+/// As `needsMore`, saying which: while a text block is open only a `.`
+/// line can end it, so a caller need not parse the block again for every
+/// line of text in it (ninep/ctl.zig, editEnd), which was quadratic.
+pub fn waitsFor(arena: std.mem.Allocator, command: []const u8) Wait {
+ var why: Why = .{};
+ const src = std.fmt.allocPrint(arena, "{s}\n", .{command}) catch return .none;
+ defer arena.free(src);
+ var ps: Parser = .{ .arena = arena, .src = src, .why = &why };
+ while (true) {
+ const c = ps.parse(0) catch
+ return if (ps.open_text) .text else if (std.mem.eql(u8, why.text(), "unmatched `{'")) .group else .none;
+ if (c == null) return if (ps.open_text) .text else .none;
+ }
+}
+
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;