From e6e0301a782989782464e5f141ff4b4030a23c38 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 29 Jan 2025 22:10:54 +0100 Subject: [PATCH] feat(buffers): save/restore full editor state on buffer switch --- src/buffer/Manager.zig | 1 + src/buffer/Selection.zig | 12 +++++++-- src/tui/editor.zig | 55 +++++++++++++++++++++++++++++----------- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index e7bec3f..7bcd881 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -65,6 +65,7 @@ pub fn delete_buffer(self: *Self, file_path: []const u8) bool { pub fn retire(_: *Self, buffer: *Buffer, meta: ?[]const u8) void { if (meta) |buf| buffer.set_meta(buf) catch {}; tp.trace(tp.channel.debug, .{ "buffer", "retire", buffer.file_path, "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); + if (meta) |buf| tp.trace(tp.channel.debug, tp.message{ .buf = buf }); } pub fn close_buffer(self: *Self, buffer: *Buffer) void { diff --git a/src/buffer/Selection.zig b/src/buffer/Selection.zig index 415ddd4..bfa03ab 100644 --- a/src/buffer/Selection.zig +++ b/src/buffer/Selection.zig @@ -1,3 +1,5 @@ +const cbor = @import("cbor"); + const Buffer = @import("Buffer.zig"); const Cursor = @import("Cursor.zig"); @@ -44,13 +46,19 @@ pub fn normalize(self: *Self) void { } pub fn write(self: *const Self, writer: Buffer.MetaWriter) !void { + try cbor.writeArrayHeader(writer, 2); try self.begin.write(writer); try self.end.write(writer); } pub fn extract(self: *Self, iter: *[]const u8) !bool { - if (!try self.begin.extract(iter)) return false; - return self.end.extract(iter); + var iter2 = iter.*; + const len = cbor.decodeArrayHeader(&iter2) catch return false; + if (len != 2) return false; + if (!try self.begin.extract(&iter2)) return false; + if (!try self.end.extract(&iter2)) return false; + iter.* = iter2; + return true; } pub fn nudge_insert(self: *Self, nudge: Self) void { diff --git a/src/tui/editor.zig b/src/tui/editor.zig index d0f7086..b537f55 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -181,6 +181,7 @@ pub const CurSel = struct { } fn write(self: *const Self, writer: Buffer.MetaWriter) !void { + try cbor.writeArrayHeader(writer, 2); try self.cursor.write(writer); if (self.selection) |sel| { try sel.write(writer); @@ -190,15 +191,21 @@ pub const CurSel = struct { } fn extract(self: *Self, iter: *[]const u8) !bool { - if (!try self.cursor.extract(iter)) return false; var iter2 = iter.*; - if (try cbor.matchValue(&iter2, cbor.null_)) { - iter.* = iter2; + const len = cbor.decodeArrayHeader(&iter2) catch return false; + if (len != 2) return false; + if (!try self.cursor.extract(&iter2)) return false; + var iter3 = iter2; + if (try cbor.matchValue(&iter3, cbor.null_)) { + iter2 = iter3; } else { + iter3 = iter2; var sel: Selection = .{}; - if (!try sel.extract(iter)) return false; + if (!try sel.extract(&iter3)) return false; self.selection = sel; + iter2 = iter3; } + iter.* = iter2; return true; } @@ -364,13 +371,23 @@ pub const Editor = struct { try cbor.writeArrayHeader(writer, 0); } try self.view.write(writer); - try self.get_primary().cursor.write(writer); + + var count_cursels: usize = 0; + for (self.cursels.items) |*cursel_| if (cursel_.*) |_| { + count_cursels += 1; + }; + try cbor.writeArrayHeader(writer, count_cursels); + for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { + try cursel.write(writer); + }; } 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 }); var file_path: []const u8 = undefined; var view_cbor: []const u8 = undefined; - var primary_cbor: []const u8 = undefined; + var cursels_cbor: []const u8 = undefined; var clipboard: []const u8 = undefined; var query: []const u8 = undefined; var find_history: []const u8 = undefined; @@ -380,7 +397,7 @@ pub const Editor = struct { tp.extract(&query), tp.extract_cbor(&find_history), tp.extract_cbor(&view_cbor), - tp.extract_cbor(&primary_cbor), + tp.extract_cbor(&cursels_cbor), })) return error.RestoreStateMatch; if (op == .open_file) @@ -390,13 +407,22 @@ pub const Editor = struct { if (!try self.view.extract(&view_cbor)) return error.RestoreView; self.scroll_dest = self.view.row; - if (!try self.get_primary().cursor.extract(&primary_cbor)) - return error.RestoreCursor; - var len = cbor.decodeArrayHeader(&find_history) catch return error.RestoryFindHistory; + + if (cursels_cbor.len > 0) + self.clear_all_cursors(); + var iter = cursels_cbor; + var len = cbor.decodeArrayHeader(&iter) catch return error.RestoreCurSels; + while (len > 0) : (len -= 1) { + var cursel: CurSel = .{}; + if (!(cursel.extract(&iter) catch false)) break; + (try self.cursels.addOne()).* = cursel; + } + + len = cbor.decodeArrayHeader(&find_history) catch return error.RestoreFindHistory; while (len > 0) : (len -= 1) { var value: []const u8 = undefined; - if (!(cbor.matchValue(&find_history, cbor.extract(&value)) catch return error.RestoryFindHistory)) - return error.RestoryFindHistory; + if (!(cbor.matchValue(&find_history, cbor.extract(&value)) catch return error.RestoreFindHistory)) + return error.RestoreFindHistory; self.push_find_history(value); } self.clamp(); @@ -433,7 +459,7 @@ pub const Editor = struct { fn deinit(self: *Self) void { var meta = std.ArrayList(u8).init(self.allocator); defer meta.deinit(); - self.write_state(meta.writer()) catch {}; + if (self.buffer) |_| self.write_state(meta.writer()) catch {}; for (self.diagnostics.items) |*d| d.deinit(self.diagnostics.allocator); self.diagnostics.deinit(); if (self.syntax) |syn| syn.destroy(); @@ -568,6 +594,7 @@ pub const Editor = struct { defer meta.deinit(); self.write_state(meta.writer()) catch {}; if (self.buffer) |b_mut| self.buffer_manager.retire(b_mut, meta.items); + self.cancel_all_selections(); self.buffer = null; self.plane.erase(); self.plane.home(); @@ -3809,7 +3836,6 @@ pub const Editor = struct { const buffer_ = self.buffer; if (buffer_) |buffer| if (buffer.is_dirty()) return tp.exit("unsaved changes"); - self.cancel_all_selections(); try self.close(); if (buffer_) |buffer| self.buffer_manager.close_buffer(buffer); @@ -3817,7 +3843,6 @@ pub const Editor = struct { pub const close_file_meta = .{ .description = "Close file" }; pub fn close_file_without_saving(self: *Self, _: Context) Result { - self.cancel_all_selections(); const buffer_ = self.buffer; if (buffer_) |buffer| buffer.reset_to_last_saved();