use alacritty_terminal::term::cell::{Cell, Flags};
use alacritty_terminal::term::color::Colors;
use alacritty_terminal::vte::ansi::{Color as AnsiColor, NamedColor, Rgb};
use ratatui::style::{Color, Modifier, Style};
pub fn cell_style(cell: &Cell, colors: &Colors) -> Style {
let mut style = Style::default()
.fg(to_ratatui(cell.fg, colors))
.bg(to_ratatui(cell.bg, colors));
let flags = cell.flags;
if flags.contains(Flags::BOLD) {
style = style.add_modifier(Modifier::BOLD);
}
if flags.contains(Flags::DIM) {
style = style.add_modifier(Modifier::DIM);
}
if flags.contains(Flags::ITALIC) {
style = style.add_modifier(Modifier::ITALIC);
}
if flags.intersects(Flags::ALL_UNDERLINES) {
style = style.add_modifier(Modifier::UNDERLINED);
}
if flags.contains(Flags::STRIKEOUT) {
style = style.add_modifier(Modifier::CROSSED_OUT);
}
if flags.contains(Flags::HIDDEN) {
style = style.add_modifier(Modifier::HIDDEN);
}
if flags.contains(Flags::INVERSE) {
style = style.add_modifier(Modifier::REVERSED);
}
style
}
pub fn to_ratatui(color: AnsiColor, colors: &Colors) -> Color {
match color {
AnsiColor::Spec(rgb) => rgb_color(rgb),
AnsiColor::Indexed(index) => match colors[usize::from(index)] {
Some(rgb) => rgb_color(rgb),
None => Color::Indexed(index),
},
AnsiColor::Named(named) => match colors[named] {
Some(rgb) => rgb_color(rgb),
None => named_color(named),
},
}
}
fn rgb_color(rgb: Rgb) -> Color {
Color::Rgb(rgb.r, rgb.g, rgb.b)
}
fn named_color(named: NamedColor) -> Color {
match named {
NamedColor::Black | NamedColor::DimBlack => Color::Black,
NamedColor::Red | NamedColor::DimRed => Color::Red,
NamedColor::Green | NamedColor::DimGreen => Color::Green,
NamedColor::Yellow | NamedColor::DimYellow => Color::Yellow,
NamedColor::Blue | NamedColor::DimBlue => Color::Blue,
NamedColor::Magenta | NamedColor::DimMagenta => Color::Magenta,
NamedColor::Cyan | NamedColor::DimCyan => Color::Cyan,
NamedColor::White | NamedColor::DimWhite => Color::Gray,
NamedColor::BrightBlack => Color::DarkGray,
NamedColor::BrightRed => Color::LightRed,
NamedColor::BrightGreen => Color::LightGreen,
NamedColor::BrightYellow => Color::LightYellow,
NamedColor::BrightBlue => Color::LightBlue,
NamedColor::BrightMagenta => Color::LightMagenta,
NamedColor::BrightCyan => Color::LightCyan,
NamedColor::BrightWhite => Color::White,
NamedColor::Foreground
| NamedColor::Background
| NamedColor::Cursor
| NamedColor::BrightForeground
| NamedColor::DimForeground => Color::Reset,
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn a_default_cell_maps_to_the_host_defaults_with_no_modifiers() {
let style = cell_style(&Cell::default(), &Colors::default());
assert_eq!(style.fg, Some(Color::Reset));
assert_eq!(style.bg, Some(Color::Reset));
assert!(style.add_modifier.is_empty());
}
#[test]
fn named_colors_map_to_the_host_palette_slots() {
let colors = Colors::default();
assert_eq!(
to_ratatui(AnsiColor::Named(NamedColor::Red), &colors),
Color::Red
);
assert_eq!(
to_ratatui(AnsiColor::Named(NamedColor::BrightBlue), &colors),
Color::LightBlue
);
assert_eq!(
to_ratatui(AnsiColor::Named(NamedColor::DimGreen), &colors),
Color::Green
);
}
#[test]
fn indexed_and_spec_colors_pass_through() {
let colors = Colors::default();
assert_eq!(
to_ratatui(AnsiColor::Indexed(42), &colors),
Color::Indexed(42)
);
assert_eq!(
to_ratatui(AnsiColor::Spec(Rgb { r: 1, g: 2, b: 3 }), &colors),
Color::Rgb(1, 2, 3)
);
}
#[test]
fn a_runtime_palette_override_wins_as_exact_rgb() {
let mut colors = Colors::default();
colors[NamedColor::Red] = Some(Rgb {
r: 200,
g: 10,
b: 10,
});
colors[7usize] = Some(Rgb { r: 9, g: 9, b: 9 });
assert_eq!(
to_ratatui(AnsiColor::Named(NamedColor::Red), &colors),
Color::Rgb(200, 10, 10)
);
assert_eq!(
to_ratatui(AnsiColor::Indexed(7), &colors),
Color::Rgb(9, 9, 9)
);
}
#[test]
fn every_named_color_maps_and_defaults_map_to_reset() {
let colors = Colors::default();
let cases: &[(NamedColor, Color)] = &[
(NamedColor::Black, Color::Black),
(NamedColor::Green, Color::Green),
(NamedColor::Yellow, Color::Yellow),
(NamedColor::Blue, Color::Blue),
(NamedColor::Magenta, Color::Magenta),
(NamedColor::Cyan, Color::Cyan),
(NamedColor::White, Color::Gray),
(NamedColor::BrightBlack, Color::DarkGray),
(NamedColor::BrightRed, Color::LightRed),
(NamedColor::BrightGreen, Color::LightGreen),
(NamedColor::BrightYellow, Color::LightYellow),
(NamedColor::BrightMagenta, Color::LightMagenta),
(NamedColor::BrightCyan, Color::LightCyan),
(NamedColor::BrightWhite, Color::White),
(NamedColor::DimBlack, Color::Black),
(NamedColor::DimRed, Color::Red),
(NamedColor::DimYellow, Color::Yellow),
(NamedColor::DimBlue, Color::Blue),
(NamedColor::DimMagenta, Color::Magenta),
(NamedColor::DimCyan, Color::Cyan),
(NamedColor::DimWhite, Color::Gray),
(NamedColor::Foreground, Color::Reset),
(NamedColor::Background, Color::Reset),
(NamedColor::Cursor, Color::Reset),
(NamedColor::BrightForeground, Color::Reset),
(NamedColor::DimForeground, Color::Reset),
];
for (named, expected) in cases {
assert_eq!(
to_ratatui(AnsiColor::Named(*named), &colors),
*expected,
"{named:?}"
);
}
}
#[test]
fn dim_strikeout_and_hidden_flags_become_modifiers() {
let cell = Cell {
flags: Flags::DIM | Flags::STRIKEOUT | Flags::HIDDEN,
..Default::default()
};
let style = cell_style(&cell, &Colors::default());
assert!(style.add_modifier.contains(Modifier::DIM));
assert!(style.add_modifier.contains(Modifier::CROSSED_OUT));
assert!(style.add_modifier.contains(Modifier::HIDDEN));
assert!(!style.add_modifier.contains(Modifier::BOLD));
}
#[test]
fn attribute_flags_become_modifiers() {
let cell = Cell {
flags: Flags::BOLD | Flags::ITALIC | Flags::UNDERCURL | Flags::INVERSE,
..Default::default()
};
let style = cell_style(&cell, &Colors::default());
assert!(style.add_modifier.contains(Modifier::BOLD));
assert!(style.add_modifier.contains(Modifier::ITALIC));
assert!(style.add_modifier.contains(Modifier::UNDERLINED));
assert!(style.add_modifier.contains(Modifier::REVERSED));
assert!(!style.add_modifier.contains(Modifier::DIM));
}
}