summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.agents/skills/pardes-9p/SKILL.md3
-rw-r--r--docs/fs.md14
-rw-r--r--src/fs-help.txt2
-rw-r--r--src/ninep/addr.zig27
4 files changed, 41 insertions, 5 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md
index 302223e5..55df3091 100644
--- a/.agents/skills/pardes-9p/SKILL.md
+++ b/.agents/skills/pardes-9p/SKILL.md
@@ -170,7 +170,8 @@ Truncating `data` is pardes's own (acme ignores OTRUNC and always inserts).
`addr`, `dot` and `limit` each read the pair of offsets they also accept, which
is why copying one onto another is all that acme's `addr=dot`, `dot=addr` and
`limit=addr` ever were; a write may also be an address expression (`#0,#5`,
-`/pattern/`, `2+1`), whose regexps are mvzr's searched as sam searches:
+`/pattern/`, `2+1`, or pardes's `12:5`: line 12, byte column 5, composing as
+`12:5,14:1`), whose regexps are mvzr's searched as sam searches:
`^`/`$` match at any line's start and end, `.` and `[^...]` never match a
newline, the leftmost match wins (the first alternative there, not the
longest). An expression is evaluated from the current address (the last one
diff --git a/docs/fs.md b/docs/fs.md
index f23129ba..d746ec28 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -357,10 +357,20 @@ exec 3>&-`.
The three range files `addr`, `dot` and `limit` each read the pair of offsets
they also accept, so copying one onto another is all that acme's `addr=dot`,
`dot=addr` and `limit=addr` ever were. A write is either that pair or an
-address expression (`#0,#5`, `/pattern/`, `2+1`); `addr` selects what `data`
+address expression (`#0,#5`, `/pattern/`, `2+1`, and pardes's own `12:5`,
+below); `addr` selects what `data`
and `xdata` read or replace, `dot` is the editor's own selection and moving it
scrolls the pane into view, and `limit` bounds a search and reads empty until
-it is set. Truncating a range file empties it; truncating `limit` lifts it.
+it is set. Truncating `dot` empties it, truncating `limit` lifts it, and
+truncating `addr` leaves it as it is (below).
+
+`line:col` is a pardes extension to sam's addresses, the spelling Look
+takes in `file:12:5`: `12:5` is the point at line 12, column 5, and it
+composes like any simple address (`12:5,14:1`, `12:5+#3`). The column is
+in bytes from 1, as Look's is, clamped to the end of the line and snapped
+back to the start of the character it falls in; a line past the end, or
+column 0, is `address out of range`. sam would read `12:5` as a syntax
+error.
Truncating `data` or `xdata` deletes the range `addr` names and nothing
else, so a shell's `echo NEW > data` replaces that range, `: > data`
deletes it, and `>>` inserts at it; only truncating `body` empties the
diff --git a/src/fs-help.txt b/src/fs-help.txt
index 214986a1..324b992e 100644
--- a/src/fs-help.txt
+++ b/src/fs-help.txt
@@ -35,7 +35,7 @@ Below, $m is the mount: $NINE_MOUNT/pardes/<pid, or NAME of --detach=NAME>; PARD
Pitfalls, one each:
Each open of pane/new makes another pane (ls, stat, find never do); rmdir closes even a dirty one.
- addr: #n,#m or /regexp/ from the current address (^ $ per line); > never resets it, 0 does; a miss refuses data.
+ addr: #n,#m, 12:5 or /re/ from the current address (^ $ per line); > never resets it, 0 does; a miss refuses data.
data: > replaces the addr range, : > deletes it, a 2nd > inserts (addr moved); body: > empties all.
tag reads the path, then its own text; > replaces that text (default words too), >> appends.
A terminal's body is a history snapshot frozen per open; pty/data is the live stream.
diff --git a/src/ninep/addr.zig b/src/ninep/addr.zig
index ccb08924..f57c62bd 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;
@@ -393,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);
@@ -441,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);