summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ninep/addr.zig31
-rw-r--r--src/ninep/pane.zig10
2 files changed, 38 insertions, 3 deletions
diff --git a/src/ninep/addr.zig b/src/ninep/addr.zig
index cc7ca632..6656c786 100644
--- a/src/ninep/addr.zig
+++ b/src/ninep/addr.zig
@@ -16,6 +16,7 @@ pub const e_range = "address out of range";
pub const e_regexp = "bad regular expression";
pub const e_slow = "regular expression search took too long";
pub const e_syntax = "bad address syntax";
+pub const e_order = "addresses out of order";
pub const Addr = struct {
text: []const u8,
@@ -93,7 +94,9 @@ pub const Addr = struct {
size = .line;
},
'/', '?' => {
- const back = c == '?';
+ // `-/re/` searches back as `?re?` does (acme's address()
+ // takes the direction a `-` set, addr.c).
+ const back = c == '?' or dir == '-';
r = a.regexp(r, a.pattern(c), back) orelse return null;
dir = 0;
size = .line;
@@ -243,6 +246,32 @@ const Node = tree.Node;
const E = tree.E;
const Status = tree.Status;
+test "an address is evaluated from the current one, as acme's are" {
+ const p = try th.withFile(testing.allocator, "a foo b foo c foo\n");
+ defer p.deinit();
+ const addr = Node.of(th.serialOf(p), .addr);
+ const Step = struct { expr: []const u8, q0: u32, q1: u32 };
+ _ = th.wr(p, addr, "#0");
+ for ([_]Step{
+ // each search starts where the last address ended
+ .{ .expr = "/foo/", .q0 = 2, .q1 = 5 },
+ .{ .expr = "/foo/", .q0 = 8, .q1 = 11 },
+ // `.` is that address, and `-` before a pattern searches back
+ .{ .expr = ".", .q0 = 8, .q1 = 11 },
+ .{ .expr = "-/foo/", .q0 = 2, .q1 = 5 },
+ .{ .expr = "./foo/", .q0 = 8, .q1 = 11 },
+ .{ .expr = "+/foo/", .q0 = 14, .q1 = 17 },
+ // a number then a pattern: the search starts at the number
+ .{ .expr = "#9/foo/", .q0 = 14, .q1 = 17 },
+ }) |c| {
+ try testing.expectEqual(Status.ok, th.wr(p, addr, c.expr).reply.status);
+ try testing.expectEqual(c.q0, p.panes[0].?.fs.addr.q0);
+ try testing.expectEqual(c.q1, p.panes[0].?.fs.addr.q1);
+ }
+ // A range whose end comes before its start is sam's error.
+ try testing.expectEqualStrings(e_order, th.wr(p, addr, "#10,#5").reply.ename);
+}
+
test "regular expressions search lines as sam's do, and a search wraps" {
const p = try th.withFile(testing.allocator, "alpha beta\nbeta gamma\ngamma\n");
defer p.deinit();
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 0a511118..6b6d5728 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -453,16 +453,22 @@ pub const e_addr_failed = "no address: the last one written to addr failed";
/// A range file takes an address expression, or that pair of offsets. One
/// that does not evaluate says why: `bad address syntax`, `no match for
-/// regexp`, `address out of range`, `bad regular expression`.
+/// regexp`, `address out of range`, `bad regular expression`, `addresses
+/// out of order`.
fn writeRange(req: Req, pane: *Pane, file: PaneFile) Reply {
const pf = &pane.fs;
const text = bodyOf(pane);
clampAddr(pf, text.len);
const expr = std.mem.trimEnd(u8, req.data, "\n");
var a: addressing.Addr = .{ .text = text, .lim = pf.limit, .expr = expr };
- const r = if (pairOf(expr)) |pair|
+ var r = if (pairOf(expr)) |pair|
State.Range{ .q0 = @min(pair.q0, clip(text.len)), .q1 = @min(pair.q1, clip(text.len)) }
else if (a.address(pf.addr)) |found| (if (a.i < expr.len) null else found) else null;
+ // sam's check, which acme leaves out: `#100,#50` names no range.
+ if (r) |found| if (found.q0 > found.q1) {
+ a.err = addressing.e_order;
+ r = null;
+ };
const range = r orelse {
if (file == .addr) pf.addr_failed = true;
return tree.failText(req.tag, E.INVAL, a.err);