Merge pull request #2761 from ghostty-org/push-zspzyylxlsvr

input: Binding set clone must deep clone actions
This commit is contained in:
Mitchell Hashimoto
2024-11-21 10:11:27 -08:00
committed by GitHub

View File

@ -421,6 +421,8 @@ pub const Action = union(enum) {
/// ///
crash: CrashThread, crash: CrashThread,
pub const Key = @typeInfo(Action).Union.tag_type.?;
pub const CrashThread = enum { pub const CrashThread = enum {
main, main,
io, io,
@ -430,6 +432,16 @@ pub const Action = union(enum) {
pub const CursorKey = struct { pub const CursorKey = struct {
normal: []const u8, normal: []const u8,
application: []const u8, application: []const u8,
pub fn clone(
self: CursorKey,
alloc: Allocator,
) Allocator.Error!CursorKey {
return .{
.normal = try alloc.dupe(u8, self.normal),
.application = try alloc.dupe(u8, self.application),
};
}
}; };
pub const AdjustSelection = enum { pub const AdjustSelection = enum {
@ -773,6 +785,50 @@ pub const Action = union(enum) {
} }
} }
/// Clone this action with the given allocator. The allocator
/// should be an arena-style allocator since fine-grained
/// deallocation is not possible.
pub fn clone(self: Action, alloc: Allocator) Allocator.Error!Action {
return switch (self) {
inline else => |value, tag| @unionInit(
Action,
@tagName(tag),
try cloneValue(alloc, value),
),
};
}
fn cloneValue(
alloc: Allocator,
value: anytype,
) Allocator.Error!@TypeOf(value) {
return switch (@typeInfo(@TypeOf(value))) {
.Void,
.Int,
.Float,
.Enum,
=> value,
.Pointer => |info| slice: {
comptime assert(info.size == .Slice);
break :slice try alloc.dupe(
info.child,
value,
);
},
.Struct => |info| if (info.is_tuple)
value
else
try value.clone(alloc),
else => {
@compileLog(@TypeOf(value));
@compileError("unexpected type");
},
};
}
/// Returns a hash code that can be used to uniquely identify this /// Returns a hash code that can be used to uniquely identify this
/// action. /// action.
pub fn hash(self: Action) u64 { pub fn hash(self: Action) u64 {
@ -1101,6 +1157,16 @@ pub const Set = struct {
action: Action, action: Action,
flags: Flags, flags: Flags,
pub fn clone(
self: Leaf,
alloc: Allocator,
) Allocator.Error!Leaf {
return .{
.action = try self.action.clone(alloc),
.flags = self.flags,
};
}
pub fn hash(self: Leaf) u64 { pub fn hash(self: Leaf) u64 {
var hasher = std.hash.Wyhash.init(0); var hasher = std.hash.Wyhash.init(0);
self.action.hash(&hasher); self.action.hash(&hasher);
@ -1390,8 +1456,9 @@ pub const Set = struct {
// If we have any leaders we need to clone them. // If we have any leaders we need to clone them.
var it = result.bindings.iterator(); var it = result.bindings.iterator();
while (it.next()) |entry| switch (entry.value_ptr.*) { while (it.next()) |entry| switch (entry.value_ptr.*) {
// No data to clone // Leaves could have data to clone (i.e. text actions
.leaf => {}, // contain allocated strings).
.leaf => |*s| s.* = try s.clone(alloc),
// Must be deep cloned. // Must be deep cloned.
.leader => |*s| { .leader => |*s| {
@ -2128,3 +2195,22 @@ test "set: consumed state" {
try testing.expect(s.get(.{ .key = .{ .translated = .a } }).?.value_ptr.* == .leaf); try testing.expect(s.get(.{ .key = .{ .translated = .a } }).?.value_ptr.* == .leaf);
try testing.expect(s.get(.{ .key = .{ .translated = .a } }).?.value_ptr.*.leaf.flags.consumed); try testing.expect(s.get(.{ .key = .{ .translated = .a } }).?.value_ptr.*.leaf.flags.consumed);
} }
test "Action: clone" {
const testing = std.testing;
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
{
var a: Action = .ignore;
const b = try a.clone(alloc);
try testing.expect(b == .ignore);
}
{
var a: Action = .{ .text = "foo" };
const b = try a.clone(alloc);
try testing.expect(b == .text);
}
}