summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/fs.md4
-rw-r--r--src/builtins.zig19
-rw-r--r--src/ninep/ctl.zig14
-rw-r--r--src/ninep/pane.zig3
-rw-r--r--src/tagline.zig2
-rw-r--r--test/snapshots/lookloaded.golden2
6 files changed, 33 insertions, 11 deletions
diff --git a/docs/fs.md b/docs/fs.md
index c72c0131..088d1c92 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -747,7 +747,9 @@ written to `addr` that goes from the current one (`.`, `+1`, `-/re/`), which
there is none of, until an address that stands alone (`2`, `#0`, `/re/`) is.
The three flag files `dirty`, `mark` and `scroll` read `0` or `1` and take
-`0` or `1`: whether the buffer differs from its file, whether a write pushes
+`0` or `1`: whether the buffer differs from its file (a file deleted on
+disk counts, as in acme: its text is only here now, and Del, Exit and the
+rest ask first), whether a write pushes
an undo point (writing `1` pushes one now), and whether a write scrolls the
pane. The writes of one open of `data`, `xdata` or `body` are one undo
step while `mark` is 1 (so `printf 'a\nb\n' > data` is one, though a
diff --git a/src/builtins.zig b/src/builtins.zig
index b0499dc0..ca01e0fe 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -536,13 +536,18 @@ fn warnModifiedIn(c: Ctx, asking: Pane.Discarding, which: Asked) bool {
// under 100 bytes it is not asked about, as acme's winclean does not
// whine about a small unnamed window, nor about a command's output.
const scratch = if (f.output) |o| o.from == .cmd and o.from.cmd == .New else false;
- 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 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;
+ // A file deleted on disk is asked about, edited or not: its text is
+ // nowhere else now (acme's winclean asks the same).
+ const gone = f.disk_gone and f.output == null;
+ if (!gone) {
+ 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 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 };
// Scratches share a name: each is named with its pane's serial too,
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index b93d83c3..ebb85609 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -2201,6 +2201,20 @@ test "+Unsaved drops a pane once it has closed" {
try testing.expect(std.mem.indexOf(u8, list.file.?.content, "/tmp/pardes-unsaved-a.txt: Modified") == null);
}
+test "a buffer whose file was deleted on disk is dirty, and Del asks first" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ try testing.expectEqualStrings("0", std.mem.trimEnd(u8, rd(p, Node.of(serial, .dirty), 0, 8).bytes, "\n"));
+ pardes.panes.File.deleted(p, @intCast(p.paneBySerial(serial).?));
+ try testing.expectEqualStrings("1", std.mem.trimEnd(u8, rd(p, Node.of(serial, .dirty), 0, 8).bytes, "\n"));
+ try testing.expectEqual(Status.err, wr(p, Node.of(serial, .ctl), "Del\n").reply.status);
+ try testing.expect(p.paneBySerial(serial) != null);
+ _ = wr(p, Node.of(serial, .ctl), "Del\n");
+ p.sync();
+ try testing.expect(p.paneBySerial(serial) == null);
+}
+
test "every EINVAL a write gets says why, in its err record too; DEL is a control character in a line" {
const p = try withFile(testing.allocator, "x\n");
defer p.deinit();
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index f90a83d8..d48c8e71 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -125,7 +125,8 @@ pub fn tagOf(p: *Pardes, pane: *Pane) []const u8 {
pub fn dirtyOf(pane: *const Pane) bool {
const f = if (pane.file) |*x| x else return false;
if (!panes.Output.fileTraits(f.output).saves) return false;
- return f.revision != f.saved_revision;
+ // Its file deleted on disk: the text is only here now, as acme counts it.
+ return f.revision != f.saved_revision or f.disk_gone;
}
/// The pane's name: its file, its document, or a terminal's directory.
diff --git a/src/tagline.zig b/src/tagline.zig
index 5f95b2a0..df093925 100644
--- a/src/tagline.zig
+++ b/src/tagline.zig
@@ -30,7 +30,7 @@ const dirty_marker = " *";
/// A file with changes it has not saved.
pub fn dirty(pane: *const panes.Pane) bool {
const f = pane.file orelse return false;
- return panes.Output.fileTraits(f.output).saves and f.revision != f.saved_revision;
+ return panes.Output.fileTraits(f.output).saves and (f.revision != f.saved_revision or f.disk_gone);
}
pub const legacy_pane_tail = " Del";
pub const legacy_file_pane_tail = " Save Del";
diff --git a/test/snapshots/lookloaded.golden b/test/snapshots/lookloaded.golden
index 6644a430..70b42102 100644
--- a/test/snapshots/lookloaded.golden
+++ b/test/snapshots/lookloaded.golden
@@ -22,7 +22,7 @@
| big.txt
== snap removed grid=100x31 cursor=54,8
|6: rm big.txt; echo GO''NE
-|7: /tmp/pardes-snap/lookloaded/cwd/big.txt Save Tt GONE
+|7: * /tmp/pardes-snap/lookloaded/cwd/big.txt Save Tt GONE
== snap look-noFS grid=100x31 cursor=14,13
|8: y Collapse Del big.txt:10
|9: 6 line 6