init
This commit is contained in:
271
src/root.zig
271
src/root.zig
@@ -38,7 +38,14 @@ const Action = struct {
|
||||
old_length: usize,
|
||||
};
|
||||
|
||||
const ActionGroup = union(ActionType) {
|
||||
const ActionGroup = struct {
|
||||
const PieceRedo = struct {
|
||||
piece: Piece,
|
||||
index: usize,
|
||||
};
|
||||
|
||||
/// holds the min amount of info inorder to restore a change
|
||||
undo_action: union(ActionType) {
|
||||
/// this will only occur when the table was already empty so boof it
|
||||
append: void,
|
||||
|
||||
@@ -51,6 +58,18 @@ const ActionGroup = union(ActionType) {
|
||||
/// happens when a piece is deletes (length set to zero)
|
||||
/// appends all modifed pieces to be restored
|
||||
delete: std.ArrayList(Action),
|
||||
},
|
||||
|
||||
/// just keeps the function args, best way to redo something is to redo it
|
||||
redo_action: union(ActionType) {
|
||||
append: Piece,
|
||||
insert: PieceRedo,
|
||||
insert_splits_piece: PieceRedo,
|
||||
delete: struct {
|
||||
from: usize,
|
||||
to: usize,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/// abstracted so i can implement redo
|
||||
@@ -59,20 +78,36 @@ const ActionList = struct {
|
||||
|
||||
actions: std.ArrayList(ActionGroup) = .empty,
|
||||
|
||||
/// offsets from the last index i down to zero
|
||||
undo_offset: usize = 0,
|
||||
|
||||
pub const empty = ActionList{};
|
||||
|
||||
pub fn append(self: *Self, alloc: std.mem.Allocator, action_group: ActionGroup) !void {
|
||||
if (self.undo_offset != 0) {
|
||||
try self.actions.resize(alloc, self.actions.items.len - 1 - self.undo_offset);
|
||||
self.undo_offset = 0;
|
||||
}
|
||||
|
||||
try self.actions.append(alloc, action_group);
|
||||
}
|
||||
|
||||
pub fn pop(self: *Self) ?ActionGroup {
|
||||
return self.actions.pop();
|
||||
pub fn undo(self: *Self) ?ActionGroup {
|
||||
if (self.undo_offset == self.actions.items.len) return null;
|
||||
defer self.undo_offset += 1;
|
||||
return self.actions.items[self.actions.items.len - 1 - self.undo_offset];
|
||||
}
|
||||
|
||||
pub fn redo(self: *Self) ?ActionGroup {
|
||||
if (self.undo_offset == 0) return null;
|
||||
self.undo_offset -= 1;
|
||||
return self.actions.items[self.actions.items.len - 1 - self.undo_offset];
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
||||
for (self.actions.items) |*act| {
|
||||
switch (act.*) {
|
||||
.delete => act.delete.deinit(alloc),
|
||||
switch (act.undo_action) {
|
||||
.delete => act.undo_action.delete.deinit(alloc),
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
@@ -103,6 +138,92 @@ const PieceTable = struct {
|
||||
/// piece : the piece to add
|
||||
/// index : the index in the text where the new piece will be added
|
||||
pub fn insert(self: *Self, alloc: Alloc, piece: Piece, index: usize) !void {
|
||||
return self.insertWActionDisable(alloc, piece, index, false);
|
||||
}
|
||||
|
||||
/// will delete all from (index) to (index)
|
||||
/// self : piece table that is being operated on
|
||||
/// alloc : allocator used for adding the actions to the action list.
|
||||
/// from : the index to be deleted from
|
||||
/// to : the index the deletion will stop
|
||||
pub fn delete(self: *Self, alloc: std.mem.Allocator, from: usize, to: usize) !void {
|
||||
return self.deleteWActionDisable(alloc, from, to, false);
|
||||
}
|
||||
|
||||
fn deleteWActionDisable(self: *Self, alloc: std.mem.Allocator, from: usize, to: usize, action_disabled: bool) !void {
|
||||
var currnet_index: usize = 0;
|
||||
var edited_piece_index: usize = 0;
|
||||
var delete_actions: std.ArrayList(Action) = .empty;
|
||||
|
||||
// from set
|
||||
for (self.pieces.items, 0..) |p, i| {
|
||||
// find the index where the split occurs
|
||||
if (currnet_index + p.length > from) {
|
||||
// we gotta split the piece
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
if (!action_disabled) try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
},
|
||||
);
|
||||
|
||||
current_piece.length -= from - currnet_index;
|
||||
edited_piece_index = i;
|
||||
|
||||
break;
|
||||
}
|
||||
if (currnet_index + p.length <= from) currnet_index += p.length;
|
||||
}
|
||||
|
||||
// to set
|
||||
|
||||
for (self.pieces.items, edited_piece_index..) |p, i| {
|
||||
if (currnet_index + p.length < to) {
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
if (!action_disabled) try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
},
|
||||
);
|
||||
|
||||
current_piece.length = 0;
|
||||
currnet_index += p.length;
|
||||
}
|
||||
|
||||
if (currnet_index + p.length >= to) {
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
if (!action_disabled) try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
},
|
||||
);
|
||||
|
||||
// shrink it
|
||||
current_piece.start += to - currnet_index;
|
||||
current_piece.length -= to - currnet_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!action_disabled) try self.actions.append(alloc, ActionGroup{
|
||||
.undo_action = .{ .delete = delete_actions },
|
||||
.redo_action = .{ .delete = .{ .from = from, .to = to } },
|
||||
});
|
||||
}
|
||||
|
||||
fn insertWActionDisable(self: *Self, alloc: Alloc, piece: Piece, index: usize, action_disabled: bool) !void {
|
||||
var currnet_start: usize = 0;
|
||||
|
||||
for (self.pieces.items, 0..) |p, i| {
|
||||
@@ -126,18 +247,30 @@ const PieceTable = struct {
|
||||
};
|
||||
|
||||
try self.pieces.replaceRange(alloc, i, 1, &pieces);
|
||||
try self.actions.append(alloc, .{
|
||||
if (!action_disabled) try self.actions.append(alloc, ActionGroup{
|
||||
.undo_action = .{
|
||||
.insert_splits_piece = Action{
|
||||
.piece_table_index = i,
|
||||
.old_length = p.length,
|
||||
.old_start = p.start,
|
||||
},
|
||||
},
|
||||
.redo_action = .{ .insert_splits_piece = .{
|
||||
.piece = piece,
|
||||
.index = index,
|
||||
} },
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (currnet_start + p.length == index) {
|
||||
try self.pieces.insert(alloc, i + 1, piece);
|
||||
try self.actions.append(alloc, .{ .insert = i + 1 });
|
||||
if (!action_disabled) try self.actions.append(alloc, ActionGroup{
|
||||
.undo_action = .{ .insert = i + 1 },
|
||||
.redo_action = .{ .insert = .{
|
||||
.piece = piece,
|
||||
.index = index,
|
||||
} },
|
||||
});
|
||||
return;
|
||||
} // just append after
|
||||
if (currnet_start + p.length < index) currnet_start += p.length;
|
||||
@@ -145,99 +278,37 @@ const PieceTable = struct {
|
||||
|
||||
// if there are no items this runs
|
||||
try self.pieces.append(alloc, piece);
|
||||
try self.actions.append(alloc, .append);
|
||||
if (!action_disabled) try self.actions.append(alloc, ActionGroup{
|
||||
.undo_action = .append,
|
||||
.redo_action = .{ .append = piece },
|
||||
});
|
||||
}
|
||||
|
||||
/// will delete all from (index) to (index)
|
||||
/// self : piece table that is being operated on
|
||||
/// alloc : allocator used for adding the actions to the action list.
|
||||
/// from : the index to be deleted from
|
||||
/// to : the index the deletion will stop
|
||||
pub fn delete(self: *Self, alloc: std.mem.Allocator, from: usize, to: usize) !void {
|
||||
var currnet_index: usize = 0;
|
||||
var edited_piece_index: usize = 0;
|
||||
var delete_actions: std.ArrayList(Action) = .empty;
|
||||
pub fn redo(self: *Self, alloc: std.mem.Allocator) !void {
|
||||
const action_group = self.actions.redo() orelse return;
|
||||
|
||||
// from set
|
||||
for (self.pieces.items, 0..) |p, i| {
|
||||
// find the index where the split occurs
|
||||
if (currnet_index + p.length > from) {
|
||||
// we gotta split the piece
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
switch (action_group.redo_action) {
|
||||
.append => |piece| {
|
||||
try self.insertWActionDisable(alloc, piece, 0, true);
|
||||
},
|
||||
);
|
||||
|
||||
current_piece.length -= from - currnet_index;
|
||||
edited_piece_index = i;
|
||||
|
||||
break;
|
||||
}
|
||||
if (currnet_index + p.length <= from) currnet_index += p.length;
|
||||
}
|
||||
|
||||
// to set
|
||||
|
||||
for (self.pieces.items, edited_piece_index..) |p, i| {
|
||||
if (currnet_index + p.length < to) {
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
.insert, .insert_splits_piece => |action| {
|
||||
try self.insertWActionDisable(alloc, action.piece, action.index, true);
|
||||
},
|
||||
);
|
||||
|
||||
current_piece.length = 0;
|
||||
currnet_index += p.length;
|
||||
}
|
||||
|
||||
if (currnet_index + p.length >= to) {
|
||||
var current_piece = &self.pieces.items[i];
|
||||
|
||||
try delete_actions.append(
|
||||
alloc,
|
||||
Action{
|
||||
.piece_table_index = i,
|
||||
.old_start = current_piece.start,
|
||||
.old_length = current_piece.length,
|
||||
.delete => |deletes| {
|
||||
try self.deleteWActionDisable(alloc, deletes.from, deletes.to, true);
|
||||
},
|
||||
);
|
||||
|
||||
// shrink it
|
||||
current_piece.start += to - currnet_index;
|
||||
current_piece.length -= to - currnet_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try self.actions.append(alloc, .{ .delete = delete_actions });
|
||||
}
|
||||
|
||||
pub fn undo(self: *Self, alloc: std.mem.Allocator) !void {
|
||||
var action_to_undo = self.actions.pop() orelse return;
|
||||
|
||||
switch (action_to_undo) {
|
||||
var action_group = self.actions.undo() orelse return;
|
||||
switch (action_group.undo_action) {
|
||||
.append => {
|
||||
self.pieces.deinit(alloc);
|
||||
self.pieces = .empty;
|
||||
},
|
||||
.insert => |insert_index| {
|
||||
|
||||
// for the small price of one alloc you too can just swap nothing in
|
||||
// might be faster, dont know should test
|
||||
// [TODO] test if this is faster
|
||||
try self.pieces.append(alloc, .none);
|
||||
_ = self.pieces.swapRemove(insert_index);
|
||||
_ = self.pieces.orderedRemove(insert_index);
|
||||
},
|
||||
.insert_splits_piece => |action| {
|
||||
|
||||
@@ -258,7 +329,7 @@ const PieceTable = struct {
|
||||
piece.start = action.old_start;
|
||||
}
|
||||
|
||||
action_to_undo.delete.deinit(alloc);
|
||||
action_group.undo_action.delete.deinit(alloc);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -315,5 +386,33 @@ test "PieceTable insert" {
|
||||
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
|
||||
try piece_table.redo(std.testing.allocator);
|
||||
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
|
||||
for (0..5) |_| {
|
||||
try piece_table.insert(std.testing.allocator, .{
|
||||
.text = &"a",
|
||||
.start = 0,
|
||||
.length = 1,
|
||||
}, 20);
|
||||
}
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
try piece_table.undo(std.testing.allocator);
|
||||
std.debug.print("{f}\n", .{piece_table});
|
||||
// try piece_table.undo(std.testing.allocator);
|
||||
// std.debug.print("{f}\n", .{piece_table});
|
||||
|
||||
piece_table.deinit(std.testing.allocator);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user