mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Clean up Kitty color protocol by using a union
Non-exhaustive enums should be avoided, use a union to make the code cleaner and safer.
This commit is contained in:
@ -18,27 +18,33 @@ 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 {
|
||||||
|
return kind: {
|
||||||
|
const s = std.meta.stringToEnum(Special, key) orelse {
|
||||||
|
const p = std.fmt.parseUnsigned(u8, key, 10) catch {
|
||||||
|
break :kind null;
|
||||||
|
};
|
||||||
|
break :kind Kind{ .palette = p };
|
||||||
|
};
|
||||||
|
break :kind Kind{ .special = s };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(
|
pub fn format(
|
||||||
@ -52,41 +58,24 @@ pub const Kind = enum(u9) {
|
|||||||
|
|
||||||
// Format as a number if its a palette color otherwise
|
// Format as a number if its a palette color otherwise
|
||||||
// format as a string.
|
// format as a string.
|
||||||
if (self.palette()) |idx| {
|
if (self == .palette) {
|
||||||
try writer.print("{}", .{idx});
|
try writer.print("{d}", .{self.palette});
|
||||||
} else {
|
} else {
|
||||||
try writer.print("{s}", .{@tagName(self)});
|
try writer.print("{s}", .{@tagName(self.special)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,83 @@ 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(
|
.special => |special| {
|
||||||
"ignoring unsupported kitty color protocol key: {}",
|
const msg: renderer.Message = switch (special) {
|
||||||
.{v.key},
|
.foreground => msg: {
|
||||||
);
|
self.foreground_color = v.color;
|
||||||
continue;
|
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(
|
.special => |special| {
|
||||||
"ignoring unsupported kitty color protocol key: {}",
|
const msg: renderer.Message = switch (special) {
|
||||||
.{key},
|
.foreground => msg: {
|
||||||
);
|
self.foreground_color = self.default_foreground_color;
|
||||||
continue;
|
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);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user