Compare commits
2 commits
76952a7d1b
...
8be5a14eda
| Author | SHA1 | Date | |
|---|---|---|---|
| 8be5a14eda | |||
| 5e292e75b5 |
2 changed files with 104 additions and 70 deletions
|
|
@ -46,9 +46,8 @@ auto_save: bool = false,
|
||||||
meta: ?[]const u8 = null,
|
meta: ?[]const u8 = null,
|
||||||
lsp_version: usize = 1,
|
lsp_version: usize = 1,
|
||||||
|
|
||||||
undo_history: ?*UndoNode = null,
|
undo_head: ?*UndoNode = null,
|
||||||
redo_history: ?*UndoNode = null,
|
redo_head: ?*UndoNode = null,
|
||||||
curr_history: ?*UndoNode = null,
|
|
||||||
|
|
||||||
mtime: i64,
|
mtime: i64,
|
||||||
utime: i64,
|
utime: i64,
|
||||||
|
|
@ -62,14 +61,15 @@ pub const EolModeTag = @typeInfo(EolMode).@"enum".tag_type;
|
||||||
|
|
||||||
const UndoNode = struct {
|
const UndoNode = struct {
|
||||||
root: Root,
|
root: Root,
|
||||||
next: ?*UndoNode = null,
|
next_undo: ?*UndoNode = null,
|
||||||
|
next_redo: ?*UndoNode = null,
|
||||||
branches: ?*UndoBranch = null,
|
branches: ?*UndoBranch = null,
|
||||||
meta: []const u8,
|
meta: []const u8,
|
||||||
file_eol_mode: EolMode,
|
file_eol_mode: EolMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const UndoBranch = struct {
|
const UndoBranch = struct {
|
||||||
redo: *UndoNode,
|
redo_head: *UndoNode,
|
||||||
next: ?*UndoBranch,
|
next: ?*UndoBranch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1511,9 +1511,8 @@ pub fn update(self: *Self, root: Root) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_undo(self: *Self, meta: []const u8) error{OutOfMemory}!void {
|
pub fn store_undo(self: *Self, meta: []const u8) error{OutOfMemory}!void {
|
||||||
self.push_undo(try self.create_undo(self.root, meta));
|
|
||||||
self.curr_history = null;
|
|
||||||
try self.push_redo_branch();
|
try self.push_redo_branch();
|
||||||
|
self.push_undo(try self.create_undo(self.root, meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_undo(self: *const Self, root: Root, meta_: []const u8) error{OutOfMemory}!*UndoNode {
|
fn create_undo(self: *const Self, root: Root, meta_: []const u8) error{OutOfMemory}!*UndoNode {
|
||||||
|
|
@ -1527,54 +1526,65 @@ fn create_undo(self: *const Self, root: Root, meta_: []const u8) error{OutOfMemo
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_undo(self: *Self, h: *UndoNode) void {
|
fn push_undo(self: *Self, node: *UndoNode) void {
|
||||||
const next = self.undo_history;
|
node.next_undo = self.undo_head;
|
||||||
self.undo_history = h;
|
self.undo_head = node;
|
||||||
h.next = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_redo(self: *Self, h: *UndoNode) void {
|
fn pop_undo(self: *Self) ?*UndoNode {
|
||||||
const next = self.redo_history;
|
const node = self.undo_head orelse return null;
|
||||||
self.redo_history = h;
|
self.undo_head = node.next_undo;
|
||||||
h.next = next;
|
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_branch(self: *Self) !void {
|
fn push_redo_branch(self: *Self) !void {
|
||||||
const r = self.redo_history orelse return;
|
const redo_head = self.redo_head orelse return;
|
||||||
const u = self.undo_history orelse return;
|
const undo_head = self.undo_head orelse return;
|
||||||
const next = u.branches;
|
const branch = try self.allocator.create(UndoBranch);
|
||||||
const b = try self.allocator.create(UndoBranch);
|
branch.* = .{
|
||||||
b.* = .{
|
.redo_head = redo_head,
|
||||||
.redo = r,
|
.next = undo_head.branches,
|
||||||
.next = next,
|
|
||||||
};
|
};
|
||||||
u.branches = b;
|
undo_head.branches = branch;
|
||||||
self.redo_history = null;
|
self.redo_head = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(self: *Self, meta: []const u8) error{Stop}![]const u8 {
|
pub fn undo(self: *Self) error{Stop}![]const u8 {
|
||||||
const r = self.curr_history orelse self.create_undo(self.root, meta) catch return error.Stop;
|
const node = self.pop_undo() orelse return error.Stop;
|
||||||
const h = self.undo_history orelse return error.Stop;
|
if (self.redo_head == null) blk: {
|
||||||
self.undo_history = h.next;
|
self.push_redo(self.create_undo(self.root, &.{}) catch break :blk);
|
||||||
self.curr_history = h;
|
}
|
||||||
self.root = h.root;
|
self.push_redo(node);
|
||||||
self.file_eol_mode = h.file_eol_mode;
|
self.root = node.root;
|
||||||
self.push_redo(r);
|
self.file_eol_mode = node.file_eol_mode;
|
||||||
self.mtime = std.time.milliTimestamp();
|
self.mtime = std.time.milliTimestamp();
|
||||||
return h.meta;
|
return node.meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo(self: *Self) error{Stop}![]const u8 {
|
pub fn redo(self: *Self) error{Stop}![]const u8 {
|
||||||
const u = self.curr_history orelse return error.Stop;
|
if (self.redo_head) |redo_head| if (self.root != redo_head.root)
|
||||||
const h = self.redo_history orelse return error.Stop;
|
return error.Stop;
|
||||||
if (u.root != self.root) return error.Stop;
|
const node = self.pop_redo() orelse return error.Stop;
|
||||||
self.redo_history = h.next;
|
self.push_undo(node);
|
||||||
self.curr_history = h;
|
if (self.redo_head) |head| {
|
||||||
self.root = h.root;
|
self.root = head.root;
|
||||||
self.file_eol_mode = h.file_eol_mode;
|
self.file_eol_mode = head.file_eol_mode;
|
||||||
self.push_undo(u);
|
if (head.next_redo == null)
|
||||||
|
self.redo_head = null;
|
||||||
|
}
|
||||||
self.mtime = std.time.milliTimestamp();
|
self.mtime = std.time.milliTimestamp();
|
||||||
return h.meta;
|
return node.meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_state(self: *const Self, writer: *std.Io.Writer) error{ Stop, OutOfMemory, WriteFailed }!void {
|
pub fn write_state(self: *const Self, writer: *std.Io.Writer) error{ Stop, OutOfMemory, WriteFailed }!void {
|
||||||
|
|
|
||||||
|
|
@ -409,6 +409,22 @@ pub const Editor = struct {
|
||||||
if (self.buffer) |p| p.set_meta(meta.written()) catch {};
|
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 {
|
pub fn write_state(self: *const Self, writer: *std.Io.Writer) !void {
|
||||||
try cbor.writeArrayHeader(writer, 10);
|
try cbor.writeArrayHeader(writer, 10);
|
||||||
try cbor.writeValue(writer, self.file_path orelse "");
|
try cbor.writeValue(writer, self.file_path orelse "");
|
||||||
|
|
@ -427,11 +443,7 @@ pub const Editor = struct {
|
||||||
}
|
}
|
||||||
try self.view.write(writer);
|
try self.view.write(writer);
|
||||||
|
|
||||||
var count_cursels: usize = 0;
|
try cbor.writeArrayHeader(writer, self.count_cursels());
|
||||||
for (self.cursels.items) |*cursel_| if (cursel_.*) |_| {
|
|
||||||
count_cursels += 1;
|
|
||||||
};
|
|
||||||
try cbor.writeArrayHeader(writer, count_cursels);
|
|
||||||
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
|
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
|
||||||
try cursel.write(writer);
|
try cursel.write(writer);
|
||||||
};
|
};
|
||||||
|
|
@ -806,16 +818,17 @@ pub const Editor = struct {
|
||||||
fn store_undo_meta(self: *Self, allocator: Allocator) ![]u8 {
|
fn store_undo_meta(self: *Self, allocator: Allocator) ![]u8 {
|
||||||
var meta: std.Io.Writer.Allocating = .init(allocator);
|
var meta: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer meta.deinit();
|
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|
|
for (self.cursels_saved.items) |*cursel_| if (cursel_.*) |*cursel|
|
||||||
try cursel.write(&meta.writer);
|
try cursel.write(&meta.writer);
|
||||||
return meta.toOwnedSlice();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_current_undo_meta(self: *Self, allocator: Allocator) ![]u8 {
|
try cbor.writeArrayHeader(&meta.writer, self.count_cursels());
|
||||||
var meta: std.Io.Writer.Allocating = .init(allocator);
|
|
||||||
defer meta.deinit();
|
|
||||||
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel|
|
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel|
|
||||||
try cursel.write(&meta.writer);
|
try cursel.write(&meta.writer);
|
||||||
|
|
||||||
return meta.toOwnedSlice();
|
return meta.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -839,15 +852,30 @@ pub const Editor = struct {
|
||||||
try self.send_editor_modified();
|
try self.send_editor_modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_undo_redo_meta(self: *Self, meta: []const u8) !void {
|
fn restore_cursels_array(self: *Self, iter: *[]const u8) !void {
|
||||||
|
var len = cbor.decodeArrayHeader(iter) catch return error.UndoMetaSyntaxCurSelsError;
|
||||||
|
while (len > 0) : (len -= 1) {
|
||||||
|
var cursel: CurSel = .{};
|
||||||
|
if (!try cursel.extract(iter)) return error.UndoMetaSyntaxCurSelError;
|
||||||
|
(try self.cursels.addOne(self.allocator)).* = cursel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore_undo_meta(self: *Self, meta: []const u8) !void {
|
||||||
if (meta.len > 0)
|
if (meta.len > 0)
|
||||||
self.clear_all_cursors();
|
self.clear_all_cursors();
|
||||||
var iter = meta;
|
var iter = meta;
|
||||||
while (iter.len > 0) {
|
if ((cbor.decodeArrayHeader(&iter) catch return error.UndoMetaSyntaxError) != 2) return error.UndoMetaSyntaxError;
|
||||||
var cursel: CurSel = .{};
|
return self.restore_cursels_array(&iter);
|
||||||
if (!try cursel.extract(&iter)) return error.SyntaxError;
|
|
||||||
(try self.cursels.addOne(self.allocator)).* = cursel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
fn restore_undo(self: *Self) !void {
|
||||||
|
|
@ -856,18 +884,14 @@ pub const Editor = struct {
|
||||||
if (self.buffer) |b_mut| {
|
if (self.buffer) |b_mut| {
|
||||||
try self.send_editor_jump_source();
|
try self.send_editor_jump_source();
|
||||||
self.cancel_all_matches();
|
self.cancel_all_matches();
|
||||||
var sfa = std.heap.stackFallback(512, self.allocator);
|
const meta = b_mut.undo() catch |e| switch (e) {
|
||||||
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 => {
|
error.Stop => {
|
||||||
self.logger.print("nothing to undo", .{});
|
self.logger.print("nothing to undo", .{});
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
else => return e,
|
else => return e,
|
||||||
};
|
};
|
||||||
try self.restore_undo_redo_meta(meta);
|
try self.restore_undo_meta(meta);
|
||||||
try self.send_editor_jump_destination();
|
try self.send_editor_jump_destination();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -883,7 +907,7 @@ pub const Editor = struct {
|
||||||
},
|
},
|
||||||
else => return e,
|
else => return e,
|
||||||
};
|
};
|
||||||
try self.restore_undo_redo_meta(meta);
|
try self.restore_redo_meta(meta);
|
||||||
try self.send_editor_jump_destination();
|
try self.send_editor_jump_destination();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3754,12 +3778,12 @@ pub const Editor = struct {
|
||||||
}
|
}
|
||||||
pub const toggle_comment_meta: Meta = .{ .description = "Toggle comment" };
|
pub const toggle_comment_meta: Meta = .{ .description = "Toggle comment" };
|
||||||
|
|
||||||
fn indent_cursor(self: *Self, root: Buffer.Root, cursor: Cursor, allocator: Allocator) error{Stop}!Buffer.Root {
|
fn indent_cursor(self: *Self, root: Buffer.Root, cursor: Cursor, no_blank_line: bool, allocator: Allocator) error{Stop}!Buffer.Root {
|
||||||
const space = " ";
|
const space = " ";
|
||||||
var cursel: CurSel = .{};
|
var cursel: CurSel = .{};
|
||||||
cursel.cursor = cursor;
|
cursel.cursor = cursor;
|
||||||
try move_cursor_begin(root, &cursel.cursor, self.metrics);
|
try move_cursor_begin(root, &cursel.cursor, self.metrics);
|
||||||
if (root.line_width(cursel.cursor.row, self.metrics) catch 0 == 0)
|
if (no_blank_line and (root.line_width(cursel.cursor.row, self.metrics) catch 0 == 0))
|
||||||
return root;
|
return root;
|
||||||
switch (self.indent_mode) {
|
switch (self.indent_mode) {
|
||||||
.spaces, .auto => {
|
.spaces, .auto => {
|
||||||
|
|
@ -3779,13 +3803,13 @@ pub const Editor = struct {
|
||||||
const sel_from_start = sel_.begin.col == 0;
|
const sel_from_start = sel_.begin.col == 0;
|
||||||
sel.normalize();
|
sel.normalize();
|
||||||
while (sel.begin.row < sel.end.row) : (sel.begin.row += 1)
|
while (sel.begin.row < sel.end.row) : (sel.begin.row += 1)
|
||||||
root = try self.indent_cursor(root, sel.begin, allocator);
|
root = try self.indent_cursor(root, sel.begin, true, allocator);
|
||||||
if (sel.end.col > 0)
|
if (sel.end.col > 0)
|
||||||
root = try self.indent_cursor(root, sel.end, allocator);
|
root = try self.indent_cursor(root, sel.end, true, allocator);
|
||||||
if (sel_from_start)
|
if (sel_from_start)
|
||||||
sel_.begin.col = 0;
|
sel_.begin.col = 0;
|
||||||
return root;
|
return root;
|
||||||
} else return try self.indent_cursor(root_, cursel.cursor, allocator);
|
} else return try self.indent_cursor(root_, cursel.cursor, self.cursels.items.len > 1, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indent(self: *Self, ctx: Context) Result {
|
pub fn indent(self: *Self, ctx: Context) Result {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue