From 7b73ca0ccd384eed4b1b84c2a974a842fe24c7d2 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 5 Aug 2025 20:08:29 +0200 Subject: [PATCH] WIP: feat: restore buffer manager state on restart --- src/buffer/Buffer.zig | 53 ++++++++++++++++++++++++++++++++++++++++++ src/buffer/Manager.zig | 26 +++++++++++++++++++++ src/main.zig | 2 +- src/tui/editor.zig | 15 ++++++------ src/tui/mainview.zig | 20 ++++++++++++---- 5 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index c094845..5dc1195 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const cbor = @import("cbor"); const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const cwd = std.fs.cwd; @@ -1486,3 +1487,55 @@ pub fn redo(self: *Self) error{Stop}![]const u8 { self.mtime = std.time.milliTimestamp(); return h.meta; } + +pub fn write_state(self: *const Self, writer: MetaWriter) error{ Stop, OutOfMemory }!void { + var content = std.ArrayListUnmanaged(u8).empty; + defer content.deinit(self.external_allocator); + try self.root.store(content.writer(self.external_allocator), self.file_eol_mode); + + try cbor.writeArrayHeader(writer, 8); + try cbor.writeValue(writer, self.get_file_path()); + try cbor.writeValue(writer, self.file_exists); + try cbor.writeValue(writer, self.file_eol_mode); + try cbor.writeValue(writer, self.hidden); + try cbor.writeValue(writer, self.ephemeral); + try cbor.writeValue(writer, self.meta); + try cbor.writeValue(writer, self.file_type_name); + try cbor.writeValue(writer, content.items); +} + +pub const ExtractStateOperation = enum { none, open_file }; + +pub fn extract_state(self: *Self, iter: *[]const u8, comptime op: ExtractStateOperation) !void { + var file_path: []const u8 = undefined; + var file_exists: bool = undefined; + var file_eol_mode: EolMode = undefined; + var hidden: bool = undefined; + var ephemeral: bool = undefined; + var meta: ?[]const u8 = undefined; + var file_type_name: ?[]const u8 = undefined; + var content: []const u8 = undefined; + + if (!try cbor.matchValue(iter, .{ + cbor.extract(&file_path), + cbor.extract(&file_exists), + cbor.extract(&file_eol_mode), + cbor.extract(&hidden), + cbor.extract(&ephemeral), + cbor.extract(&meta), + cbor.extract(&file_type_name), + cbor.extract(&content), + })) + return error.Stop; + + self.set_file_path(file_path); + self.file_exists = file_exists; + self.file_eol_mode = file_eol_mode; + self.hidden = hidden; + self.ephemeral = ephemeral; + self.meta = meta; + self.file_type_name = file_type_name; + try self.reset_from_string_and_update(content); + + _ = op; +} diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index d2484ee..2c0a2eb 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -51,6 +51,32 @@ pub fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) Buf return buffer; } +pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) error{ Stop, OutOfMemory }!void { + const buffers = self.list_unordered(self.allocator) catch return; + defer self.allocator.free(buffers); + for (buffers) |buffer| + buffer.write_state(writer) catch |e| switch (e) { + error.Stop => {}, + else => return, + }; +} + +pub fn extract_state(self: *Self, iter: *[]const u8, comptime op: Buffer.ExtractStateOperation) !void { + while (iter.len > 0) { + var buffer = try Buffer.create(self.allocator); + errdefer buffer.deinit(); + buffer.extract_state(iter, op) catch |e| switch (e) { + error.Stop => { + buffer.deinit(); + return; + }, + else => |e_| return e_, + }; + try self.buffers.put(self.allocator, try self.allocator.dupe(u8, buffer.get_file_path()), buffer); + tp.trace(tp.channel.debug, .{ "buffer", "extract", buffer.get_file_path(), buffer.file_type_name }); + } +} + pub fn get_buffer_for_file(self: *Self, file_path: []const u8) ?*Buffer { return self.buffers.get(file_path); } diff --git a/src/main.zig b/src/main.zig index 07e1cf5..f337409 100644 --- a/src/main.zig +++ b/src/main.zig @@ -942,7 +942,7 @@ pub fn get_restore_file_name() ![]const u8 { const restore_file = if (local.restore_file) |file| file else - try std.fmt.bufPrint(&local.restore_file_buffer, "{s}/{s}", .{ try get_app_cache_dir(application_name), restore_file_name }); + try std.fmt.bufPrint(&local.restore_file_buffer, "{s}/{s}", .{ try get_app_state_dir(application_name), restore_file_name }); local.restore_file = restore_file; return restore_file; } diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 09e232c..66472f1 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -401,9 +401,7 @@ pub const Editor = struct { }; } - pub fn extract_state(self: *Self, buf: []const u8, comptime op: enum { none, open_file }) !void { - tp.trace(tp.channel.debug, .{ "extract_state", self.file_path }); - tp.trace(tp.channel.debug, tp.message{ .buf = buf }); + pub fn extract_state(self: *Self, iter: *[]const u8, comptime op: Buffer.ExtractStateOperation) !void { self.restored_state = true; var file_path: []const u8 = undefined; var view_cbor: []const u8 = undefined; @@ -411,7 +409,7 @@ pub const Editor = struct { var clipboard: []const u8 = undefined; var last_find_query: []const u8 = undefined; var find_history: []const u8 = undefined; - if (!try cbor.match(buf, .{ + if (!try cbor.matchValue(iter, .{ tp.extract(&file_path), tp.extract(&clipboard), tp.extract(&last_find_query), @@ -440,11 +438,11 @@ pub const Editor = struct { if (cursels_cbor.len > 0) self.clear_all_cursors(); - var iter = cursels_cbor; - var len = cbor.decodeArrayHeader(&iter) catch return error.RestoreCurSels; + var cursels_iter = cursels_cbor; + var len = cbor.decodeArrayHeader(&cursels_iter) catch return error.RestoreCurSels; while (len > 0) : (len -= 1) { var cursel: CurSel = .{}; - if (!(cursel.extract(&iter) catch false)) break; + if (!(cursel.extract(&cursels_iter) catch false)) break; (try self.cursels.addOne(self.allocator)).* = cursel; } @@ -659,7 +657,8 @@ pub const Editor = struct { if (buffer_meta) |meta| { const frame_ = tracy.initZone(@src(), .{ .name = "extract_state" }); defer frame_.deinit(); - try self.extract_state(meta, .none); + var iter = meta; + try self.extract_state(&iter, .none); } try self.send_editor_open(file_path, new_buf.file_exists, ftn, fti, ftc); } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index c210882..cf0993f 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1288,11 +1288,21 @@ fn create_home_split(self: *Self) !void { } pub fn write_restore_info(self: *Self) void { - const editor = self.get_active_editor() orelse return; + // const editor = self.get_active_editor() orelse return; var sfa = std.heap.stackFallback(512, self.allocator); const a = sfa.get(); var meta = std.ArrayListUnmanaged(u8).empty; - editor.write_state(meta.writer(a)) catch return; + // editor.write_state(meta.writer(a)) catch return; + + const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); + const buffers = buffer_manager.list_unordered(self.allocator) catch return; + defer self.allocator.free(buffers); + for (buffers) |buffer| + buffer.write_state(meta.writer(a)) catch |e| switch (e) { + error.Stop => {}, + else => return, + }; + const file_name = root.get_restore_file_name() catch return; var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch return; defer file.close(); @@ -1300,7 +1310,7 @@ pub fn write_restore_info(self: *Self) void { } fn read_restore_info(self: *Self) !void { - const editor = self.get_active_editor() orelse return; + // const editor = self.get_active_editor() orelse return; const file_name = try root.get_restore_file_name(); const file = try std.fs.cwd().openFile(file_name, .{ .mode = .read_only }); defer file.close(); @@ -1308,7 +1318,9 @@ fn read_restore_info(self: *Self) !void { var buf = try self.allocator.alloc(u8, @intCast(stat.size)); defer self.allocator.free(buf); const size = try file.readAll(buf); - try editor.extract_state(buf[0..size], .open_file); + var iter: []const u8 = buf[0..size]; + // try editor.extract_state(&iter, .open_file); + try self.buffer_manager.extract_state(&iter, .open_file); } fn get_next_mru_buffer(self: *Self) ?[]const u8 {