summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/fs.md3
-rw-r--r--src/dump.zig17
-rw-r--r--src/ninep/events.zig7
-rw-r--r--test/fs.py5
-rw-r--r--test/snapshots/dump.golden3
5 files changed, 29 insertions, 6 deletions
diff --git a/docs/fs.md b/docs/fs.md
index bc48db2f..90d5ad64 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -509,7 +509,8 @@ the `del` of a terminal whose shell exited by itself, `ask <serial> <what>
<choices>` when a pane asks a question (answered by `answer` on its ctl),
and `save <serial> <name>`,
`dump <path>` when a Dump is written and `restore <path>` in a Restore's
-new log after its panes' `new`s, and `msg <serial|-> <text>`
+new log after its panes' `new`s, then `restored <old> <new>` for each pane,
+mapping the serial it had to the one it has now, and `msg <serial|-> <text>`
for every line the editor says, repeats included (with `verbose` on, that
includes each builtin announcing itself as it runs), and `err <serial|->
<file>: <why>` for every write or truncation the tree refused or that
diff --git a/src/dump.zig b/src/dump.zig
index 54fd1da1..0aaea39d 100644
--- a/src/dump.zig
+++ b/src/dump.zig
@@ -168,6 +168,9 @@ pub const Pane = struct {
rows: u16 = 0,
vweight: f32 = 1,
collapsed: bool = false,
+ /// Its serial when dumped (0 in older dumps): a Restore logs
+ /// `restored <old> <new>` for it.
+ serial: u32 = 0,
/// Exact editable tail when the user touched it. Null retains the live
/// default; a present empty slice deliberately restores an empty tail.
tag_tail: ?[]const u8 = null,
@@ -632,6 +635,7 @@ pub fn dumpState(p: *Pardes) !void {
else
try pardes.panes.Terminal.dumpPane(pane, arena, tag, body, scroll);
dp.tag_tail = pane.tag.own;
+ dp.serial = pane.serial;
dump_panes[panes_len] = dp;
panes_len += 1;
}
@@ -684,16 +688,22 @@ pub fn restore(p: *Pardes, zon_bytes: []const u8, from: []const u8) !*Pardes {
var opts = p.opts;
opts.cols = p.screen_w;
opts.rows = p.screen_h;
- const replacement = try initDump(p.gpa, opts, zon_bytes, p);
+ var old: [MAX_PANES]u32 = @splat(0);
+ const replacement = try initDump(p.gpa, opts, zon_bytes, p, &old);
pardes.ctlfs.events.notePath(replacement, "restore", from);
+ // Which serial each pane had, for a script that held the old ones.
+ for (replacement.panes, old) |slot, was| if (slot) |pane| if (was != 0) {
+ var rec: [32]u8 = undefined;
+ pardes.ctlfs.events.notePath(replacement, "restored", std.fmt.bufPrint(&rec, "{d} {d}", .{ was, pane.serial }) catch continue);
+ };
return replacement;
}
pub fn initFromDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8) !*Pardes {
- return initDump(gpa, opts, zon_bytes, null);
+ return initDump(gpa, opts, zon_bytes, null, null);
}
-fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previous: ?*const Pardes) !*Pardes {
+fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previous: ?*const Pardes, old_serials: ?*[MAX_PANES]u32) !*Pardes {
const image_gpa = opts.image_allocator orelse gpa;
const pdf_gpa = opts.pdf_allocator orelse gpa;
const tree_sitter_gpa = opts.tree_sitter_allocator orelse gpa;
@@ -743,6 +753,7 @@ fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previo
}
for (st.panes, 0..) |src, i| {
if (i >= MAX_PANES) break;
+ if (old_serials) |o| o[i] = src.serial;
const pane: *pardes.panes.Pane = switch (src.kind) {
.terminal => terminal: {
const t = src.terminal.?;
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 1c66c18f..0e489be7 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -934,8 +934,13 @@ test "a Dump written and a Restore made are in the log, with their files" {
const log = try freezeLog(core);
defer core.gpa.free(log.bytes);
var want: [64]u8 = undefined;
- try testing.expect(std.mem.endsWith(u8, log.bytes, try std.fmt.bufPrint(&want, "{s} /tmp/pardes.dump.zon\n", .{what})));
+ try testing.expect(std.mem.indexOf(u8, log.bytes, try std.fmt.bufPrint(&want, "{s} /tmp/pardes.dump.zon\n", .{what})) != null);
}
+ // The replacement's last says which serial each pane had.
+ const log = try freezeLog(restored);
+ defer restored.gpa.free(log.bytes);
+ var want: [64]u8 = undefined;
+ try testing.expect(std.mem.endsWith(u8, log.bytes, try std.fmt.bufPrint(&want, "restore /tmp/pardes.dump.zon\nrestored {d} {d}\n", .{ p.panes[0].?.serial, restored.panes[0].?.serial })));
}
test "opens of the log share the open records, and a closed one frees its record" {
diff --git a/test/fs.py b/test/fs.py
index 62e78655..67edf01e 100644
--- a/test/fs.py
+++ b/test/fs.py
@@ -662,7 +662,10 @@ def test(binary, quic=False):
raise AssertionError('Restore left an old body fid usable')
with Client(address) as restored:
# The new log names its panes, then where they came from.
- assert restored.read('/log').endswith(b'\nrestore ' + str(saved).encode() + b'\n'), restored.read('/log')
+ log = restored.read('/log')
+ assert b'\nrestore ' + str(saved).encode() + b'\n' in log, log
+ # then which serial each pane had: restored <old> <new>
+ assert log.endswith(b'\n'.join(b'restored %d %d' % (old, new) for old, new in zip(sorted(old_ids), sorted(int(r.split()[0]) for r in restored.read('/index').splitlines()))) + b'\n'), log
restored_ids = [int(row.split()[0]) for row in restored.read('/index').splitlines()]
assert len(restored_ids) == 2 and old_ids.isdisjoint(restored_ids), restored_ids
file_id, control_id = restored_ids
diff --git a/test/snapshots/dump.golden b/test/snapshots/dump.golden
index cdf22daf..deed689c 100644
--- a/test/snapshots/dump.golden
+++ b/test/snapshots/dump.golden
@@ -33,6 +33,7 @@
.rows = 13,
.vweight = 1,
.collapsed = false,
+ .serial = 1,
.tag_tail = null,
.terminal = .{
.cwd = "/tmp/pardes-snap/dump/cwd",
@@ -56,6 +57,7 @@
.rows = 12,
.vweight = 1,
.collapsed = false,
+ .serial = 2,
.tag_tail = null,
.terminal = .{
.cwd = "/tmp/pardes-snap/dump/cwd",
@@ -79,6 +81,7 @@
.rows = 27,
.vweight = 1,
.collapsed = false,
+ .serial = 3,
.tag_tail = null,
.terminal = .{
.cwd = "/tmp/pardes-snap/dump/cwd",