summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig21
1 files changed, 21 insertions, 0 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 16935f41..363524f1 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -454,6 +454,7 @@ 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);
const errno = get(p, pane);
if (errno != 0) return Reply.fail(req.tag, errno);
} else if (std.mem.eql(u8, line, "lock")) {
@@ -477,6 +478,21 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply {
const e_not_locked = "window not locked by this open";
+/// acme's get asks winclean first (exec.c:513): unsaved text is warned
+/// about once, and the same get again, nothing edited since, discards it.
+fn getRefused(p: *Pardes, pane: *Pane) ?[]const u8 {
+ const f = pane_files.fileOf(pane) orelse return null;
+ if (!panes.Output.fileTraits(f.output).saves or f.revision == f.saved_revision) return null;
+ // Dirty by a rename alone is no text to lose: only edits are asked about.
+ const saved = f.saved_hash orelse return null;
+ if (std.hash.Wyhash.hash(0, f.content) == saved) return null;
+ if (pane.discard_warned) |w| if (w.revision == f.revision and w.by == .get) return null;
+ pane.discard_warned = .{ .revision = f.revision, .by = .get };
+ const said = std.fmt.bufPrint(&p.fs.ename, "{s}: Modified (get again to discard)", .{f.path}) catch "Modified (get again to discard)";
+ p.setMessage(p.paneBySerial(pane.serial).?, said);
+ return said;
+}
+
fn get(p: *Pardes, pane: *Pane) u16 {
const f = pane_files.fileOf(pane) orelse return 0;
if (!panes.Output.fileTraits(f.output).saves) return 0;
@@ -487,6 +503,7 @@ fn get(p: *Pardes, pane: *Pane) u16 {
panes.File.pushUndo(p, pane);
panes.File.setContent(p, f, bytes);
f.saved_revision = f.revision;
+ f.saved_hash = std.hash.Wyhash.hash(0, f.content);
return 0;
}
@@ -960,6 +977,10 @@ test "get reloads the pane from disk and reports a missing file without losing d
_ = wr(p, Node.of(serial, .name), try std.fmt.bufPrint(&name, "{s}/missing.txt\n", .{directory}));
_ = wr(p, Node.of(serial, .body), "unsaved\n");
+ // 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.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);