summaryrefslogtreecommitdiff
path: root/src/recent.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 11:40:03 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit2f187232227dde2755a7f2f2ce717f3e1f685f5c (patch)
tree0aebb2ad5831e235ab5833b514493878a00faf8c /src/recent.zig
parentf0fc0be608bb1ee96c20d9f9baaefd2543fed7a2 (diff)
downloadpardes-2f187232227dde2755a7f2f2ce717f3e1f685f5c.tar.gz
pardes-2f187232227dde2755a7f2f2ce717f3e1f685f5c.zip
Recent keeps only files on disk, and its cap never drops an open one
A renamed scratch never saved and a /virtual/ pane were recorded as recent files, and at 200 the oldest entry went even when that file was open. Opens, renames and closes now record a path only when it is a file on this host, and the cap drops the oldest closed entry. A name with a newline is not written to the kept list, where it would read back as two. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/recent.zig')
-rw-r--r--src/recent.zig39
1 files changed, 35 insertions, 4 deletions
diff --git a/src/recent.zig b/src/recent.zig
index 9f18d737..91c0afeb 100644
--- a/src/recent.zig
+++ b/src/recent.zig
@@ -18,6 +18,10 @@ pub const Entry = struct {
/// The selection's other end then (its anchor), 0 when there was none.
aline: u32 = 0,
acol: u32 = 0,
+ /// Open in a pane now (opened, not closed since): never the one the
+ /// cap drops. ponytail: a pane renamed away leaves its old path marked
+ /// open until the session ends; kept rather than tracked by pane.
+ open: bool = false,
};
pub const List = struct {
@@ -41,9 +45,14 @@ pub const List = struct {
var entry: Entry = if (l.find(path)) |i| l.entries.orderedRemove(i) else .{
.path = gpa.dupe(u8, path) catch return,
};
+ entry.open = true;
+ // The oldest closed file goes for it; an open one never does.
if (l.entries.items.len >= cap) {
- const last = l.entries.pop().?;
- gpa.free(last.path);
+ var i = l.entries.items.len;
+ while (i > 0) : (i -= 1) if (!l.entries.items[i - 1].open) {
+ gpa.free(l.entries.orderedRemove(i - 1).path);
+ break;
+ };
}
l.entries.insert(gpa, 0, entry) catch {
gpa.free(entry.path);
@@ -70,6 +79,7 @@ pub const List = struct {
l.entries.items[i].col = col;
l.entries.items[i].aline = aline;
l.entries.items[i].acol = acol;
+ l.entries.items[i].open = false;
l.dirty = true;
}
@@ -80,7 +90,9 @@ pub const List = struct {
/// One line a file, `<line> <col> <aline> <acol> <path>`, most recent
/// first.
pub fn write(l: *const List, w: *std.Io.Writer) !void {
- for (l.entries.items) |e| try w.print("{d} {d} {d} {d} {s}\n", .{ e.line, e.col, e.aline, e.acol, e.path });
+ // A name with a newline in it would read back as two lines: not kept.
+ for (l.entries.items) |e| if (std.mem.indexOfScalar(u8, e.path, '\n') == null)
+ try w.print("{d} {d} {d} {d} {s}\n", .{ e.line, e.col, e.aline, e.acol, e.path });
}
/// The lines `write` wrote; a line that is not one is passed over.
@@ -135,7 +147,26 @@ test "the list keeps a path once, most recent first, its dot from closing, cappe
try std.testing.expectEqual(@as(usize, 3), again.entries.items.len);
for (0..cap + 5) |i| {
var name: [16]u8 = undefined;
- l.opened(gpa, std.fmt.bufPrint(&name, "/f{d}", .{i}) catch unreachable);
+ const path = std.fmt.bufPrint(&name, "/f{d}", .{i}) catch unreachable;
+ l.opened(gpa, path);
+ l.closed(gpa, path, 1, 1);
}
try std.testing.expectEqual(@as(usize, cap), l.entries.items.len);
}
+
+test "the cap drops the oldest closed file, never an open one" {
+ const gpa = std.testing.allocator;
+ var l: List = .{};
+ defer l.deinit(gpa);
+ var name: [16]u8 = undefined;
+ // The oldest is open; the next is closed.
+ l.opened(gpa, "/open");
+ l.opened(gpa, "/shut");
+ l.closed(gpa, "/shut", 1, 1);
+ for (2..cap) |i| l.opened(gpa, std.fmt.bufPrint(&name, "/f{d}", .{i}) catch unreachable);
+ try std.testing.expectEqual(@as(usize, cap), l.entries.items.len);
+ l.opened(gpa, "/new");
+ try std.testing.expectEqual(@as(usize, cap), l.entries.items.len);
+ try std.testing.expect(l.get("/open") != null);
+ try std.testing.expect(l.get("/shut") == null);
+}