clean up some of the color usage, use exhaustive switches

This commit is contained in:
Mitchell Hashimoto
2025-07-03 09:25:55 -07:00
parent e87e5e7361
commit 465ac5b1b7
4 changed files with 107 additions and 77 deletions

View File

@ -601,8 +601,8 @@ foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
/// Since version 1.2.0, this can also be set to `cell-foreground` to match
/// the cell foreground color, or `cell-background` to match the cell
/// background color.
@"selection-foreground": ?DynamicColor = null,
@"selection-background": ?DynamicColor = null,
@"selection-foreground": ?TerminalColor = null,
@"selection-background": ?TerminalColor = null,
/// Whether to clear selected text when typing. This defaults to `true`.
/// This is typical behavior for most terminal emulators as well as
@ -659,7 +659,7 @@ palette: Palette = .{},
/// * `cell-background` - Match the cell background color.
/// (Available since version 1.2.0)
///
@"cursor-color": ?DynamicColor = null,
@"cursor-color": ?TerminalColor = null,
/// The opacity level (opposite of transparency) of the cursor. A value of 1
/// is fully opaque and a value of 0 is fully transparent. A value less than 0
@ -712,7 +712,7 @@ palette: Palette = .{},
/// Since version 1.2.0, this can also be set to `cell-foreground` to match
/// the cell foreground color, or `cell-background` to match the cell
/// background color.
@"cursor-text": ?DynamicColor = null,
@"cursor-text": ?TerminalColor = null,
/// Enables the ability to move the cursor at prompts by using `alt+click` on
/// Linux and `option+click` on macOS.
@ -4463,16 +4463,14 @@ pub const Color = struct {
}
};
/// Represents the color values that can be set to a non-static value.
///
/// Can either be a Color or one of the special values
/// "cell-foreground" or "cell-background".
pub const DynamicColor = union(enum) {
/// Represents color values that can also reference special color
/// values such as "cell-foreground" or "cell-background".
pub const TerminalColor = union(enum) {
color: Color,
@"cell-foreground",
@"cell-background",
pub fn parseCLI(input_: ?[]const u8) !DynamicColor {
pub fn parseCLI(input_: ?[]const u8) !TerminalColor {
const input = input_ orelse return error.ValueRequired;
if (std.mem.eql(u8, input, "cell-foreground")) return .@"cell-foreground";
if (std.mem.eql(u8, input, "cell-background")) return .@"cell-background";
@ -4480,7 +4478,7 @@ pub const DynamicColor = union(enum) {
}
/// Used by Formatter
pub fn formatEntry(self: DynamicColor, formatter: anytype) !void {
pub fn formatEntry(self: TerminalColor, formatter: anytype) !void {
switch (self) {
.color => try self.color.formatEntry(formatter),
@ -4494,23 +4492,23 @@ pub const DynamicColor = union(enum) {
const testing = std.testing;
try testing.expectEqual(
DynamicColor{ .color = Color{ .r = 78, .g = 42, .b = 132 } },
try DynamicColor.parseCLI("#4e2a84"),
TerminalColor{ .color = Color{ .r = 78, .g = 42, .b = 132 } },
try TerminalColor.parseCLI("#4e2a84"),
);
try testing.expectEqual(
DynamicColor{ .color = Color{ .r = 0, .g = 0, .b = 0 } },
try DynamicColor.parseCLI("black"),
TerminalColor{ .color = Color{ .r = 0, .g = 0, .b = 0 } },
try TerminalColor.parseCLI("black"),
);
try testing.expectEqual(
DynamicColor{.@"cell-foreground"},
try DynamicColor.parseCLI("cell-foreground"),
TerminalColor{.@"cell-foreground"},
try TerminalColor.parseCLI("cell-foreground"),
);
try testing.expectEqual(
DynamicColor{.@"cell-background"},
try DynamicColor.parseCLI("cell-background"),
TerminalColor{.@"cell-background"},
try TerminalColor.parseCLI("cell-background"),
);
try testing.expectError(error.InvalidValue, DynamicColor.parseCLI("a"));
try testing.expectError(error.InvalidValue, TerminalColor.parseCLI("a"));
}
test "formatConfig" {
@ -4518,7 +4516,7 @@ pub const DynamicColor = union(enum) {
var buf = std.ArrayList(u8).init(testing.allocator);
defer buf.deinit();
var sc: DynamicColor = .{.@"cell-foreground"};
var sc: TerminalColor = .{.@"cell-foreground"};
try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.items);
}
@ -8172,11 +8170,11 @@ test "compatibility: removed cursor-invert-fg-bg" {
try cfg.finalize();
try testing.expectEqual(
DynamicColor.@"cell-foreground",
TerminalColor.@"cell-foreground",
cfg.@"cursor-color",
);
try testing.expectEqual(
DynamicColor.@"cell-background",
TerminalColor.@"cell-background",
cfg.@"cursor-text",
);
}
@ -8196,11 +8194,11 @@ test "compatibility: removed selection-invert-fg-bg" {
try cfg.finalize();
try testing.expectEqual(
DynamicColor.@"cell-background",
TerminalColor.@"cell-background",
cfg.@"selection-foreground",
);
try testing.expectEqual(
DynamicColor.@"cell-foreground",
TerminalColor.@"cell-foreground",
cfg.@"selection-background",
);
}

View File

@ -133,7 +133,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
/// This is cursor color as set in the user's config, if any. If no cursor color
/// is set in the user's config, then the cursor color is determined by the
/// current foreground color.
default_cursor_color: ?configpkg.Config.DynamicColor,
default_cursor_color: ?configpkg.Config.TerminalColor,
/// The current set of cells to render. This is rebuilt on every frame
/// but we keep this around so that we don't reallocate. Each set of
@ -509,14 +509,14 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
font_features: std.ArrayListUnmanaged([:0]const u8),
font_styles: font.CodepointResolver.StyleStatus,
font_shaping_break: configpkg.FontShapingBreak,
cursor_color: ?configpkg.Config.DynamicColor,
cursor_color: ?configpkg.Config.TerminalColor,
cursor_opacity: f64,
cursor_text: ?configpkg.Config.DynamicColor,
cursor_text: ?configpkg.Config.TerminalColor,
background: terminal.color.RGB,
background_opacity: f64,
foreground: terminal.color.RGB,
selection_background: ?configpkg.Config.DynamicColor,
selection_foreground: ?configpkg.Config.DynamicColor,
selection_background: ?configpkg.Config.TerminalColor,
selection_foreground: ?configpkg.Config.TerminalColor,
bold_is_bright: bool,
min_contrast: f32,
padding_color: configpkg.WindowPaddingColor,
@ -2548,21 +2548,31 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
else
false;
// The `_style` suffixed values are the colors based on
// the cell style (SGR), before applying any additional
// configuration, inversions, selections, etc.
const bg_style = style.bg(cell, color_palette);
const fg_style = style.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color;
const fg_style = style.fg(
color_palette,
self.config.bold_is_bright,
) orelse self.foreground_color orelse self.default_foreground_color;
// The final background color for the cell.
const bg = bg: {
if (selected) {
break :bg if (self.config.selection_background) |selection_color|
// Use the selection background if set, otherwise the default fg color.
switch (selection_color) {
.color => selection_color.color.toTerminalRGB(),
// If we have an explicit selection background color
// specified int he config, use that
if (self.config.selection_background) |v| {
break :bg switch (v) {
.color => |color| color.toTerminalRGB(),
.@"cell-foreground" => if (style.flags.inverse) bg_style else fg_style,
.@"cell-background" => if (style.flags.inverse) fg_style else bg_style,
};
}
else
self.foreground_color orelse self.default_foreground_color;
// If no configuration, then our selection background
// is our foreground color.
break :bg self.foreground_color orelse self.default_foreground_color;
}
// Not selected
@ -2582,22 +2592,27 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
};
const fg = fg: {
const final_bg = bg_style orelse self.background_color orelse self.default_background_color;
// Our happy-path non-selection background color
// is our style or our configured defaults.
const final_bg = bg_style orelse
self.background_color orelse
self.default_background_color;
// Whether we need to use the bg color as our fg color:
// - Cell is selected, inverted, and set to cell-foreground
// - Cell is selected, not inverted, and set to cell-background
// - Cell is inverted and not selected
if (selected) {
// Use the selection foreground if set, otherwise the default bg color.
break :fg if (self.config.selection_foreground) |selection_color|
switch (selection_color) {
.color => selection_color.color.toTerminalRGB(),
// Use the selection foreground if set
if (self.config.selection_foreground) |v| {
break :fg switch (v) {
.color => |color| color.toTerminalRGB(),
.@"cell-foreground" => if (style.flags.inverse) final_bg else fg_style,
.@"cell-background" => if (style.flags.inverse) fg_style else final_bg,
};
}
else
self.background_color orelse self.default_background_color;
break :fg self.background_color orelse self.default_background_color;
}
break :fg if (style.flags.inverse)
@ -2787,25 +2802,36 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Prepare the cursor cell contents.
const style = cursor_style_ orelse break :cursor;
const cursor_color = self.cursor_color orelse if (self.default_cursor_color) |color| color: {
// If cursor-color is set, then compute the correct color.
// Otherwise, use the foreground color
if (color == .color) {
// Use the color set by cursor-color, if any.
break :color color.color.toTerminalRGB();
}
const cursor_color = cursor_color: {
// If an explicit cursor color was set by OSC 12, use that.
if (self.cursor_color) |v| break :cursor_color v;
// Use our configured color if specified
if (self.default_cursor_color) |v| switch (v) {
.color => |color| break :cursor_color color.toTerminalRGB(),
inline .@"cell-foreground",
.@"cell-background",
=> |_, tag| {
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
const fg_style = sty.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color;
const bg_style = sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color;
const fg_style = sty.fg(
color_palette,
self.config.bold_is_bright,
) orelse self.foreground_color orelse self.default_foreground_color;
const bg_style = sty.bg(
screen.cursor.page_cell,
color_palette,
) orelse self.background_color orelse self.default_background_color;
break :color switch (color) {
// If the cell is reversed, use the opposite cell color instead.
break :cursor_color switch (tag) {
.color => unreachable,
.@"cell-foreground" => if (sty.flags.inverse) bg_style else fg_style,
.@"cell-background" => if (sty.flags.inverse) fg_style else bg_style,
else => unreachable,
};
} else self.foreground_color orelse self.default_foreground_color;
},
};
break :cursor_color self.foreground_color orelse self.default_foreground_color;
};
self.addCursor(screen, style, cursor_color);

View File

@ -163,7 +163,7 @@ pub const DerivedConfig = struct {
image_storage_limit: usize,
cursor_style: terminalpkg.CursorStyle,
cursor_blink: ?bool,
cursor_color: ?configpkg.Config.DynamicColor,
cursor_color: ?configpkg.Config.TerminalColor,
foreground: configpkg.Config.Color,
background: configpkg.Config.Color,
osc_color_report_format: configpkg.Config.OSCColorReportFormat,
@ -263,13 +263,16 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
// Create our stream handler. This points to memory in self so it
// isn't safe to use until self.* is set.
const handler: StreamHandler = handler: {
const default_cursor_color = if (opts.config.cursor_color) |color|
switch (color) {
.color => color.color.toTerminalRGB(),
else => null,
}
else
null;
const default_cursor_color: ?terminalpkg.color.RGB = color: {
if (opts.config.cursor_color) |color| switch (color) {
.color => break :color color.color.toTerminalRGB(),
.@"cell-foreground",
.@"cell-background",
=> {},
};
break :color null;
};
break :handler .{
.alloc = alloc,

View File

@ -121,13 +121,16 @@ pub const StreamHandler = struct {
self.default_background_color = config.background.toTerminalRGB();
self.default_cursor_style = config.cursor_style;
self.default_cursor_blink = config.cursor_blink;
self.default_cursor_color = if (config.cursor_color) |color|
switch (color) {
.color => color.color.toTerminalRGB(),
else => null,
}
else
null;
self.default_cursor_color = color: {
if (config.cursor_color) |color| switch (color) {
.color => break :color color.color.toTerminalRGB(),
.@"cell-foreground",
.@"cell-background",
=> {},
};
break :color null;
};
// If our cursor is the default, then we update it immediately.
if (self.default_cursor) self.setCursorStyle(.default) catch |err| {