diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 20:09:32 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | f8fe738416fe3a40a7136f2cb5528e8fd57f40df (patch) | |
| tree | 4f5f3ff990d79523a191c0754a154f3f5ffdee43 /src | |
| parent | 506ca99f13aec67d593e4a30bbe416dea176d0ba (diff) | |
| download | pardes-f8fe738416fe3a40a7136f2cb5528e8fd57f40df.tar.gz pardes-f8fe738416fe3a40a7136f2cb5528e8fd57f40df.zip | |
Recent and /recent keep PDFs and images as they keep files, and an open file's row is where its dot is now
Only text files were recorded, so a PDF read yesterday was not there to
find again, and an open file's row showed the place it last closed at,
stale by however far it had moved since. A PDF is kept at its page and
reopened there from its row; an image by its path; an open one's row
reads its pane's place at the time of the listing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/Output.zig | 10 | ||||
| -rw-r--r-- | src/look.zig | 5 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 21 | ||||
| -rw-r--r-- | src/ninep/events.zig | 5 | ||||
| -rw-r--r-- | src/pardes.zig | 16 |
5 files changed, 51 insertions, 6 deletions
diff --git a/src/Output.zig b/src/Output.zig index 06f2f1dc..4aa0c021 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -612,10 +612,16 @@ pub fn openJumps(p: *Pardes, id: usize) !void { /// The recent list as rows: `<path>:<line>:<col> open|closed`, most recent /// first; a look at a row opens the file there, as a +Jumps row does. pub fn recentText(p: *Pardes, w: *std.Io.Writer, look_rows: bool) !void { - for (p.recent.entries.items) |e| { + for (p.recent.entries.items) |stored| { + // An open one's row is where it is now, not where it last closed. + var e = stored; var is_open = false; - for (p.panes) |slot| if (slot) |pane| if (pane.file) |f| if (f.output == null and std.mem.eql(u8, f.path, e.path)) { + for (p.panes) |slot| if (slot) |pane| if (Pardes.recentPlace(pane)) |d| if (std.mem.eql(u8, d.path, e.path)) { is_open = true; + e.line = d.line; + e.col = d.col; + e.aline = d.aline; + e.acol = d.acol; }; const state: []const u8 = if (is_open) "open" else "(closed)"; if (!look_rows) { diff --git a/src/look.zig b/src/look.zig index 2e8a4b39..36b91545 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1481,6 +1481,11 @@ pub fn reopenAt(p: *Pardes, from: usize, path: []const u8, at: Spot) void { lookAt(p, from, path); const id = p.active; const pane = p.panes[id] orelse return; + // A PDF's place is its page, the last if it has fewer now. + if (comptime pdf_enabled) if (pane.pdf) |*pv| if (std.mem.eql(u8, pv.path, path)) { + if (at.line > 0 and pv.focusLocation(p.pdf_gpa, @min(at.line, pv.page_count), 0)) panes.Pdf.resetPageChrome(pane); + return; + }; const f = if (pane.file) |*file| file else return; if (!std.mem.eql(u8, f.path, path)) return; // The empty "line" after a final newline is no line to land on. diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index eff8003b..75a7eb9a 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -3060,6 +3060,27 @@ test "a Jumplist entry is (closed) only while no pane has its file open" { for (p.jumps[0..p.njumps]) |j| try testing.expect(j.serial != 0); } +test "Recent's row for a file still open is where its dot is now, not where it last closed" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "a.txt", .data = "one\ntwo\nthree\n" }); + var dir_buf: [4096]u8 = undefined; + const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)]; + var line: [4200]u8 = undefined; + try testing.expectEqual(Status.ok, wr(p, root_look, try std.fmt.bufPrint(&line, "{s}/a.txt:3\n", .{dir})).reply.status); + p.sync(); + const pane = p.panes[p.active].?; + pane.body.vsel.active = false; // the look's selection of line 3 let go + pane.body.cur_row = 1; + pane.body.cur_col = 2; + try testing.expectEqual(Status.ok, wr(p, root_exec, "Recent\n").reply.status); + const rows = p.panes[p.paneBySerial(try std.fmt.parseInt(u32, std.mem.trimEnd(u8, rd(p, root_exec, 0, 64).bytes, "\n"), 10)).?].?; + var want: [4300]u8 = undefined; + try testing.expect(std.mem.indexOf(u8, rows.file.?.content, try std.fmt.bufPrint(&want, "{s}/a.txt:2:3 open\n", .{dir})) != null); +} + test "a closed file is found again: Recent and /recent list it closed, a look at its row reopens it at its dot, and a jump back does too" { const p = try withFile(testing.allocator, "x\n"); defer p.deinit(); diff --git a/src/ninep/events.zig b/src/ninep/events.zig index a09f7278..bacf3ad6 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -140,8 +140,9 @@ pub fn announce(p: *Pardes) void { if (!pane.fs.unannounced) continue; pane.fs.unannounced = false; noteLog(p, .new, pane); - // A file opened (a look, a Restore, a boot): first in Recent. - if (pane.file) |f| if (f.output == null and pardes.Pardes.recentKeeps(f.path)) p.recent.opened(p.recentGpa(), f.path); + // A file, PDF or image opened (a look, a Restore, a boot): first in + // Recent. + if (pardes.Pardes.recentPlace(pane)) |d| if (pardes.Pardes.recentKeeps(d.path)) p.recent.opened(p.recentGpa(), d.path); } } diff --git a/src/pardes.zig b/src/pardes.zig index bea79b18..fbc19b8a 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4709,13 +4709,25 @@ pub const Pardes = struct { return exec.isFile(local); } - /// A file pane closing: its place noted in Recent, when Recent keeps it. + /// A file, PDF or image pane closing: its place noted in Recent, when + /// Recent keeps it. fn noteClosed(p: *Pardes, pane: *const Pane) void { - const d = recentDot(pane) orelse return; + const d = recentPlace(pane) orelse return; if (p.recent.get(d.path) == null and !recentKeeps(d.path)) return; p.recent.closedSel(p.recentGpa(), d.path, d.line, d.col, d.aline, d.acol); } + pub const RecentPlace = struct { path: []const u8, line: u32, col: u32, aline: u32 = 0, acol: u32 = 0 }; + + /// Where a pane is, as Recent keeps it: a file's dot, a PDF's page + /// (`:page:1`), an image's path alone (line 0). Null for any other. + pub fn recentPlace(pane: *const Pane) ?RecentPlace { + if (recentDot(pane)) |d| return .{ .path = d.path, .line = d.line, .col = d.col, .aline = d.aline, .acol = d.acol }; + if (pane.pdfPath()) |path| return .{ .path = path, .line = @intCast(pane.pdfPage().? + 1), .col = 1 }; + if (pane.image) |iv| return .{ .path = iv.path, .line = 0, .col = 0 }; + return null; + } + fn recentDot(pane: *const Pane) ?struct { path: []const u8, line: u32, col: u32, aline: u32 = 0, acol: u32 = 0 } { const f = pane.file orelse return null; if (f.output != null) return null; |
