diff options
Diffstat (limited to 'src/ninep/addr.zig')
| -rw-r--r-- | src/ninep/addr.zig | 56 |
1 files changed, 51 insertions, 5 deletions
diff --git a/src/ninep/addr.zig b/src/ninep/addr.zig index 0537c17a..1e47183b 100644 --- a/src/ninep/addr.zig +++ b/src/ninep/addr.zig @@ -91,6 +91,26 @@ pub const Addr = struct { if (d < '0' or d > '9') break; n = @min(n * 10 + (d - '0'), std.math.maxInt(u32)); } + // pardes's `line:col`, Look's `file:12:5` spelling (look.zig + // parsePathLine): the point at that column of that line, the + // column in bytes from 1, clamped to the line's end and snapped + // to a character's start as Look's cursor is. Not sam's. + if (size == .line and dir == 0 and a.i + 1 < a.expr.len and a.expr[a.i] == ':' and + a.expr[a.i + 1] >= '0' and a.expr[a.i + 1] <= '9') + { + a.i += 1; + var col: u64 = 0; + while (a.i < a.expr.len and a.expr[a.i] >= '0' and a.expr[a.i] <= '9') : (a.i += 1) + col = @min(col * 10 + (a.expr[a.i] - '0'), std.math.maxInt(u32)); + const lines = modal.cursorLineCount(a.text); + if (n == 0 or n > lines or col == 0) { + a.err = e_range; + return null; + } + const q = clip(modal.offsetAt(a.text, .{ .row = @intCast(n - 1), .col = @intCast(col - 1) })); + r = .{ .q0 = q, .q1 = q }; + continue; + } r = a.number(r, @intCast(n), dir, size) orelse return null; dir = 0; size = .line; @@ -202,8 +222,8 @@ pub const Addr = struct { a.err = "no previous regular expression"; return null; } - var rx = regexp_.Regex.compile(pat) catch { - a.err = e_regexp; + var rx = regexp_.Regex.compile(pat) catch |err| { + a.err = if (err == error.Anchor) regexp_.Regex.e_anchor else e_regexp; return null; }; const found = if (back) found: { @@ -340,13 +360,34 @@ test "a failed address leaves none, so data refuses rather than act at the last try testing.expectEqualStrings(pane_files.e_addr_failed, th.rd(p, addr, 0, 64).reply.ename); try testing.expectEqual(E.INVAL, th.call(p, .{ .tag = 1, .op = .setattr, .node = Node.of(serial, .xdata), .truncate = true }).errno()); try testing.expectEqualStrings("one\ntwo\n", p.panes[0].?.file.?.content); - // A good address, or a truncated addr, gives it one again. + // A good address gives it one again; truncating addr does not. _ = th.wr(p, addr, "#0,#3"); try testing.expectEqual(Status.ok, th.wr(p, data, "ONE").reply.status); try testing.expectEqualStrings("ONE\ntwo\n", p.panes[0].?.file.?.content); _ = th.wr(p, addr, "99"); _ = th.call(p, .{ .tag = 1, .op = .setattr, .node = addr, .truncate = true }); - try testing.expectEqual(Status.ok, th.wr(p, data, "0").reply.status); + try testing.expectEqualStrings(pane_files.e_addr_failed, th.wr(p, data, "0").reply.ename); +} + +test "a shell's > on addr searches on from the last address, and 0 starts over" { + const p = try th.withFile(testing.allocator, "foo x foo y foo\n"); + defer p.deinit(); + const serial = th.serialOf(p); + const addr = Node.of(serial, .addr); + const data = Node.of(serial, .data); + // echo /foo/ > addr; printf foobar > data -- twice over, as a loop does. + for (0..3) |_| { + _ = th.call(p, .{ .tag = 1, .op = .setattr, .node = addr, .truncate = true }); + try testing.expectEqual(Status.ok, th.wr(p, addr, "/foo/").reply.status); + _ = th.call(p, .{ .tag = 1, .op = .setattr, .node = data, .truncate = true }); + try testing.expectEqual(Status.ok, th.wr(p, data, "foobar").reply.status); + } + try testing.expectEqualStrings("foobar x foobar y foobar\n", p.panes[0].?.file.?.content); + // .+#1 moves on from where the last write left the address. + _ = th.wr(p, addr, "0"); + _ = th.call(p, .{ .tag = 1, .op = .setattr, .node = addr, .truncate = true }); + try testing.expectEqual(Status.ok, th.wr(p, addr, ".+#1").reply.status); + try testing.expectEqualStrings(" 1 1 ", th.rd(p, addr, 0, 64).bytes); } test "the address language, form by form" { @@ -372,6 +413,11 @@ test "the address language, form by form" { .{ .expr = "/two/", .q0 = 4, .q1 = 7 }, .{ .expr = "/t.o/", .q0 = 4, .q1 = 7 }, .{ .expr = "1\n", .q0 = 0, .q1 = 4 }, + // pardes's line:col, composing like any simple address. + .{ .expr = "2:2", .q0 = 5, .q1 = 5 }, + .{ .expr = "2:99", .q0 = 7, .q1 = 7 }, + .{ .expr = "2:2,3:1", .q0 = 5, .q1 = 8 }, + .{ .expr = "2:2+#3", .q0 = 8, .q1 = 8 }, }) |c| { _ = th.wr(p, addr, "#0"); const w = th.wr(p, addr, c.expr); @@ -420,7 +466,7 @@ test "the address language, form by form" { for ([_][2][]const u8{ .{ "zzz", e_syntax }, .{ "/nomatch/", e_no_match }, .{ "99", e_range }, .{ "#999", e_range }, .{ "/a[/", e_regexp }, .{ "/(a/", e_regexp }, - .{ "/*a/", e_regexp }, + .{ "/*a/", e_regexp }, .{ "99:1", e_range }, .{ "2:0", e_range }, }) |c| { _ = th.wr(p, addr, "#0"); try testing.expectEqualStrings(c[1], th.wr(p, addr, c[0]).reply.ename); |
