Merge pull request #2128 from jcollie/kitty-color-protocol-fixup

Clean up Kitty color protocol by using a union
This commit is contained in:
Mitchell Hashimoto
2024-08-24 19:57:44 -07:00
committed by GitHub
3 changed files with 107 additions and 147 deletions

View File

@ -18,27 +18,26 @@ pub const OSC = struct {
terminator: Terminator = .st, terminator: Terminator = .st,
}; };
pub const Kind = enum(u9) { pub const Special = enum {
// Make sure that this stays in sync with the highest numbered enum foreground,
// value. background,
pub const max: u9 = 263; selection_foreground,
selection_background,
cursor,
cursor_text,
visual_bell,
second_transparent_background,
};
// These _must_ start at 256 since enum values 0-255 are reserved pub const Kind = union(enum) {
// for the palette. pub const max: usize = std.math.maxInt(u8) + @typeInfo(Special).Enum.fields.len;
foreground = 256,
background = 257,
selection_foreground = 258,
selection_background = 259,
cursor = 260,
cursor_text = 261,
visual_bell = 262,
second_transparent_background = 263,
_,
/// Return the palette index that this kind is representing palette: u8,
/// or null if its a special color. special: Special,
pub fn palette(self: Kind) ?u8 {
return std.math.cast(u8, @intFromEnum(self)) orelse null; pub fn parse(key: []const u8) ?Kind {
if (std.meta.stringToEnum(Special, key)) |s| return .{ .special = s };
return .{ .palette = std.fmt.parseUnsigned(u8, key, 10) catch return null };
} }
pub fn format( pub fn format(
@ -50,43 +49,23 @@ pub const Kind = enum(u9) {
_ = layout; _ = layout;
_ = opts; _ = opts;
// Format as a number if its a palette color otherwise switch (self) {
// format as a string. .palette => |p| try writer.print("{d}", .{p}),
if (self.palette()) |idx| { .special => |s| try writer.print("{s}", .{@tagName(s)}),
try writer.print("{}", .{idx});
} else {
try writer.print("{s}", .{@tagName(self)});
} }
} }
}; };
test "OSC: kitty color protocol kind" {
const info = @typeInfo(Kind);
try std.testing.expectEqual(false, info.Enum.is_exhaustive);
var min: usize = std.math.maxInt(info.Enum.tag_type);
var max: usize = 0;
inline for (info.Enum.fields) |field| {
if (field.value > max) max = field.value;
if (field.value < min) min = field.value;
}
try std.testing.expect(min >= 256);
try std.testing.expect(max == Kind.max);
}
test "OSC: kitty color protocol kind string" { test "OSC: kitty color protocol kind string" {
const testing = std.testing; const testing = std.testing;
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
{ {
const actual = try std.fmt.bufPrint(&buf, "{}", .{Kind.foreground}); const actual = try std.fmt.bufPrint(&buf, "{}", .{Kind{ .special = .foreground }});
try testing.expectEqualStrings("foreground", actual); try testing.expectEqualStrings("foreground", actual);
} }
{ {
const actual = try std.fmt.bufPrint(&buf, "{}", .{@as(Kind, @enumFromInt(42))}); const actual = try std.fmt.bufPrint(&buf, "{}", .{Kind{ .palette = 42 }});
try testing.expectEqualStrings("42", actual); try testing.expectEqualStrings("42", actual);
} }
} }

View File

@ -1006,19 +1006,10 @@ pub const Parser = struct {
return; return;
} }
// For our key, we first try to parse it as a special key. If that const key = kitty.color.Kind.parse(self.temp_state.key) orelse {
// doesn't work then we try to parse it as a number for a palette.
const key: kitty.color.Kind = std.meta.stringToEnum(
kitty.color.Kind,
self.temp_state.key,
) orelse @enumFromInt(std.fmt.parseUnsigned(
u8,
self.temp_state.key,
10,
) catch {
log.warn("unknown key in kitty color protocol: {s}", .{self.temp_state.key}); log.warn("unknown key in kitty color protocol: {s}", .{self.temp_state.key});
return; return;
}); };
const value = value: { const value = value: {
if (self.buf_start == self.buf_idx) break :value ""; if (self.buf_start == self.buf_idx) break :value "";
@ -1633,6 +1624,7 @@ test "OSC: hyperlink end" {
test "OSC: kitty color protocol" { test "OSC: kitty color protocol" {
const testing = std.testing; const testing = std.testing;
const Kind = kitty.color.Kind;
var p: Parser = .{ .alloc = testing.allocator }; var p: Parser = .{ .alloc = testing.allocator };
defer p.deinit(); defer p.deinit();
@ -1646,12 +1638,12 @@ test "OSC: kitty color protocol" {
{ {
const item = cmd.kitty_color_protocol.list.items[0]; const item = cmd.kitty_color_protocol.list.items[0];
try testing.expect(item == .query); try testing.expect(item == .query);
try testing.expectEqual(kitty.color.Kind.foreground, item.query); try testing.expectEqual(Kind{ .special = .foreground }, item.query);
} }
{ {
const item = cmd.kitty_color_protocol.list.items[1]; const item = cmd.kitty_color_protocol.list.items[1];
try testing.expect(item == .set); try testing.expect(item == .set);
try testing.expectEqual(kitty.color.Kind.background, item.set.key); try testing.expectEqual(Kind{ .special = .background }, item.set.key);
try testing.expectEqual(@as(u8, 0xf0), item.set.color.r); try testing.expectEqual(@as(u8, 0xf0), item.set.color.r);
try testing.expectEqual(@as(u8, 0xf8), item.set.color.g); try testing.expectEqual(@as(u8, 0xf8), item.set.color.g);
try testing.expectEqual(@as(u8, 0xff), item.set.color.b); try testing.expectEqual(@as(u8, 0xff), item.set.color.b);
@ -1659,7 +1651,7 @@ test "OSC: kitty color protocol" {
{ {
const item = cmd.kitty_color_protocol.list.items[2]; const item = cmd.kitty_color_protocol.list.items[2];
try testing.expect(item == .set); try testing.expect(item == .set);
try testing.expectEqual(kitty.color.Kind.cursor, item.set.key); try testing.expectEqual(Kind{ .special = .cursor }, item.set.key);
try testing.expectEqual(@as(u8, 0xf0), item.set.color.r); try testing.expectEqual(@as(u8, 0xf0), item.set.color.r);
try testing.expectEqual(@as(u8, 0xf8), item.set.color.g); try testing.expectEqual(@as(u8, 0xf8), item.set.color.g);
try testing.expectEqual(@as(u8, 0xff), item.set.color.b); try testing.expectEqual(@as(u8, 0xff), item.set.color.b);
@ -1667,22 +1659,22 @@ test "OSC: kitty color protocol" {
{ {
const item = cmd.kitty_color_protocol.list.items[3]; const item = cmd.kitty_color_protocol.list.items[3];
try testing.expect(item == .reset); try testing.expect(item == .reset);
try testing.expectEqual(kitty.color.Kind.cursor_text, item.reset); try testing.expectEqual(Kind{ .special = .cursor_text }, item.reset);
} }
{ {
const item = cmd.kitty_color_protocol.list.items[4]; const item = cmd.kitty_color_protocol.list.items[4];
try testing.expect(item == .reset); try testing.expect(item == .reset);
try testing.expectEqual(kitty.color.Kind.visual_bell, item.reset); try testing.expectEqual(Kind{ .special = .visual_bell }, item.reset);
} }
{ {
const item = cmd.kitty_color_protocol.list.items[5]; const item = cmd.kitty_color_protocol.list.items[5];
try testing.expect(item == .query); try testing.expect(item == .query);
try testing.expectEqual(kitty.color.Kind.selection_background, item.query); try testing.expectEqual(Kind{ .special = .selection_background }, item.query);
} }
{ {
const item = cmd.kitty_color_protocol.list.items[6]; const item = cmd.kitty_color_protocol.list.items[6];
try testing.expect(item == .set); try testing.expect(item == .set);
try testing.expectEqual(kitty.color.Kind.selection_background, item.set.key); try testing.expectEqual(Kind{ .special = .selection_background }, item.set.key);
try testing.expectEqual(@as(u8, 0xaa), item.set.color.r); try testing.expectEqual(@as(u8, 0xaa), item.set.color.r);
try testing.expectEqual(@as(u8, 0xbb), item.set.color.g); try testing.expectEqual(@as(u8, 0xbb), item.set.color.g);
try testing.expectEqual(@as(u8, 0xcc), item.set.color.b); try testing.expectEqual(@as(u8, 0xcc), item.set.color.b);
@ -1690,12 +1682,12 @@ test "OSC: kitty color protocol" {
{ {
const item = cmd.kitty_color_protocol.list.items[7]; const item = cmd.kitty_color_protocol.list.items[7];
try testing.expect(item == .query); try testing.expect(item == .query);
try testing.expectEqual(@as(kitty.color.Kind, @enumFromInt(2)), item.query); try testing.expectEqual(Kind{ .palette = 2 }, item.query);
} }
{ {
const item = cmd.kitty_color_protocol.list.items[8]; const item = cmd.kitty_color_protocol.list.items[8];
try testing.expect(item == .set); try testing.expect(item == .set);
try testing.expectEqual(@as(kitty.color.Kind, @enumFromInt(3)), item.set.key); try testing.expectEqual(Kind{ .palette = 3 }, item.set.key);
try testing.expectEqual(@as(u8, 0xff), item.set.color.r); try testing.expectEqual(@as(u8, 0xff), item.set.color.r);
try testing.expectEqual(@as(u8, 0xff), item.set.color.g); try testing.expectEqual(@as(u8, 0xff), item.set.color.g);
try testing.expectEqual(@as(u8, 0xff), item.set.color.b); try testing.expectEqual(@as(u8, 0xff), item.set.color.b);

View File

@ -1319,17 +1319,18 @@ pub const StreamHandler = struct {
switch (item) { switch (item) {
.query => |key| { .query => |key| {
const color: terminal.color.RGB = switch (key) { const color: terminal.color.RGB = switch (key) {
.foreground => self.foreground_color, .palette => |palette| self.terminal.color_palette.colors[palette],
.background => self.background_color, .special => |special| switch (special) {
.cursor => self.cursor_color, .foreground => self.foreground_color,
else => if (key.palette()) |idx| .background => self.background_color,
self.terminal.color_palette.colors[idx] .cursor => self.cursor_color,
else { else => {
log.warn("ignoring unsupported kitty color protocol key: {}", .{key}); log.warn("ignoring unsupported kitty color protocol key: {}", .{key});
continue; continue;
},
}, },
} orelse { } orelse {
log.warn("no color configured for: {s}", .{@tagName(key)}); log.warn("no color configured for {}", .{key});
continue; continue;
}; };
@ -1339,85 +1340,73 @@ pub const StreamHandler = struct {
); );
}, },
.set => |v| switch (v.key) { .set => |v| switch (v.key) {
.foreground => { .palette => |palette| {
self.foreground_color = v.color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.foreground_color = v.color,
});
},
.background => {
self.background_color = v.color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.background_color = v.color,
});
},
.cursor => {
self.cursor_color = v.color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.cursor_color = v.color,
});
},
else => if (v.key.palette()) |i| {
self.terminal.flags.dirty.palette = true; self.terminal.flags.dirty.palette = true;
self.terminal.color_palette.colors[i] = v.color; self.terminal.color_palette.colors[palette] = v.color;
self.terminal.color_palette.mask.unset(i); self.terminal.color_palette.mask.unset(palette);
} else { },
log.warn(
"ignoring unsupported kitty color protocol key: {}", .special => |special| {
.{v.key}, const msg: renderer.Message = switch (special) {
); .foreground => msg: {
continue; self.foreground_color = v.color;
break :msg .{ .foreground_color = v.color };
},
.background => msg: {
self.background_color = v.color;
break :msg .{ .background_color = v.color };
},
.cursor => msg: {
self.cursor_color = v.color;
break :msg .{ .cursor_color = v.color };
},
else => {
log.warn(
"ignoring unsupported kitty color protocol key: {}",
.{v.key},
);
continue;
},
};
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(msg);
}, },
}, },
.reset => |key| switch (key) { .reset => |key| switch (key) {
.foreground => { .palette => |palette| {
self.foreground_color = self.default_foreground_color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.foreground_color = self.default_foreground_color,
});
},
.background => {
self.background_color = self.default_background_color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.background_color = self.default_background_color,
});
},
.cursor => {
self.cursor_color = self.default_cursor_color;
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(.{
.cursor_color = self.default_cursor_color,
});
},
else => if (key.palette()) |i| {
self.terminal.flags.dirty.palette = true; self.terminal.flags.dirty.palette = true;
self.terminal.color_palette.colors[i] = self.terminal.default_palette[i]; self.terminal.color_palette.colors[palette] = self.terminal.default_palette[palette];
self.terminal.color_palette.mask.unset(i); self.terminal.color_palette.mask.unset(palette);
} else { },
log.warn(
"ignoring unsupported kitty color protocol key: {}", .special => |special| {
.{key}, const msg: renderer.Message = switch (special) {
); .foreground => msg: {
continue; self.foreground_color = self.default_foreground_color;
break :msg .{ .foreground_color = self.default_foreground_color };
},
.background => msg: {
self.background_color = self.default_background_color;
break :msg .{ .background_color = self.default_background_color };
},
.cursor => msg: {
self.cursor_color = self.default_cursor_color;
break :msg .{ .cursor_color = self.default_cursor_color };
},
else => {
log.warn(
"ignoring unsupported kitty color protocol key: {}",
.{key},
);
continue;
},
};
// See messageWriter which has similar logic and
// explains why we may have to do this.
self.rendererMessageWriter(msg);
}, },
}, },
} }