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