summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 00:54:58 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commite988d4e24bfc0b80285754359b4b86f1b15b33f6 (patch)
tree7a0e62703f7da558e8695bcf5e2432ff1e52a464
parent7aa057e29b729ad6d9ac53e3a06f99858fc449e4 (diff)
downloadpardes-e988d4e24bfc0b80285754359b4b86f1b15b33f6.tar.gz
pardes-e988d4e24bfc0b80285754359b4b86f1b15b33f6.zip
Restore keeps each pane's dot, as acme's dump does
A restored text pane came back with its caret at its view's top, whatever had been selected. The dump now keeps each text pane's dot, q0 and q1 as acme's keeps a window's, and Restore puts it back without moving the view it kept. Two look-walk tests that restored and walked on now start from the match they were on. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--docs/config.md4
-rw-r--r--src/File.zig8
-rw-r--r--src/dump.zig24
-rw-r--r--test/output.zig15
4 files changed, 45 insertions, 6 deletions
diff --git a/docs/config.md b/docs/config.md
index 43b289cb..88f809ed 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -637,7 +637,9 @@ uses the argument as a path as before. Absolute paths and argument-free Restore
retain their existing behavior. `Dump` still honors `$PARDES_DUMP` and otherwise
writes a timestamped file in the default directory.
-A dump keeps the settings that differ from a fresh session's (`Placement
+A dump keeps each text pane's dot (its selection, or the caret), as acme's
+keeps a window's q0 and q1, and a Restore puts it back where the kept view
+shows it. It keeps the settings that differ from a fresh session's (`Placement
pardes`, `Verbose off`, a shader), as the root ctl reads them, and a Restore
sets them again; the theme is kept with the layout, and the font stays the
frontend's. REPL bindings are not kept.
diff --git a/src/File.zig b/src/File.zig
index 929e33bf..6a1ac319 100644
--- a/src/File.zig
+++ b/src/File.zig
@@ -814,6 +814,14 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane {
pane.file.?.tree_context = saved.tree_context and supportsContext(pane);
pane.body.cur_pinned = true;
pane.body.cur_row = @intCast(src.scroll);
+ // Its dot as it was, where the view the dump kept shows it.
+ if (saved.dot) |dot| if (dot[0] <= dot[1] and dot[1] <= content.len) {
+ const a = modal.positionAt(content, dot[0]);
+ const h = modal.positionAt(content, if (dot[1] > dot[0]) modal.prevGrapheme(content, dot[1]) else dot[0]);
+ pane.body.vsel = .{ .active = dot[1] > dot[0], .row = @intCast(a.row), .col = @intCast(a.col), .explicit = true };
+ pane.body.cur_row = @intCast(h.row);
+ pane.body.cur_col = @intCast(h.col);
+ };
pane.cols = @max(1, src.cols);
pane.rows = @max(1, src.rows);
if (output == null and filesystem.localPath(path) != null) p.emit(.{ .watch = .{ .pane = @intCast(id), .on = true, .mode = .baseline_disk } });
diff --git a/src/dump.zig b/src/dump.zig
index 5b0983ad..7101b708 100644
--- a/src/dump.zig
+++ b/src/dump.zig
@@ -142,6 +142,9 @@ pub const File = struct {
origin_arg: []const u8 = "",
mini_source: []const u8 = "",
mini_colors_b64: []const u8 = "",
+ /// Its dot, q0 and q1 in bytes, as acme's dump keeps a window's; null
+ /// in older dumps.
+ dot: ?[2]u32 = null,
};
pub const ImagePalette = enum {
@@ -653,6 +656,10 @@ pub fn dumpState(p: *Pardes) !void {
else
try pardes.panes.Terminal.dumpPane(pane, arena, tag, body, scroll);
dp.tag_tail = pane.tag.own;
+ if (dp.file) |*df| {
+ const dot = ctlfs.pane.dotOf(pane);
+ df.dot = .{ dot.q0, dot.q1 };
+ }
dp.serial = pane.serial;
dump_panes[panes_len] = dp;
panes_len += 1;
@@ -918,3 +925,20 @@ test "a dump keeps the settings that differ from a fresh session's, and a restor
try std.testing.expectEqual(config.Runtime.Placement.pardes, restored.settings.placement);
try std.testing.expect(!restored.settings.verbose);
}
+
+test "a restored pane keeps its dot, as acme's dump keeps a window's" {
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .tty_only = true });
+ defer p.deinit();
+ const pane = try p.setTestFile("one two three\nfour\n");
+ ctlfs.pane.setDot(pane, .{ .q0 = 4, .q1 = 7 });
+ try dumpState(p);
+ const restored = try initFromDump(gpa, .{ .tty_only = true }, p.dump_out.?);
+ defer restored.deinit();
+ const again = for (restored.panes) |slot| {
+ if (slot) |candidate| if (candidate.file != null) break candidate;
+ } else return error.FileLost;
+ const dot = ctlfs.pane.dotOf(again);
+ try std.testing.expectEqual(@as(u32, 4), dot.q0);
+ try std.testing.expectEqual(@as(u32, 7), dot.q1);
+}
diff --git a/test/output.zig b/test/output.zig
index e49ec4ff..95cdbfb7 100644
--- a/test/output.zig
+++ b/test/output.zig
@@ -799,13 +799,16 @@ test "n/N skips formatted context and selects comment match locations" {
defer restored.deinit();
pardes.look.armLookWalk(restored, id);
const restored_result = restored.panes[id].?;
- restored.update(.{ .key = .{ .cp = 'n' } });
+ // The dot came back on the match it was on (the first), so the
+ // walk goes on from there.
try std.testing.expectEqual(@as(i32, @intCast(matches[0])), restored_result.body.cur_row);
- restored.update(.{ .key = .{ .cp = 'N' } });
+ restored.update(.{ .key = .{ .cp = 'n' } });
try std.testing.expectEqual(@as(i32, @intCast(matches[1])), restored_result.body.cur_row);
+ restored.update(.{ .key = .{ .cp = 'N' } });
+ try std.testing.expectEqual(@as(i32, @intCast(matches[0])), restored_result.body.cur_row);
const restored_file = &restored_result.file.?;
- const last = modal.lineSlice(restored_file.content, matches[1]);
- try std.testing.expectEqualStrings(last[0..restored_file.location_rows[matches[1]].location_end], selectedOutputText(restored_result) orelse "");
+ const last = modal.lineSlice(restored_file.content, matches[0]);
+ try std.testing.expectEqualStrings(last[0..restored_file.location_rows[matches[0]].location_end], selectedOutputText(restored_result) orelse "");
}
}
}
@@ -1587,7 +1590,9 @@ test "stacked search and LSP results retain source tint navigation and dump owne
}
restored.active = id;
pardes.look.armLookWalk(restored, id);
- restored.update(.{ .key = .{ .cp = 'N' } });
+ // The dot came back on the match it was on; the walk goes on.
+ try std.testing.expectEqual(@as(i32, @intCast(matches[0])), restored.panes[id].?.body.cur_row);
+ restored.update(.{ .key = .{ .cp = 'n' } });
try std.testing.expectEqual(@as(i32, @intCast(matches[1])), restored.panes[id].?.body.cur_row);
try std.testing.expectEqualStrings(source_text, source.file.?.content);
}