summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 04:52:38 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit430d482c16101ce85b5c0f80f6bdd882c09a3272 (patch)
tree10dad6a09f8fd8e0784dc83058c10103c35fba25 /src
parent4e82c95653e1e8e03fde91e3cea2de04b3fabbc8 (diff)
downloadpardes-430d482c16101ce85b5c0f80f6bdd882c09a3272.tar.gz
pardes-430d482c16101ce85b5c0f80f6bdd882c09a3272.zip
Refusals and records say more: get quotes its line, a bare answer lists the choices, a clean reload is logged
get's refusal now quotes the line as a builtin's (Save's) does; a bare answer while a question stands says what it takes (answer takes k j or -); and a clean buffer reloaded from disk logs changed <serial> reloaded, as the kept dirty case logs changed <serial>. (A failed Save already says why: Save <path>: <the error in words>.) Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/File.zig3
-rw-r--r--src/ninep/ctl.zig20
-rw-r--r--src/pardes.zig2
3 files changed, 23 insertions, 2 deletions
diff --git a/src/File.zig b/src/File.zig
index 4011301a..17399d8b 100644
--- a/src/File.zig
+++ b/src/File.zig
@@ -994,6 +994,9 @@ pub fn changed(p: *Pardes, id: u8, bytes: []const u8) void {
return;
};
f.disk_newer = null;
+ // Logged as the dirty case is, with what became of it.
+ var rec: [32]u8 = undefined;
+ pardes.ctlfs.events.notePath(p, "changed", std.fmt.bufPrint(&rec, "{d} reloaded", .{pane.serial}) catch "");
const new = p.gpa.dupe(u8, bytes) catch return;
pushUndo(p, pane);
setContent(p, f, new);
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index f40825e3..eb8ed292 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -648,7 +648,15 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply {
if (apply and p.paneBySerial(serial) == null) return Reply.fail(req.tag, E.NOENT);
if (std.mem.eql(u8, line, "get")) {
if (!apply) continue;
- if (getRefused(p, pane)) |said| return tree.failText(req.tag, E.IO, said);
+ // Quoting the line, as a builtin's refusal (Save's) does.
+ if (getRefused(p, pane)) |said_in_ename| {
+ // refuse writes ename, where the words are: copy them out.
+ var said_buf: [320]u8 = undefined;
+ const said = said_buf[0..said_in_ename.len];
+ @memcpy(said, said_in_ename);
+ const r = refuse(p, req, said, line);
+ return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = r.ename };
+ }
const errno = get(p, pane);
if (errno != 0) return Reply.fail(req.tag, errno);
} else if (std.mem.eql(u8, line, "lock")) {
@@ -661,6 +669,12 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply {
held = false;
if (apply) pane.fs.lock = null;
} else if (std.mem.eql(u8, line, "answer")) {
+ // With a question up, what it offers.
+ var offered: [64]u8 = undefined;
+ if (p.questionChoices(p.paneBySerial(serial).?, &offered)) |choices| {
+ var why: [96]u8 = undefined;
+ return tree.failText(req.tag, E.INVAL, std.fmt.bufPrint(&p.fs.ename, "answer takes {s} or -", .{choices}) catch std.fmt.bufPrint(&why, "answer takes a choice or -", .{}) catch "answer takes a choice or -");
+ }
return tree.failText(req.tag, E.INVAL, "answer takes a choice or -");
} else if (std.mem.startsWith(u8, line, "answer ")) {
// The pane's question (its `ask` in the log), answered.
@@ -1280,7 +1294,7 @@ test "get reloads the pane from disk and reports a missing file without losing d
// Unsaved text is asked about once, as acme's get asks winclean.
const asked = wr(p, ctl_node, "get\n");
try testing.expectEqual(E.IO, asked.errno());
- try testing.expect(std.mem.endsWith(u8, asked.reply.ename, ": Modified (get again to discard)"));
+ try testing.expect(std.mem.endsWith(u8, asked.reply.ename, ": Modified (get again to discard) \"get\""));
try testing.expectEqual(E.NOENT, wr(p, ctl_node, "get\n").errno());
try testing.expect(p.paneBySerial(serial) != null);
try testing.expectEqualStrings("from disk\nunsaved\n", pane.file.?.content);
@@ -1571,6 +1585,8 @@ test "a file changed on disk under unsaved edits is not reloaded: it says so, ge
// Clean, it takes the new text.
pardes.panes.File.changed(p, 0, "two\n");
try testing.expectEqualStrings("two\n", f.content);
+ var reloaded: [32]u8 = undefined;
+ try testing.expect(th.logHas(p, try std.fmt.bufPrint(&reloaded, "changed {d} reloaded\n", .{serial})));
// Edited, it keeps its text and stays dirty.
_ = wr(p, Node.of(serial, .body), "mine\n");
pardes.panes.File.changed(p, 0, "three\n");
diff --git a/src/pardes.zig b/src/pardes.zig
index 289dcee0..5d709679 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -7977,6 +7977,8 @@ test "a pane closed while its question stands ends the question in the log" {
p.update(.{ .key = .{ .cp = ' ' } });
p.update(.{ .key = .{ .cp = 'd' } });
try std.testing.expectEqual(Pane.Prompt.del_side, p.panes[closing].?.prompt);
+ // A bare answer says what this question takes.
+ try std.testing.expectEqualStrings("answer takes k j or -", th.wr(p, ctlfs.Node.of(serial, .ctl), "answer\n").reply.ename);
try p.removePane(closing, null);
var want: [64]u8 = undefined;
try std.testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "answer {d} -\ndel {d}", .{ serial, serial })));