summaryrefslogtreecommitdiff
path: root/src/recent.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 11:09:17 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit5db9e71ba32dd6f0a0292eaa341d1d8ba805483c (patch)
treefeaf11471f49fdba5122922a960889ea4cb4f549 /src/recent.zig
parent6b46d0797003585a99f8e5b99b3e8cff635ebebc (diff)
downloadpardes-5db9e71ba32dd6f0a0292eaa341d1d8ba805483c.tar.gz
pardes-5db9e71ba32dd6f0a0292eaa341d1d8ba805483c.zip
A remembered place is clamped to the file as it is now, its selection's two ends kept
A Recent row or a jumplist entry for a line the file no longer has failed the look; now it opens at the last line. The selection's anchor is remembered too, so a reopen restores both ends where they fit, and Recent and Jumplist share the (closed) marker. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/recent.zig')
-rw-r--r--src/recent.zig40
1 files changed, 31 insertions, 9 deletions
diff --git a/src/recent.zig b/src/recent.zig
index b6a1a66a..9f18d737 100644
--- a/src/recent.zig
+++ b/src/recent.zig
@@ -15,6 +15,9 @@ pub const Entry = struct {
/// Where its dot was when it last closed (1-based; 0 none).
line: u32 = 0,
col: u32 = 0,
+ /// The selection's other end then (its anchor), 0 when there was none.
+ aline: u32 = 0,
+ acol: u32 = 0,
};
pub const List = struct {
@@ -53,6 +56,11 @@ pub const List = struct {
/// `path` closed with its dot at `line`:`col`: where a reopen lands.
/// Its place in the list is its opening's.
pub fn closed(l: *List, gpa: std.mem.Allocator, path: []const u8, line: u32, col: u32) void {
+ l.closedSel(gpa, path, line, col, 0, 0);
+ }
+
+ /// `closed`, with the selection's anchor too (0 0 for none).
+ pub fn closedSel(l: *List, gpa: std.mem.Allocator, path: []const u8, line: u32, col: u32, aline: u32, acol: u32) void {
const i = l.find(path) orelse found: {
l.opened(gpa, path);
// Out of memory for it: nothing to note the dot on.
@@ -60,6 +68,8 @@ pub const List = struct {
};
l.entries.items[i].line = line;
l.entries.items[i].col = col;
+ l.entries.items[i].aline = aline;
+ l.entries.items[i].acol = acol;
l.dirty = true;
}
@@ -67,9 +77,10 @@ pub const List = struct {
return l.entries.items[l.find(path) orelse return null];
}
- /// One line a file, `<line> <col> <path>`, most recent first.
+ /// 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} {s}\n", .{ e.line, e.col, e.path });
+ for (l.entries.items) |e| 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.
@@ -77,13 +88,21 @@ pub const List = struct {
var lines = std.mem.splitScalar(u8, bytes, '\n');
while (lines.next()) |line| {
if (l.entries.items.len >= cap) break;
- var it = std.mem.splitScalar(u8, line, ' ');
- const ln = std.fmt.parseInt(u32, it.next() orelse continue, 10) catch continue;
- const col = std.fmt.parseInt(u32, it.next() orelse continue, 10) catch continue;
- const path = it.rest();
+ // Numbers, then the path (which starts with `/`): two of them in
+ // a list written before the anchor was kept, four since.
+ var nums: [4]u32 = @splat(0);
+ var n: usize = 0;
+ var rest = line;
+ while (rest.len > 0 and rest[0] != '/' and n < nums.len) : (n += 1) {
+ const sp = std.mem.indexOfScalar(u8, rest, ' ') orelse break;
+ nums[n] = std.fmt.parseInt(u32, rest[0..sp], 10) catch break;
+ rest = rest[sp + 1 ..];
+ }
+ if (n != 2 and n != 4) continue;
+ const path = rest;
if (path.len == 0 or path[0] != '/' or l.find(path) != null) continue;
const owned = gpa.dupe(u8, path) catch return;
- l.entries.append(gpa, .{ .path = owned, .line = ln, .col = col }) catch {
+ l.entries.append(gpa, .{ .path = owned, .line = nums[0], .col = nums[1], .aline = nums[2], .acol = nums[3] }) catch {
gpa.free(owned);
return;
};
@@ -105,12 +124,15 @@ test "the list keeps a path once, most recent first, its dot from closing, cappe
var buf: [256]u8 = undefined;
var w = std.Io.Writer.fixed(&buf);
try l.write(&w);
- try std.testing.expectEqualStrings("7 3 /a\n0 0 /b\n", w.buffered());
+ try std.testing.expectEqualStrings("7 3 0 0 /a\n0 0 0 0 /b\n", w.buffered());
var again: List = .{};
defer again.deinit(gpa);
again.read(gpa, w.buffered());
again.read(gpa, "junk\n");
- try std.testing.expectEqual(@as(usize, 2), again.entries.items.len);
+ // A list written before the anchor was kept still reads.
+ again.read(gpa, "4 2 /old\n");
+ try std.testing.expectEqual(@as(u32, 4), again.get("/old").?.line);
+ 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);