diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 16:51:34 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 6122159572f1cc950666df484e2074cf32f22eb5 (patch) | |
| tree | 62e0d2f960af827826e1b615777cab9a243f506d /src/ninep/ctl.zig | |
| parent | a15eabc3b9d98ef5f659ca82a2b0f2afb7451a39 (diff) | |
| download | pardes-6122159572f1cc950666df484e2074cf32f22eb5.tar.gz pardes-6122159572f1cc950666df484e2074cf32f22eb5.zip | |
A 9P look keeps its leading blanks: an indented line, or blanks alone, is looked for as written
Every line written to look or exec went through Messages.next, which
trimmed blanks from both ends. A look of ` indented` then found the
first bare `indented`, and a diff's blank context line ` ` looked at
nothing. A look's line is now its text: only its `\r` and newline go.
lookAt keeps the blanks for the word search too; it expands `~` only for
a look that starts with one, and the path, address and command readings
trim for themselves as before. runSearch keeps a pattern's blanks, since
a builtin's argument comes trimmed already. An exec line is trimmed as
before. fs.md says so.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/ctl.zig')
| -rw-r--r-- | src/ninep/ctl.zig | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 549aa265..66fa22ab 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -110,6 +110,9 @@ const Messages = struct { p: *Pardes, data: []const u8, at: usize = 0, + /// A look's line is its text, leading blanks and all (` return x`, + /// a diff's context line ` ` alone): only its `\r` goes. + keep_blanks: bool = false, fn init(p: *Pardes, data: []const u8) Messages { return .{ .p = p, .data = data }; @@ -120,6 +123,7 @@ const Messages = struct { const start = m.at; var end = std.mem.indexOfScalarPos(u8, m.data, start, '\n') orelse m.data.len; m.at = end + 1; + if (m.keep_blanks) return std.mem.trimEnd(u8, m.data[start..end], "\r"); const first = std.mem.trim(u8, m.data[start..end], " \t\r"); const edit = "Edit"; if (!std.mem.startsWith(u8, first, edit) or (first.len > edit.len and first[edit.len] != ' ' and first[edit.len] != '\t')) @@ -186,6 +190,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep for ([2]bool{ false, true }) |apply| { if (apply) p.fs.results_len = 0; var it = Messages.init(p, req.data); + it.keep_blanks = !exec; while (it.next()) |text| { if (text.len == 0) continue; // Only an Edit block holds newlines (Messages). @@ -2540,6 +2545,24 @@ test "a restored pane whose file is gone from disk comes back dirty, and Del ask try testing.expect(std.mem.indexOf(u8, r.reply.ename, "Modified") != null); } +test "a 9P look keeps its leading blanks, a blank-only look included, and exec still trims" { + const p = try withFile(testing.allocator, "a indented\nb\n indented\n \n"); + defer p.deinit(); + const serial = serialOf(p); + const pane = p.panes[p.paneBySerial(serial).?].?; + pane_files.setDot(pane, .{ .q0 = 0, .q1 = 0 }); + // The indented line, not the `indented` after `a `. + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .look), " indented\n").reply.status); + try testing.expectEqual(pane_files.State.Range{ .q0 = 13, .q1 = 25 }, pane_files.dotOf(pane)); + // Blanks alone are text to find too. + pane_files.setDot(pane, .{ .q0 = 26, .q1 = 26 }); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .look), " \n").reply.status); + try testing.expectEqual(pane_files.State.Range{ .q0 = 26, .q1 = 30 }, pane_files.dotOf(pane)); + // exec's line is still trimmed: ` Msg hi` runs Msg. + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .exec), " Msg blanks trimmed\n").reply.status); + try testing.expect(th.logHas(p, "blanks trimmed")); +} + test "a line starting with # runs as nothing at an exec or a ctl, silently" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); |
