diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 55 |
1 files changed, 50 insertions, 5 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 9842362f..a2cfe1c6 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -44,7 +44,7 @@ const file_pane_tail = " " ++ file_pane_builtins_str; // Builtins: executing the name (middle-click / Tab) runs the action through the // ONE dispatcher (onSelect + actOnSelection), no matter where the name appears. -const SelectAction = enum { none, send, newcol, delcol, del, save, tutor, kill, debug, colors, nextcolor, dump }; +const SelectAction = enum { none, send, newcol, delcol, del, save, tutor, kill, debug, colors, nextcolor, dump, restore }; const Builtin = struct { name: []const u8, action: SelectAction }; const builtins = [_]Builtin{ .{ .name = "Kill", .action = .kill }, @@ -54,6 +54,7 @@ const builtins = [_]Builtin{ .{ .name = "Colors", .action = .colors }, .{ .name = "NextColor", .action = .nextcolor }, .{ .name = "Dump", .action = .dump }, + .{ .name = "Restore", .action = .restore }, .{ .name = "Save", .action = .save }, .{ .name = "Del", .action = .del }, .{ .name = "Delcol", .action = .delcol }, @@ -66,6 +67,8 @@ fn onSelect(button: Mouse.Button, text: []const u8) SelectAction { inline for (builtins) |b| { if (std.mem.eql(u8, cmd, b.name)) return b.action; } + // Restore takes the dump path as its first argument + if (std.mem.startsWith(u8, cmd, "Restore ") or std.mem.startsWith(u8, cmd, "Restore\t")) return .restore; return .send; } @@ -340,8 +343,9 @@ pub const Effect = union(enum) { /// write this pane's file content to its path; the shell reads both off /// the core (content is unbounded, effects are fixed-size values) save_file: struct { pane: u8 }, - /// a serialized state dump is ready in core.dump_out; write it to - /// dump.default_path (acme-style: another instance loads it with -l) + /// a serialized state dump is ready in core.dump_out; write it to the + /// path dump.outPath resolves (acme-style: another instance loads it + /// with -l, or the Restore builtin loads it into this one) write_dump, /// the yank register changed; the shell reads it off the core (OSC 52 out) set_clipboard, @@ -663,6 +667,13 @@ pub const Pardes = struct { yank: ?[]u8 = null, /// the last serialized dump (gpa-owned), read by the write_dump effect dump_out: ?[]u8 = null, + /// where the shell wrote the last dump (shell reports back after + /// write_dump); shown in the topbar as `Restore <path>` + last_dump: ?[]const u8 = null, + /// a Restore builtin wants this dump loaded into the live session; the + /// shell consumes it via takeRestore each frame (core stays sans-IO) + restore_req: ?[]const u8 = null, + restore_buf: [1024]u8 = undefined, surface: Surface = .{}, /// per-update scratch (paneCursorLines, selection text); reset each update @@ -710,6 +721,7 @@ pub const Pardes = struct { const gpa = p.gpa; if (p.yank) |y| gpa.free(y); if (p.dump_out) |d| gpa.free(d); + if (p.last_dump) |d| gpa.free(d); if (p.chord_arg) |a| gpa.free(a); p.effects.deinit(gpa); p.scratch.deinit(); @@ -717,6 +729,29 @@ pub const Pardes = struct { gpa.destroy(p); } + /// shell feedback after write_dump: remember where the dump landed (the + /// topbar grows a `Restore <path>` word pointing at it) + pub fn setLastDump(p: *Pardes, path: []const u8) void { + const copy = p.gpa.dupe(u8, path) catch return; + if (p.last_dump) |old| p.gpa.free(old); + p.last_dump = copy; + } + + /// the shell polls this each frame: a pending Restore's dump path, or null + pub fn takeRestore(p: *Pardes) ?[]const u8 { + const r = p.restore_req; + p.restore_req = null; + return r; + } + + /// the topbar line: the fixed builtins, plus `Restore <path>` once a dump + /// exists — render and click dispatch must agree on this exact string + fn topbar(p: *Pardes, buf: []u8) []const u8 { + if (p.last_dump) |d| + return std.fmt.bufPrint(buf, topbar_str ++ " Restore {s}", .{d}) catch topbar_str; + return topbar_str; + } + fn deinitPane(p: *Pardes, pane: *Pane) void { if (pane.image) |*iv| { p.gpa.free(iv.path); @@ -2374,7 +2409,8 @@ pub const Pardes = struct { // global tagbar: MIDDLE runs the command under the click // (a stray left click must never Kill) if (m.button == .middle) { - const word = wordAtCol(topbar_str, mcol); + var tb_buf: [1200]u8 = undefined; + const word = wordAtCol(p.topbar(&tb_buf), mcol); if (word.len > 0) p.actOnSelection(.middle, p.active, word, null); } return; @@ -2870,6 +2906,14 @@ pub const Pardes = struct { .colors => p.colors_on = !p.colors_on, .nextcolor => p.theme_idx = (p.theme_idx + 1) % themes.len, .dump => p.dumpState() catch {}, + .restore => { + var it = std.mem.tokenizeAny(u8, std.mem.trim(u8, txt, " \t\r\n"), " \t"); + _ = it.next(); // the word "Restore" + const path = it.next() orelse (p.last_dump orelse return); + if (path.len > p.restore_buf.len) return; + @memcpy(p.restore_buf[0..path.len], path); + p.restore_req = p.restore_buf[0..path.len]; + }, .kill => { p.quit = true; p.emit(.quit); @@ -3335,7 +3379,8 @@ pub const Pardes = struct { // global tagbar: full width, top row s.fill(0, 0, s.cols, TOPBAR_H, .{ .bg = .{ .rgb = th.tag_bg } }); - _ = s.print(0, 0, s.cols, topbar_str, .{ .fg = .{ .rgb = th.tag_fg }, .bg = .{ .rgb = th.tag_bg } }); + var tb_buf: [1200]u8 = undefined; + _ = s.print(0, 0, s.cols, p.topbar(&tb_buf), .{ .fg = .{ .rgb = th.tag_fg }, .bg = .{ .rgb = th.tag_bg } }); // resize-handle hint / drag previews: a dash overlay that keeps the // underlying colors (border drags + hover), or the move indicator |
