From 45609211f5047b5aec9964807cd5c98242cc70d7 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 21:40:57 -0300 Subject: A session restored from a dump is built as a fresh one is: its column and workspace tags take several selections, and its 9P files have a time dump.initDump copied Pardes.init's construction and had drifted from it: it never gave the column and workspace tags their allocator, so after -l or Restore they could not hold more than one selection, and never set fs.started, so every 9P mtime read 0. Pardes.create is now the one construction, allocators, tags, the tree's clock and the mounts, that init and initDump both start from. Co-Authored-By: Claude Opus 5.5 --- src/dump.zig | 35 ++++++++++++++++------------------- src/pardes.zig | 22 +++++++++++++++------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/dump.zig b/src/dump.zig index 73d4e4ed..78c2bd7a 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -848,23 +848,7 @@ const OldSerials = struct { }; fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previous: ?*const Pardes, old_serials: ?*OldSerials) !*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; - const p = try gpa.create(Pardes); - p.* = .{ - .gpa = gpa, - .image_gpa = image_gpa, - .pdf_gpa = pdf_gpa, - .tree_sitter_gpa = tree_sitter_gpa, - .opts = opts, - .fs = .{}, - .screen_w = opts.cols, - .screen_h = opts.rows, - .scratch = .init(gpa), - .frame_arena = .init(opts.frame_allocator orelse gpa), - .fallback = .{ .gpa = gpa }, - }; + const p = try Pardes.create(gpa, opts); errdefer p.deinit(); if (previous) |old| { p.next_serial = old.next_serial; @@ -879,8 +863,6 @@ fn initDump(gpa: std.mem.Allocator, opts: Options, zon_bytes: []const u8, previo p.recent = old.recent.clone(p.recentGpa()); p.recent_loaded = old.recent_loaded; } else p.loadRecent(); - for (opts.mounts) |mount| try p.fs.mount(gpa, mount.name, mount.dial); - p.opts.mounts = &.{}; var parsed = try readZon(gpa, zon_bytes, "load"); defer parsed.deinit(); const st = parsed.value; @@ -1027,6 +1009,21 @@ test "a session with every column it may hold dumps and restores them all" { try std.testing.expectEqual(@as(usize, MAX_COLS), restored.ncol); } +test "a restored session's column and workspace tags take more than one selection, and its 9P files have a time" { + const th = @import("ninep/testing.zig"); + const gpa = std.testing.allocator; + const p = try Pardes.init(gpa, .{ .tty_only = true }); + defer p.deinit(); + try dumpState(p); + const restored = try initFromDump(gpa, .{ .tty_only = true }, p.dump_out.?); + defer restored.deinit(); + try std.testing.expect(restored.col_tags[0].reserveSels()); + try std.testing.expect(restored.global_tag.reserveSels()); + try std.testing.expect(restored.fs.started != 0); + const stat = th.call(restored, .{ .tag = 1, .op = .getattr, .node = @intFromEnum(pardes.ctlfs.TopFile.index) }); + try std.testing.expect(stat.reply.attr.mtime != 0); +} + 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 }); diff --git a/src/pardes.zig b/src/pardes.zig index 623af3c9..3ecc80ae 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4594,16 +4594,18 @@ pub const Pardes = struct { /// `p.executeBuiltinLine(...)`; it lives with the rest of exec in exec.zig. pub const executeBuiltinLine = exec.executeBuiltinLine; - pub fn init(gpa: std.mem.Allocator, opts: Options) !*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; + /// The one construction of a session, empty: its allocators, its column + /// and workspace tags, the 9P tree's clock and its mounts. `init` boots + /// a layout into it and a dump's restore (dump.initDump) its panes; a + /// second copy of this drifted once, leaving restored tags without an + /// allocator and every 9P mtime 0. + pub fn create(gpa: std.mem.Allocator, opts: Options) !*Pardes { const p = try gpa.create(Pardes); p.* = .{ .gpa = gpa, - .image_gpa = image_gpa, - .pdf_gpa = pdf_gpa, - .tree_sitter_gpa = tree_sitter_gpa, + .image_gpa = opts.image_allocator orelse gpa, + .pdf_gpa = opts.pdf_allocator orelse gpa, + .tree_sitter_gpa = opts.tree_sitter_allocator orelse gpa, .opts = opts, .col_tags = @splat(.{ .what = .column, .gpa = gpa }), .global_tag = .{ .what = .workspace, .gpa = gpa }, @@ -4618,6 +4620,12 @@ pub const Pardes = struct { p.fs.started = ctlfs.events.now(); for (opts.mounts) |mount| try p.fs.mount(gpa, mount.name, mount.dial); p.opts.mounts = &.{}; + return p; + } + + pub fn init(gpa: std.mem.Allocator, opts: Options) !*Pardes { + const p = try create(gpa, opts); + errdefer p.deinit(); p.boot = Boot.of(opts); switch (p.boot) { .document => { -- cgit v1.3