init
This commit is contained in:
303
src/root.zig
303
src/root.zig
@@ -38,19 +38,38 @@ const Action = struct {
|
|||||||
old_length: usize,
|
old_length: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActionGroup = union(ActionType) {
|
const ActionGroup = struct {
|
||||||
/// this will only occur when the table was already empty so boof it
|
const PieceRedo = struct {
|
||||||
append: void,
|
piece: Piece,
|
||||||
|
index: usize,
|
||||||
|
};
|
||||||
|
|
||||||
/// happens when a piece is cleanly inserted in to the table
|
/// holds the min amount of info inorder to restore a change
|
||||||
insert: usize,
|
undo_action: union(ActionType) {
|
||||||
|
/// this will only occur when the table was already empty so boof it
|
||||||
|
append: void,
|
||||||
|
|
||||||
/// happens when the piece requires to be split before indexing
|
/// happens when a piece is cleanly inserted in to the table
|
||||||
insert_splits_piece: Action,
|
insert: usize,
|
||||||
|
|
||||||
/// happens when a piece is deletes (length set to zero)
|
/// happens when the piece requires to be split before indexing
|
||||||
/// appends all modifed pieces to be restored
|
insert_splits_piece: Action,
|
||||||
delete: std.ArrayList(Action),
|
|
||||||
|
/// 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
|
/// abstracted so i can implement redo
|
||||||
@@ -59,20 +78,36 @@ const ActionList = struct {
|
|||||||
|
|
||||||
actions: std.ArrayList(ActionGroup) = .empty,
|
actions: std.ArrayList(ActionGroup) = .empty,
|
||||||
|
|
||||||
|
/// offsets from the last index i down to zero
|
||||||
|
undo_offset: usize = 0,
|
||||||
|
|
||||||
pub const empty = ActionList{};
|
pub const empty = ActionList{};
|
||||||
|
|
||||||
pub fn append(self: *Self, alloc: std.mem.Allocator, action_group: ActionGroup) !void {
|
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);
|
try self.actions.append(alloc, action_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(self: *Self) ?ActionGroup {
|
pub fn undo(self: *Self) ?ActionGroup {
|
||||||
return self.actions.pop();
|
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 {
|
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
||||||
for (self.actions.items) |*act| {
|
for (self.actions.items) |*act| {
|
||||||
switch (act.*) {
|
switch (act.undo_action) {
|
||||||
.delete => act.delete.deinit(alloc),
|
.delete => act.undo_action.delete.deinit(alloc),
|
||||||
else => continue,
|
else => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +138,92 @@ const PieceTable = struct {
|
|||||||
/// piece : the piece to add
|
/// piece : the piece to add
|
||||||
/// index : the index in the text where the new piece will be added
|
/// 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 {
|
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;
|
var currnet_start: usize = 0;
|
||||||
|
|
||||||
for (self.pieces.items, 0..) |p, i| {
|
for (self.pieces.items, 0..) |p, i| {
|
||||||
@@ -126,18 +247,30 @@ const PieceTable = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try self.pieces.replaceRange(alloc, i, 1, &pieces);
|
try self.pieces.replaceRange(alloc, i, 1, &pieces);
|
||||||
try self.actions.append(alloc, .{
|
if (!action_disabled) try self.actions.append(alloc, ActionGroup{
|
||||||
.insert_splits_piece = Action{
|
.undo_action = .{
|
||||||
.piece_table_index = i,
|
.insert_splits_piece = Action{
|
||||||
.old_length = p.length,
|
.piece_table_index = i,
|
||||||
.old_start = p.start,
|
.old_length = p.length,
|
||||||
|
.old_start = p.start,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
.redo_action = .{ .insert_splits_piece = .{
|
||||||
|
.piece = piece,
|
||||||
|
.index = index,
|
||||||
|
} },
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currnet_start + p.length == index) {
|
if (currnet_start + p.length == index) {
|
||||||
try self.pieces.insert(alloc, i + 1, piece);
|
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;
|
return;
|
||||||
} // just append after
|
} // just append after
|
||||||
if (currnet_start + p.length < index) currnet_start += p.length;
|
if (currnet_start + p.length < index) currnet_start += p.length;
|
||||||
@@ -145,99 +278,37 @@ const PieceTable = struct {
|
|||||||
|
|
||||||
// if there are no items this runs
|
// if there are no items this runs
|
||||||
try self.pieces.append(alloc, piece);
|
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)
|
pub fn redo(self: *Self, alloc: std.mem.Allocator) !void {
|
||||||
/// self : piece table that is being operated on
|
const action_group = self.actions.redo() orelse return;
|
||||||
/// 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;
|
|
||||||
|
|
||||||
// from set
|
switch (action_group.redo_action) {
|
||||||
for (self.pieces.items, 0..) |p, i| {
|
.append => |piece| {
|
||||||
// find the index where the split occurs
|
try self.insertWActionDisable(alloc, piece, 0, true);
|
||||||
if (currnet_index + p.length > from) {
|
},
|
||||||
// we gotta split the piece
|
.insert, .insert_splits_piece => |action| {
|
||||||
var current_piece = &self.pieces.items[i];
|
try self.insertWActionDisable(alloc, action.piece, action.index, true);
|
||||||
|
},
|
||||||
try delete_actions.append(
|
.delete => |deletes| {
|
||||||
alloc,
|
try self.deleteWActionDisable(alloc, deletes.from, deletes.to, true);
|
||||||
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];
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.actions.append(alloc, .{ .delete = delete_actions });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(self: *Self, alloc: std.mem.Allocator) !void {
|
pub fn undo(self: *Self, alloc: std.mem.Allocator) !void {
|
||||||
var action_to_undo = self.actions.pop() orelse return;
|
var action_group = self.actions.undo() orelse return;
|
||||||
|
switch (action_group.undo_action) {
|
||||||
switch (action_to_undo) {
|
|
||||||
.append => {
|
.append => {
|
||||||
self.pieces.deinit(alloc);
|
self.pieces.deinit(alloc);
|
||||||
self.pieces = .empty;
|
self.pieces = .empty;
|
||||||
},
|
},
|
||||||
.insert => |insert_index| {
|
.insert => |insert_index| {
|
||||||
|
_ = self.pieces.orderedRemove(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);
|
|
||||||
},
|
},
|
||||||
.insert_splits_piece => |action| {
|
.insert_splits_piece => |action| {
|
||||||
|
|
||||||
@@ -258,7 +329,7 @@ const PieceTable = struct {
|
|||||||
piece.start = action.old_start;
|
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});
|
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);
|
piece_table.deinit(std.testing.allocator);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user