summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/File.zig4
-rw-r--r--src/dump.zig34
2 files changed, 38 insertions, 0 deletions
diff --git a/src/File.zig b/src/File.zig
index 6a1ac319..810bc766 100644
--- a/src/File.zig
+++ b/src/File.zig
@@ -809,6 +809,10 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane {
.location_rows = location_rows,
.scroll = src.scroll,
.revision = @intFromBool(saved.dirty),
+ // Restored with unsaved text, it has text to lose as an edited pane
+ // has: a hash its text does not match, so Exit, Restore, Del and
+ // get ask first (builtins.warnModifiedIn reads only edited panes).
+ .saved_hash = if (saved.dirty) std.hash.Wyhash.hash(0, content) +% 1 else null,
.history = history,
};
pane.file.?.tree_context = saved.tree_context and supportsContext(pane);
diff --git a/src/dump.zig b/src/dump.zig
index 88a95994..54b77451 100644
--- a/src/dump.zig
+++ b/src/dump.zig
@@ -949,3 +949,37 @@ test "a restored pane keeps its dot, as acme's dump keeps a window's" {
try std.testing.expectEqual(@as(u32, 4), dot.q0);
try std.testing.expectEqual(@as(u32, 7), dot.q1);
}
+
+test "a pane restored with unsaved text is asked about by Exit, Restore, Del and get" {
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .tty_only = true });
+ defer p.deinit();
+ const pane = try p.setTestFile("saved\n");
+ _ = ctlfs.pane.spliceBody(p, pane, 0, 0, "unsaved ").?;
+ try std.testing.expect(pane.file.?.revision != pane.file.?.saved_revision);
+ try dumpState(p);
+ // Restore reads its dump before it warns: one it can read.
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "d.zon", .data = p.dump_out.? });
+ var dir_buf: [4096]u8 = undefined;
+ const dir = dir_buf[0..try tmp.dir.realPath(std.testing.io, &dir_buf)];
+ var restore_line: [4200]u8 = undefined;
+ for ([_][]const u8{ "Exit", "Restore", "Del", "get" }) |word| {
+ const restored = try initFromDump(gpa, .{ .tty_only = true }, p.dump_out.?);
+ defer restored.deinit();
+ const id = for (restored.panes, 0..) |slot, i| {
+ if (slot) |candidate| if (candidate.file != null and candidate.file.?.output == null) break i;
+ } else return error.FileLost;
+ const th = @import("ninep/testing.zig");
+ const node = if (std.mem.eql(u8, word, "Del") or std.mem.eql(u8, word, "get"))
+ ctlfs.Node.of(restored.panes[id].?.serial, .ctl)
+ else
+ @intFromEnum(ctlfs.TopFile.ctl);
+ const line = if (std.mem.eql(u8, word, "Restore")) try std.fmt.bufPrint(&restore_line, "Restore {s}/d.zon\n", .{dir}) else if (std.mem.eql(u8, word, "Exit")) "Exit\n" else if (std.mem.eql(u8, word, "Del")) "Del\n" else "get\n";
+ const answer = th.wr(restored, node, line);
+ try std.testing.expectEqual(ctlfs.Status.err, answer.reply.status);
+ try std.testing.expect(std.mem.indexOf(u8, answer.reply.ename, "Modified") != null);
+ try std.testing.expect(!restored.quit and restored.panes[id] != null);
+ }
+}