summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ninep/pane.zig29
-rw-r--r--src/ninep/tree.zig8
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;