diff options
Diffstat (limited to 'src/builtins.zig')
| -rw-r--r-- | src/builtins.zig | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/src/builtins.zig b/src/builtins.zig index 32e3cd84..51771c8f 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -234,6 +234,7 @@ test "Exit asks once about unsaved text, and quits when asked again" { defer p.deinit(); const pane = try p.setTestFile("saved\n"); pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified + pane.file.?.saved_hash = 0; // edited: what it had is not what it has try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); try std.testing.expect(!p.quit); try std.testing.expect(std.mem.endsWith(u8, pane.msg[0..pane.msg_len], ": Modified (Exit again to discard)")); @@ -247,6 +248,7 @@ test "Exit over a ctl write names every pane it asks about, and the next Exit di defer p.deinit(); const first = try p.setTestFile("saved\n"); first.file.?.saved_revision = first.file.?.revision -% 1; + first.file.?.saved_hash = 0; // edited: what it had is not what it has p.newScratchBelow(p.active); const second = p.panes[p.active].?; panes.File.setContent(p, &second.file.?, try std.testing.allocator.dupe(u8, "x" ** 120)); @@ -282,6 +284,7 @@ test "Restore asks about unsaved text as Exit does, and restores when asked agai defer p.deinit(); const pane = try p.setTestFile("saved\n"); pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified + pane.file.?.saved_hash = 0; // edited: what it had is not what it has var line_buf: [4200]u8 = undefined; const line = try testDump(&line_buf); try std.testing.expect(p.executeBuiltinLine(p.active, line)); @@ -296,6 +299,7 @@ test "a Restore of a dump that cannot be read says so, before the unsaved-text w defer p.deinit(); const pane = try p.setTestFile("saved\n"); pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified + pane.file.?.saved_hash = 0; // edited: what it had is not what it has p.fs.no_prompt = true; defer p.fs.no_prompt = false; try std.testing.expect(p.executeBuiltinLine(p.active, "Restore /nope/missing.zon")); @@ -310,6 +314,7 @@ test "a Restore's warning is not an Exit's: each word is warned on its own" { defer p.deinit(); const pane = try p.setTestFile("saved\n"); pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified + pane.file.?.saved_hash = 0; // edited: what it had is not what it has var line_buf: [4200]u8 = undefined; try std.testing.expect(p.executeBuiltinLine(p.active, try testDump(&line_buf))); try std.testing.expect(p.restore_req == null); @@ -474,10 +479,10 @@ fn warnModifiedIn(c: Ctx, asking: Pane.Discarding, which: Asked) bool { if (f.revision == f.saved_revision or !(panes.Output.fileTraits(f.output).saves or scratch)) continue; if (f.output != null and f.content.len < 100) continue; // Dirty by a rename alone is no text to lose, as get says (ctl.zig). - // Nor is an empty buffer that never held a file. - if (f.saved_hash) |saved| { - if (std.hash.Wyhash.hash(0, f.content) == saved) continue; - } else if (f.content.len == 0) continue; + // Nor is a buffer never edited at all: its first edit is what keeps + // the text it had (File.zig), so with none there is nothing to lose. + const saved = f.saved_hash orelse continue; + if (std.hash.Wyhash.hash(0, f.content) == saved) continue; if (pane.discard_warned) |w| if (w.revision == f.revision and w.by == asking) continue; pane.discard_warned = .{ .revision = f.revision, .by = asking }; var buf: [limits.host_path_cap + 48]u8 = undefined; @@ -1767,11 +1772,13 @@ test "Exit and Restore name a modified +New scratch as well as files" { defer p.deinit(); const file = try p.setTestFile("saved\n"); file.file.?.saved_revision = file.file.?.revision -% 1; // modified + file.file.?.saved_hash = 0; // edited: what it had is not what it has p.newScratchBelow(p.active); const scratch = &p.panes[p.active].?.file.?; scratch.content = try p.gpa.realloc(scratch.content, 120); @memset(scratch.content, 'n'); // past acme's 100 bytes, where it whines scratch.revision +%= 1; // typed into, never saved + scratch.saved_hash = 0; // edited: what it had is not what it has p.fs.no_prompt = true; defer p.fs.no_prompt = false; try std.testing.expect(p.executeBuiltinLine(p.active, "Exit")); @@ -1786,6 +1793,7 @@ test "Del and Delcol refuse a pane with unsaved text once, as acme's do, and clo defer p.deinit(); const pane = try p.setTestFile("saved\n"); pane.file.?.saved_revision = pane.file.?.revision -% 1; // modified + pane.file.?.saved_hash = 0; // edited: what it had is not what it has const serial = pane.serial; try std.testing.expect(p.executeBuiltinLine(p.active, "Delcol")); try std.testing.expect(p.paneBySerial(serial) != null); @@ -1797,3 +1805,14 @@ test "Del and Delcol refuse a pane with unsaved text once, as acme's do, and clo try std.testing.expect(p.executeBuiltinLine(p.active, "Del")); try std.testing.expect(p.paneBySerial(serial) == null); } + +test "a pane renamed but never edited has no text to lose: Del closes it at once" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + p.newScratchBelow(p.active); + const pane = try p.setTestFile("loaded\n"); + pane.file.?.saved_revision = pane.file.?.revision -% 1; // dirty by the rename alone + const serial = pane.serial; + try std.testing.expect(p.executeBuiltinLine(0, "Del")); + try std.testing.expect(p.paneBySerial(serial) == null); +} |
