Files
ELYTerminal/src/term/ink.rs
T
ZacharyZhang-NY ae1d8ce746
CI / platforms (windows-latest) (push) Failing after 8s
CI / macos (push) Failing after 8m31s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s
T06: terminal view: frame, colors and styles, wide and combining cells, cursor and blink, font, padding, resize; bundled faces and icon font; shot example
2026-10-14 12:59:32 -04:00

177 lines
5.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use alacritty_terminal::{
term::color::Colors,
vte::ansi::{Color, NamedColor, Rgb},
};
use gpui::{Hsla, Rgba};
use crate::themes::TerminalTheme;
/// The colors a terminal paints with: its theme, under any a program set by OSC 4, 10, 11 or 12.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ink {
pub ansi: [Hsla; 16],
pub fg: Hsla,
pub bg: Hsla,
pub cursor: Hsla,
pub cursor_text: Hsla,
}
impl Ink {
pub fn new(theme: &TerminalTheme, set: &Colors) -> Self {
let named = |name: NamedColor| set[name].map(hsla);
let mut ansi = theme.ansi;
for (index, color) in ansi.iter_mut().enumerate() {
if let Some(spec) = set[index] {
*color = hsla(spec);
}
}
Self {
ansi,
fg: named(NamedColor::Foreground).unwrap_or(theme.foreground),
bg: named(NamedColor::Background).unwrap_or(theme.background),
cursor: named(NamedColor::Cursor).unwrap_or(theme.cursor),
cursor_text: theme.cursor_text,
}
}
pub fn resolve(&self, color: Color, set: &Colors) -> Hsla {
match color {
Color::Spec(spec) => hsla(spec),
Color::Indexed(index) => self.indexed(index.into(), set),
Color::Named(name) => self.named(name),
}
}
fn named(&self, name: NamedColor) -> Hsla {
match name {
NamedColor::Foreground | NamedColor::BrightForeground => self.fg,
NamedColor::Background => self.bg,
NamedColor::Cursor => self.cursor,
NamedColor::DimForeground => self.fg.opacity(DIM),
dim if (NamedColor::DimBlack as usize..=NamedColor::DimWhite as usize)
.contains(&(dim as usize)) =>
{
self.named(dim.to_bright()).opacity(DIM)
}
ansi => self.ansi[ansi as usize],
}
}
fn indexed(&self, index: usize, set: &Colors) -> Hsla {
if let Some(spec) = set[index] {
return hsla(spec);
}
match index {
0..16 => self.ansi[index],
16..232 => {
let step = |level: usize| if level == 0 { 0 } else { 55 + 40 * level as u8 };
let cube = index - 16;
hsla(Rgb {
r: step(cube / 36),
g: step(cube / 6 % 6),
b: step(cube % 6),
})
}
_ => {
let gray = 8 + 10 * (index - 232) as u8;
hsla(Rgb {
r: gray,
g: gray,
b: gray,
})
}
}
}
/// Color `index` as an answer to a program's query: 0–255, then 256 text, 257 ground, 258 cursor.
pub fn query(&self, index: usize, set: &Colors) -> Rgb {
let color = match index {
256 => self.fg,
257 => self.bg,
258 => self.cursor,
index => self.indexed(index.min(255), set),
};
let rgba = color.to_rgb();
let byte = |channel: f32| (channel * 255.0).round() as u8;
Rgb {
r: byte(rgba.r),
g: byte(rgba.g),
b: byte(rgba.b),
}
}
}
/// How much of the text color dim text keeps.
pub const DIM: f32 = 0.66;
fn hsla(spec: Rgb) -> Hsla {
let channel = |value: u8| f32::from(value) / 255.0;
Rgba {
r: channel(spec.r),
g: channel(spec.g),
b: channel(spec.b),
a: 1.0,
}
.into()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::themes;
fn ink(set: &Colors) -> Ink {
Ink::new(themes::find("catppuccin-mocha").expect("mocha"), set)
}
#[test]
fn indexed_colors_follow_the_xterm_cube_and_ramp() {
let set = Colors::default();
let ink = ink(&set);
assert_eq!(ink.query(196, &set), Rgb { r: 255, g: 0, b: 0 });
assert_eq!(ink.query(16, &set), Rgb { r: 0, g: 0, b: 0 });
assert_eq!(
ink.query(244, &set),
Rgb {
r: 128,
g: 128,
b: 128
}
);
assert_eq!(
ink.query(1, &set),
Rgb {
r: 0xf3,
g: 0x8b,
b: 0xa8
},
"the theme's red"
);
assert_eq!(
ink.query(257, &set),
Rgb {
r: 0x1e,
g: 0x1e,
b: 0x2e
}
);
}
#[test]
fn a_color_the_program_set_wins() {
let mut set = Colors::default();
set[1] = Some(Rgb { r: 1, g: 2, b: 3 });
set[NamedColor::Background] = Some(Rgb { r: 9, g: 9, b: 9 });
let ink = ink(&set);
assert_eq!(
ink.resolve(Color::Named(NamedColor::Red), &set),
hsla(Rgb { r: 1, g: 2, b: 3 })
);
assert_eq!(ink.bg, hsla(Rgb { r: 9, g: 9, b: 9 }));
assert_eq!(
ink.resolve(Color::Named(NamedColor::DimRed), &set),
ink.ansi[1].opacity(DIM)
);
}
}