135 lines
3.8 KiB
Zig
135 lines
3.8 KiB
Zig
const std = @import("std");
|
|
|
|
const CommandIF = @import("./command/root.zig").Interface;
|
|
|
|
///
|
|
const Self = @This();
|
|
|
|
///
|
|
const CommandList = std.ArrayList(CommandIF);
|
|
|
|
///
|
|
commands: CommandList,
|
|
|
|
/// next command to be applied by redoing
|
|
head: usize,
|
|
|
|
///
|
|
pub fn init(alloc: std.mem.Allocator) Self {
|
|
return Self {
|
|
.commands = CommandList.init(alloc),
|
|
.head = 0,
|
|
};
|
|
}
|
|
|
|
///
|
|
pub fn deinit(self: *Self) void {
|
|
for (self.commands.items) |*command| {
|
|
command.deinit();
|
|
}
|
|
self.commands.deinit();
|
|
}
|
|
|
|
///
|
|
pub fn exec(self: *Self, command: CommandIF) !void {
|
|
if (self.head >= self.commands.items.len) {
|
|
try self.commands.append(command);
|
|
} else {
|
|
try self.commands.resize(self.head + 1);
|
|
self.commands.items[self.head] = command;
|
|
}
|
|
errdefer _ = self.commands.pop();
|
|
|
|
self.head = self.commands.items.len;
|
|
try command.apply();
|
|
}
|
|
|
|
///
|
|
pub fn undo(self: *Self) !void {
|
|
if (self.head == 0) {
|
|
return error.NoCommand;
|
|
}
|
|
self.head -= 1;
|
|
errdefer self.head += 1;
|
|
|
|
try self.commands.items[self.head].revert();
|
|
}
|
|
|
|
///
|
|
pub fn redo(self: *Self) !void {
|
|
if (self.head >= self.commands.items.len) {
|
|
return error.NoCommand;
|
|
}
|
|
try self.commands.items[self.head].apply();
|
|
self.head += 1;
|
|
}
|
|
|
|
///
|
|
pub fn isUndoAvailable(self: *const Self) bool {
|
|
return self.head > 0;
|
|
}
|
|
///
|
|
pub fn isRedoAvailable(self: *const Self) bool {
|
|
return self.head < self.commands.items.len;
|
|
}
|
|
|
|
test {
|
|
var value1: i32 = 0;
|
|
var value2: i32 = 0;
|
|
|
|
var history = init(std.testing.allocator);
|
|
defer history.deinit();
|
|
|
|
try std.testing.expectEqual(false, history.isUndoAvailable());
|
|
try std.testing.expectEqual(false, history.isRedoAvailable());
|
|
try std.testing.expectError(error.NoCommand, history.undo());
|
|
try std.testing.expectError(error.NoCommand, history.redo());
|
|
|
|
try history.exec(
|
|
try CommandIF.make(std.testing.allocator, CommandIF.Mock { .target = &value1, }));
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), false);
|
|
try std.testing.expectEqual(value1, 1);
|
|
|
|
try history.undo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), false);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), true);
|
|
try std.testing.expectEqual(value1, 0);
|
|
|
|
try history.redo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), false);
|
|
try std.testing.expectEqual(value1, 1);
|
|
|
|
try history.exec(
|
|
try CommandIF.make(std.testing.allocator, CommandIF.Mock { .target = &value2, }));
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), false);
|
|
try std.testing.expectEqual(value1, 1);
|
|
try std.testing.expectEqual(value2, 1);
|
|
|
|
try history.undo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), true);
|
|
try std.testing.expectEqual(value1, 1);
|
|
try std.testing.expectEqual(value2, 0);
|
|
|
|
try history.redo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), false);
|
|
try std.testing.expectEqual(value1, 1);
|
|
try std.testing.expectEqual(value2, 1);
|
|
|
|
try history.undo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), true);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), true);
|
|
try std.testing.expectEqual(value1, 1);
|
|
try std.testing.expectEqual(value2, 0);
|
|
|
|
try history.undo();
|
|
try std.testing.expectEqual(history.isUndoAvailable(), false);
|
|
try std.testing.expectEqual(history.isRedoAvailable(), true);
|
|
try std.testing.expectEqual(value1, 0);
|
|
try std.testing.expectEqual(value2, 0);
|
|
}
|