diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 344bba4..47d3416 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -46,8 +46,9 @@ auto_save: bool = false, meta: ?[]const u8 = null, lsp_version: usize = 1, -undo_head: ?*UndoNode = null, -redo_head: ?*UndoNode = null, +undo_history: ?*UndoNode = null, +redo_history: ?*UndoNode = null, +curr_history: ?*UndoNode = null, mtime: i64, utime: i64, @@ -61,15 +62,14 @@ pub const EolModeTag = @typeInfo(EolMode).@"enum".tag_type; const UndoNode = struct { root: Root, - next_undo: ?*UndoNode = null, - next_redo: ?*UndoNode = null, + next: ?*UndoNode = null, branches: ?*UndoBranch = null, meta: []const u8, file_eol_mode: EolMode, }; const UndoBranch = struct { - redo_head: *UndoNode, + redo: *UndoNode, next: ?*UndoBranch, }; @@ -1511,8 +1511,9 @@ pub fn update(self: *Self, root: Root) void { } pub fn store_undo(self: *Self, meta: []const u8) error{OutOfMemory}!void { - try self.push_redo_branch(); self.push_undo(try self.create_undo(self.root, meta)); + self.curr_history = null; + try self.push_redo_branch(); } fn create_undo(self: *const Self, root: Root, meta_: []const u8) error{OutOfMemory}!*UndoNode { @@ -1526,65 +1527,54 @@ fn create_undo(self: *const Self, root: Root, meta_: []const u8) error{OutOfMemo return h; } -fn push_undo(self: *Self, node: *UndoNode) void { - node.next_undo = self.undo_head; - self.undo_head = node; +fn push_undo(self: *Self, h: *UndoNode) void { + const next = self.undo_history; + self.undo_history = h; + h.next = next; } -fn pop_undo(self: *Self) ?*UndoNode { - const node = self.undo_head orelse return null; - self.undo_head = node.next_undo; - return node; -} - -fn push_redo(self: *Self, node: *UndoNode) void { - node.next_redo = self.redo_head; - self.redo_head = node; -} - -fn pop_redo(self: *Self) ?*UndoNode { - const node = self.redo_head orelse return null; - self.redo_head = node.next_redo; - return node; +fn push_redo(self: *Self, h: *UndoNode) void { + const next = self.redo_history; + self.redo_history = h; + h.next = next; } fn push_redo_branch(self: *Self) !void { - const redo_head = self.redo_head orelse return; - const undo_head = self.undo_head orelse return; - const branch = try self.allocator.create(UndoBranch); - branch.* = .{ - .redo_head = redo_head, - .next = undo_head.branches, + const r = self.redo_history orelse return; + const u = self.undo_history orelse return; + const next = u.branches; + const b = try self.allocator.create(UndoBranch); + b.* = .{ + .redo = r, + .next = next, }; - undo_head.branches = branch; - self.redo_head = null; + u.branches = b; + self.redo_history = null; } -pub fn undo(self: *Self) error{Stop}![]const u8 { - const node = self.pop_undo() orelse return error.Stop; - if (self.redo_head == null) blk: { - self.push_redo(self.create_undo(self.root, &.{}) catch break :blk); - } - self.push_redo(node); - self.root = node.root; - self.file_eol_mode = node.file_eol_mode; +pub fn undo(self: *Self, meta: []const u8) error{Stop}![]const u8 { + const r = self.curr_history orelse self.create_undo(self.root, meta) catch return error.Stop; + const h = self.undo_history orelse return error.Stop; + self.undo_history = h.next; + self.curr_history = h; + self.root = h.root; + self.file_eol_mode = h.file_eol_mode; + self.push_redo(r); self.mtime = std.time.milliTimestamp(); - return node.meta; + return h.meta; } pub fn redo(self: *Self) error{Stop}![]const u8 { - if (self.redo_head) |redo_head| if (self.root != redo_head.root) - return error.Stop; - const node = self.pop_redo() orelse return error.Stop; - self.push_undo(node); - if (self.redo_head) |head| { - self.root = head.root; - self.file_eol_mode = head.file_eol_mode; - if (head.next_redo == null) - self.redo_head = null; - } + const u = self.curr_history orelse return error.Stop; + const h = self.redo_history orelse return error.Stop; + if (u.root != self.root) return error.Stop; + self.redo_history = h.next; + self.curr_history = h; + self.root = h.root; + self.file_eol_mode = h.file_eol_mode; + self.push_undo(u); self.mtime = std.time.milliTimestamp(); - return node.meta; + return h.meta; } pub fn write_state(self: *const Self, writer: *std.Io.Writer) error{ Stop, OutOfMemory, WriteFailed }!void { diff --git a/src/tui/editor.zig b/src/tui/editor.zig index cf48767..6875626 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -409,22 +409,6 @@ pub const Editor = struct { if (self.buffer) |p| p.set_meta(meta.written()) catch {}; } - fn count_cursels(self: *const Self) usize { - var count: usize = 0; - for (self.cursels.items) |*cursel_| if (cursel_.*) |_| { - count += 1; - }; - return count; - } - - fn count_cursels_saved(self: *const Self) usize { - var count: usize = 0; - for (self.cursels_saved.items) |*cursel_| if (cursel_.*) |_| { - count += 1; - }; - return count; - } - pub fn write_state(self: *const Self, writer: *std.Io.Writer) !void { try cbor.writeArrayHeader(writer, 10); try cbor.writeValue(writer, self.file_path orelse ""); @@ -443,7 +427,11 @@ pub const Editor = struct { } try self.view.write(writer); - try cbor.writeArrayHeader(writer, self.count_cursels()); + 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); }; @@ -818,17 +806,16 @@ pub const Editor = struct { fn store_undo_meta(self: *Self, allocator: Allocator) ![]u8 { var meta: std.Io.Writer.Allocating = .init(allocator); defer meta.deinit(); - - try cbor.writeArrayHeader(&meta.writer, 2); - - try cbor.writeArrayHeader(&meta.writer, self.count_cursels_saved()); for (self.cursels_saved.items) |*cursel_| if (cursel_.*) |*cursel| try cursel.write(&meta.writer); + return meta.toOwnedSlice(); + } - try cbor.writeArrayHeader(&meta.writer, self.count_cursels()); + fn store_current_undo_meta(self: *Self, allocator: Allocator) ![]u8 { + var meta: std.Io.Writer.Allocating = .init(allocator); + defer meta.deinit(); for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| try cursel.write(&meta.writer); - return meta.toOwnedSlice(); } @@ -852,46 +839,35 @@ pub const Editor = struct { try self.send_editor_modified(); } - fn restore_cursels_array(self: *Self, iter: *[]const u8) !void { - var len = cbor.decodeArrayHeader(iter) catch return error.UndoMetaSyntaxCurSelsError; - while (len > 0) : (len -= 1) { + fn restore_undo_redo_meta(self: *Self, meta: []const u8) !void { + if (meta.len > 0) + self.clear_all_cursors(); + var iter = meta; + while (iter.len > 0) { var cursel: CurSel = .{}; - if (!try cursel.extract(iter)) return error.UndoMetaSyntaxCurSelError; + if (!try cursel.extract(&iter)) return error.SyntaxError; (try self.cursels.addOne(self.allocator)).* = cursel; } } - fn restore_undo_meta(self: *Self, meta: []const u8) !void { - if (meta.len > 0) - self.clear_all_cursors(); - var iter = meta; - if ((cbor.decodeArrayHeader(&iter) catch return error.UndoMetaSyntaxError) != 2) return error.UndoMetaSyntaxError; - return self.restore_cursels_array(&iter); - } - - fn restore_redo_meta(self: *Self, meta: []const u8) !void { - if (meta.len > 0) - self.clear_all_cursors(); - var iter = meta; - if ((cbor.decodeArrayHeader(&iter) catch return error.UndoMetaSyntaxError) != 2) return error.UndoMetaSyntaxError; - try cbor.skipValue(&iter); // first array is pre-operation cursels - return self.restore_cursels_array(&iter); // second array is post-operation cursels - } - fn restore_undo(self: *Self) !void { if (self.pause_undo) try self.resume_undo_history(.{}); if (self.buffer) |b_mut| { try self.send_editor_jump_source(); self.cancel_all_matches(); - const meta = b_mut.undo() catch |e| switch (e) { + var sfa = std.heap.stackFallback(512, self.allocator); + const sfa_allocator = sfa.get(); + const redo_metadata = try self.store_current_undo_meta(sfa_allocator); + defer sfa_allocator.free(redo_metadata); + const meta = b_mut.undo(redo_metadata) catch |e| switch (e) { error.Stop => { self.logger.print("nothing to undo", .{}); return; }, else => return e, }; - try self.restore_undo_meta(meta); + try self.restore_undo_redo_meta(meta); try self.send_editor_jump_destination(); } } @@ -907,7 +883,7 @@ pub const Editor = struct { }, else => return e, }; - try self.restore_redo_meta(meta); + try self.restore_undo_redo_meta(meta); try self.send_editor_jump_destination(); } } @@ -3778,12 +3754,12 @@ pub const Editor = struct { } pub const toggle_comment_meta: Meta = .{ .description = "Toggle comment" }; - fn indent_cursor(self: *Self, root: Buffer.Root, cursor: Cursor, no_blank_line: bool, allocator: Allocator) error{Stop}!Buffer.Root { + fn indent_cursor(self: *Self, root: Buffer.Root, cursor: Cursor, allocator: Allocator) error{Stop}!Buffer.Root { const space = " "; var cursel: CurSel = .{}; cursel.cursor = cursor; try move_cursor_begin(root, &cursel.cursor, self.metrics); - if (no_blank_line and (root.line_width(cursel.cursor.row, self.metrics) catch 0 == 0)) + if (root.line_width(cursel.cursor.row, self.metrics) catch 0 == 0) return root; switch (self.indent_mode) { .spaces, .auto => { @@ -3803,13 +3779,13 @@ pub const Editor = struct { const sel_from_start = sel_.begin.col == 0; sel.normalize(); while (sel.begin.row < sel.end.row) : (sel.begin.row += 1) - root = try self.indent_cursor(root, sel.begin, true, allocator); + root = try self.indent_cursor(root, sel.begin, allocator); if (sel.end.col > 0) - root = try self.indent_cursor(root, sel.end, true, allocator); + root = try self.indent_cursor(root, sel.end, allocator); if (sel_from_start) sel_.begin.col = 0; return root; - } else return try self.indent_cursor(root_, cursel.cursor, self.cursels.items.len > 1, allocator); + } else return try self.indent_cursor(root_, cursel.cursor, allocator); } pub fn indent(self: *Self, ctx: Context) Result {