diff options
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/pane.zig | 29 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 8 |
2 files changed, 32 insertions, 5 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 852a9ec0..9aada3ee 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -48,6 +48,11 @@ pub const State = struct { /// follows sets it or, refused, leaves it as it was; with none, the /// open's release empties it (tree.zig releaseHandle). dot_truncated: bool = false, + /// The open whose writes to `sel` are being kept together (0: none) and + /// the text they have made so far: the next write of that open goes at + /// its end, as acme's wrselrange does. + wrsel_by: u32 = 0, + wrsel: Range = .{}, /// Opens of `event`, which hold the pane scripted. readers: u16 = 0, /// One of `readers` reads `event`; a second reading open is refused. @@ -787,12 +792,20 @@ fn writeData(p: *Pardes, req: Req, pane: *Pane) Reply { fn writeSel(p: *Pardes, req: Req, pane: *Pane) Reply { if (fileOf(pane) == null) return tree.failText(req.tag, E.INVAL, e_no_text); - const d = dotOf(pane); + const pf = &pane.fs; + // The same open's next write follows its last; any other replaces dot. + const going_on = req.handle != 0 and pf.wrsel_by == req.handle; + const d = if (going_on) State.Range{ .q0 = pf.wrsel.q1, .q1 = pf.wrsel.q1 } else dotOf(pane); const q0: usize = d.q0; const q1: usize = @max(q0, @as(usize, d.q1)); const take = spliceBody(p, pane, q0, q1, req.data) orelse return Reply.fail(req.tag, E.NOMEM); - setDot(pane, .{ .q0 = clip(q0 + take), .q1 = clip(q0 + take) }); + // What was written is selected, as acme's wrsel leaves it: `cat sel` + // reads it back. + const start = if (going_on) pf.wrsel.q0 else clip(q0); + pf.wrsel = .{ .q0 = start, .q1 = clip(q0 + take) }; + pf.wrsel_by = req.handle; + setDot(pane, pf.wrsel); return .{ .tag = req.tag, .written = @intCast(take) }; } @@ -1265,9 +1278,17 @@ test "sel reads the selection and writing sel replaces it" { try testing.expectEqual(Status.ok, wr(p, sel, "ONE").reply.status); try testing.expectEqualStrings("ONE\ntwo\n", p.panes[0].?.file.?.content); + // What was written is selected, as acme's wrsel leaves it. + try testing.expectEqualStrings("ONE", rd(p, sel, 0, 100).bytes); + // A write of another open replaces it; one open's writes run on. _ = wr(p, sel, "!"); - try testing.expectEqualStrings("ONE!\ntwo\n", p.panes[0].?.file.?.content); - try testing.expectEqual(@as(u64, 0), call(p, .{ .tag = 2, .op = .getattr, .node = sel }).reply.attr.size); + try testing.expectEqualStrings("!\ntwo\n", p.panes[0].?.file.?.content); + const hs = call(p, .{ .tag = 3, .op = .open, .node = sel, .omode = 1 }).reply.handle; + _ = call(p, .{ .tag = 4, .op = .write, .node = sel, .handle = hs, .data = "a" }); + _ = call(p, .{ .tag = 5, .op = .write, .node = sel, .handle = hs, .data = "b" }); + _ = call(p, .{ .tag = 6, .op = .release, .node = sel, .handle = hs, .opened = true }); + try testing.expectEqualStrings("ab\ntwo\n", p.panes[0].?.file.?.content); + try testing.expectEqualStrings("ab", rd(p, sel, 0, 100).bytes); } test "the flag files read and write the state acme kept behind ctl verbs" { diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index c71a706f..cc8f938a 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -843,7 +843,8 @@ fn open(p: *Pardes, req: Req, target: Target) Reply { const reader = req.omode & 3 != 1; // OWRITE is the one mode that never reads break :what switch (t.file) { .body => if (pn.isTerminal()) .{ .snapshot = null } else if (req.omode & 3 == 0) return .{ .tag = req.tag } else .writes, - .data, .xdata => if (req.omode & 3 != 0) .writes else return .{ .tag = req.tag }, + // sel too: one open's writes run on from its last (wrsel). + .data, .xdata, .sel => if (req.omode & 3 != 0) .writes else return .{ .tag = req.tag }, // Reads of these consume, so two readers would each see half // the stream; the second is refused rather than robbed. .event => if (reader and pn.fs.event_reader) return failText(req.tag, E.BUSY, e_in_use) else .{ .event = reader }, @@ -932,6 +933,11 @@ fn releaseHandle(p: *Pardes, req: Req) void { }; // The writes of one open are one undo step (pane.zig `joined`): its // close ends that step. + // A sel open's run of writes ends with it. + if (t == .pane and t.pane.file == .sel) if (p.paneBySerial(t.pane.serial)) |id| { + const pf = &p.panes[id].?.fs; + if (pf.wrsel_by == req.handle) pf.wrsel_by = 0; + }; if (t == .pane and (t.pane.file == .body or t.pane.file == .data or t.pane.file == .xdata)) if (p.paneBySerial(t.pane.serial)) |id| { const pf = &p.panes[id].?.fs; if (pf.joined_by == 0 or pf.joined_by == req.handle) pf.joined = null; |
