diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 00:56:51 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 7c4cb0a7badecdd91e910c64b824eff68eae1b84 (patch) | |
| tree | e9de04116c61388a01f62178fa55f296246e7bd0 | |
| parent | e988d4e24bfc0b80285754359b4b86f1b15b33f6 (diff) | |
| download | pardes-7c4cb0a7badecdd91e910c64b824eff68eae1b84.tar.gz pardes-7c4cb0a7badecdd91e910c64b824eff68eae1b84.zip | |
Edit's refusals name the pattern, not the line, and say when dot is empty
A ctl Edit that missed answered `Edit: no match for regexp "Edit
/nomatch/d"`, quoting the whole program; it now names the address that
missed (`no match for regexp /nomatch/`) and quotes no line after. An s
on an empty dot says `no substitution: dot is empty`. A \\1 is blamed
only once its pattern has matched, as before. tags.md says & is special
in s and not in c, a or i, as in sam.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | docs/tags.md | 6 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 5 | ||||
| -rw-r--r-- | src/sam_edit.zig | 14 |
3 files changed, 20 insertions, 5 deletions
diff --git a/docs/tags.md b/docs/tags.md index c1034cb4..bfca4f0e 100644 --- a/docs/tags.md +++ b/docs/tags.md @@ -62,7 +62,11 @@ if every command ran; an error says why in acme's words and changes nothing. `p` and `=` print to the directory's `+Errors`. Left out: the file commands `b B D e r w f X Y`, the pipes `< | >`, and `\1`-`\9` in `s`, since mvzr keeps no submatches; acme applies changes that come out of -sequence with a warning, pardes refuses the Edit. +sequence with a warning, pardes refuses the Edit. In `s`, `&` in the +replacement is the matched text (`\&` a plain `&`); in `c`, `a` and `i` an +`&` is only an `&`, as in sam. A pattern that finds nothing says so with the +pattern (`no match for regexp /nomatch/`), and an `s` on an empty dot says +`no substitution: dot is empty`. `Undo` and `Redo` are acme's: typed or clicked in a pane's tag, or written to its `ctl`, they step the body back and forward through its edits, as the diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index e1dd3fc5..2fdc47cc 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -332,7 +332,10 @@ pub fn runBuiltin(p: *Pardes, req: Req, id: usize, line: []const u8) ?Reply { if (!created) noteResult(p, context); if (p.fs.refused) return refuse(p, req, e_prompt, line); if (p.fs.failure_len == 0) return null; - const refusal = refuse(p, req, p.fs.failure[0..p.fs.failure_len], line); + // An Edit's failure names what failed (the pattern that missed): its + // line, a program, is not quoted after it. + const edit = std.mem.startsWith(u8, line, "Edit ") or std.mem.eql(u8, line, "Edit"); + const refusal = if (edit) tree.failText(req.tag, E.IO, p.fs.failure[0..p.fs.failure_len]) else refuse(p, req, p.fs.failure[0..p.fs.failure_len], line); return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = refusal.ename }; } diff --git a/src/sam_edit.zig b/src/sam_edit.zig index 679823da..29dec726 100644 --- a/src/sam_edit.zig +++ b/src/sam_edit.zig @@ -390,7 +390,11 @@ const Exec = struct { fn address(ex: *Exec, expr: ?[]const u8, dot: Range) Failure!Range { const e = expr orelse return dot; var a: addr_lang.Addr = .{ .text = ex.text, .lim = null, .expr = e }; - const r = a.address(dot) orelse return fail(ex.why, "{s}", .{a.err}); + // A miss names the pattern that missed, not the whole Edit line. + const r = a.address(dot) orelse return if (std.mem.eql(u8, a.err, addr_lang.e_no_match)) + fail(ex.why, "no match for regexp {s}", .{e}) + else + fail(ex.why, "{s}", .{a.err}); if (a.i < e.len) return fail(ex.why, "bad address syntax", .{}); if (r.q0 > r.q1) return fail(ex.why, "addresses out of order", .{}); return r; @@ -537,7 +541,10 @@ const Exec = struct { did = true; if (!c.global) break; } - if (!did and ex.nest == 0) return fail(ex.why, "no substitution", .{}); + if (!did and ex.nest == 0) return if (r.q0 == r.q1) + fail(ex.why, "no substitution: dot is empty", .{}) + else + fail(ex.why, "no substitution", .{}); return r; } @@ -679,8 +686,9 @@ test "an Edit that fails halfway changes nothing, and says why in acme's words" var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); for ([_][2][]const u8{ - .{ ",x/foo/c/bar/\n/nomatch/d", "no match for regexp" }, + .{ ",x/foo/c/bar/\n/nomatch/d", "no match for regexp /nomatch/" }, .{ ",s/zzz/y/", "no substitution" }, + .{ "s/foo/y/", "no substitution: dot is empty" }, .{ "k", "unknown command k" }, .{ ",x/a/{\na/1/\ni/2/\n}", "changes out of sequence" }, .{ "}", "right brace with no left brace" }, |
