diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/pane.zig | 36 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 8 |
2 files changed, 41 insertions, 3 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 13381066..9d20c587 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -63,6 +63,11 @@ pub const State = struct { lock: ?u32 = null, /// The host started a shell it could not teach to mark its prompts. unmarked: bool = false, + /// `tag` was truncated (`echo X > tag`): until that open is released, + /// the newline that ends a write is held back, so the one that ends the + /// whole of it is dropped and draws no empty row. + tag_rewrite: bool = false, + tag_held_newline: bool = false, /// The pty's size as pty/ctl's winsize last set it, until the pane's own /// grid resizes and gives the pty its size again. winsize: ?[2]u16 = null, @@ -427,11 +432,23 @@ fn writeTag(req: Req, pane: *Pane) Reply { if (req.data.len == 0) return .{ .tag = req.tag, .written = 0 }; // The tag's own text grows by what is written, newlines and all: a tag // is a text like any other (acme's tag file appends the same way). + const pf = &pane.fs; const had = tagline.curTail(pane).len; - const room = limits.max_tag_tail -| had; + const room = limits.max_tag_tail -| had -| @intFromBool(pf.tag_held_newline); if (room == 0) return Reply.fail(req.tag, E.NOSPC); const take = wholeUtf8(req.data[0..@min(req.data.len, room)]); - pane.appendTag(req.data[0..take]) catch return Reply.fail(req.tag, E.NOMEM); + // A truncating write drops ONE trailing newline, its whole text's: a + // newline held from a write before goes in once more text follows it. + if (pf.tag_held_newline) { + pane.appendTag("\n") catch return Reply.fail(req.tag, E.NOMEM); + pf.tag_held_newline = false; + } + var text = req.data[0..take]; + if (pf.tag_rewrite and text.len > 0 and text[text.len - 1] == '\n') { + text = text[0 .. text.len - 1]; + pf.tag_held_newline = true; + } + pane.appendTag(text) catch return Reply.fail(req.tag, E.NOMEM); return .{ .tag = req.tag, .written = @intCast(take) }; } @@ -648,6 +665,8 @@ pub fn truncate(p: *Pardes, pane: *Pane, file: PaneFile) tree.Status { pane.tag.cur_col = if (tagline.pathPrefix(p.scratch.allocator(), pane)) |prefix| @intCast(prefix.len) else |_| 0; pane.tag.vsel.active = false; pane.tag.nsel = 0; + pf.tag_rewrite = true; + pf.tag_held_newline = false; }, // A shell's `>` truncates before it writes: the address written // is evaluated from where the last one left off, as with `>>`. @@ -977,6 +996,19 @@ test "truncating the tag clears its editable tail" { // and a click on the G puts the tag's cursor on it const at = tagline.tagOffsetAt(p, pane, 0, "/test.txt ".len).?; try testing.expectEqualStrings("Get", whole[at.col..]); + + // `echo Make > tag`: the newline ending the truncating write's text is + // dropped, one only; a newline between its writes stays. + _ = call(p, .{ .tag = 3, .op = .release, .node = tag }); + _ = call(p, .{ .tag = 4, .op = .setattr, .node = tag, .truncate = true }); + _ = wr(p, tag, "Make\n"); + try testing.expectEqualStrings("Make", pane.tag.own.?); + _ = wr(p, tag, "More\n\n"); + try testing.expectEqualStrings("Make\nMore\n", pane.tag.own.?); + _ = call(p, .{ .tag = 5, .op = .release, .node = tag }); + // `>>` keeps what it is given. + _ = wr(p, tag, " Tail\n"); + try testing.expectEqualStrings("Make\nMore\n Tail\n", pane.tag.own.?); } test "name reads the file name and writing it promotes a scratch without touching its body" { diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index ad95f6bf..e5320f61 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -676,8 +676,14 @@ fn release(p: *Pardes, req: Req) Reply { /// its releases before the connection's slot is reused, so none outlives /// the connection it came on. fn releaseHandle(p: *Pardes, req: Req) void { - const o = openOf(p, req) orelse return; const t = Node.target(req.node).?; + // A truncating write to `tag` is over when its open goes (pane.zig + // writeTag): a newline it held back is dropped. + if (t == .pane and t.pane.file == .tag) if (p.paneBySerial(t.pane.serial)) |id| { + p.panes[id].?.fs.tag_rewrite = false; + p.panes[id].?.fs.tag_held_newline = false; + }; + const o = openOf(p, req) orelse return; const of: ?*Pane = if (t == .pane) (if (p.paneBySerial(t.pane.serial)) |id| p.panes[id] else null) else null; if (of) |pn| switch (o.what) { .event => |reader| { |
