diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/pane.zig | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 7526075b..fef0f85f 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -231,14 +231,22 @@ pub fn setDot(pane: *Pane, r: State.Range) void { pane.ensureCursorVisible(); } +/// Scrolls `off` into view and nothing else: the cursor is dot's head, and +/// a write shown is not a write that moves dot (acme's textinsert leaves +/// dot where its rule puts it; only the view follows the text). pub fn showOffset(pane: *Pane, off: usize) void { const text = bodyOf(pane); const c = positionAt(pane, text, @min(off, text.len)); + // Where dot is now, kept: pinned, so the view moving under it does not + // move it to the view's top (pinCursor). + pane.pinCursor(); + const row = pane.body.cur_row; + const col = pane.body.cur_col; pane.body.cur_row = @intCast(c.row); pane.body.cur_col = @intCast(c.col); - pane.body.cur_pinned = true; - pane.body.sticky_col = -1; pane.ensureCursorVisible(); + pane.body.cur_row = row; + pane.body.cur_col = col; } pub fn clampAddr(pf: *State, len: usize) void { @@ -1350,6 +1358,39 @@ test "after a failed address, one that goes from it says there is none" { try testing.expectEqual(Status.ok, wr(p, addr, ".").reply.status); } +test "a 9P write moves dot only as acme's textinsert does, and scrolls only the view" { + const Case = struct { at: u32, want: State.Range }; + // dot is `cd` of `abcdef\n` (2..4); two bytes go in at `at`. + for ([_]Case{ + .{ .at = 0, .want = .{ .q0 = 4, .q1 = 6 } }, // before: shifted + .{ .at = 2, .want = .{ .q0 = 2, .q1 = 6 } }, // at its start: grown + .{ .at = 3, .want = .{ .q0 = 2, .q1 = 6 } }, // inside: grown + .{ .at = 4, .want = .{ .q0 = 2, .q1 = 4 } }, // at its end: left + .{ .at = 6, .want = .{ .q0 = 2, .q1 = 4 } }, // after: left + }) |c| { + const p = try withFile(testing.allocator, "abcdef\n"); + defer p.deinit(); + const serial = serialOf(p); + const pane = p.panes[p.paneBySerial(serial).?].?; + setDot(pane, .{ .q0 = 2, .q1 = 4 }); + var addr: [16]u8 = undefined; + _ = wr(p, Node.of(serial, .addr), try std.fmt.bufPrint(&addr, "#{d}", .{c.at})); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .data), "XX").reply.status); + try testing.expectEqual(c.want, dotOf(pane)); + } + // A body append leaves dot, and a sel write after it replaces dot, not + // the text appended. + const p = try withFile(testing.allocator, "abcdef\n"); + defer p.deinit(); + const serial = serialOf(p); + const pane = p.panes[p.paneBySerial(serial).?].?; + setDot(pane, .{ .q0 = 2, .q1 = 4 }); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .body), "more\n").reply.status); + try testing.expectEqual(State.Range{ .q0 = 2, .q1 = 4 }, dotOf(pane)); + try testing.expectEqual(Status.ok, wr(p, Node.of(serial, .sel), "YY").reply.status); + try testing.expectEqualStrings("abYYef\nmore\n", fileOf(pane).?.content); +} + test "a write of two lines to name is refused EINVAL, on a held open or not" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); |
