use crate::{
color_choice::ColorLevel,
role::StyleRole,
style::{AnsiColor, Style},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Theme {
styles: [Style; StyleRole::COUNT],
}
impl Default for Theme {
fn default() -> Self {
Self::dark()
}
}
impl Theme {
pub const NAMES: &'static [&'static str] = &[
"dark",
"light",
"monochrome",
"ansi16",
"gruvbox",
"nord",
"dracula",
"solarized-dark",
"solarized-light",
"one-dark",
"catppuccin",
"tokyo-night",
"monokai",
"ayu-dark",
];
pub fn by_name(name: &str) -> Option<Self> {
let normalized = name.trim().to_ascii_lowercase().replace('_', "-");
match normalized.as_str() {
"default" | "dark" => Some(Self::dark()),
"light" => Some(Self::light()),
"monochrome" | "mono" | "plain" => Some(Self::monochrome()),
"ansi16" | "ansi" => Some(Self::ansi16()),
"gruvbox" => Some(Self::gruvbox()),
"nord" => Some(Self::nord()),
"dracula" => Some(Self::dracula()),
"solarized-dark" => Some(Self::solarized_dark()),
"solarized-light" => Some(Self::solarized_light()),
"one-dark" | "onedark" => Some(Self::one_dark()),
"catppuccin" | "catppuccin-mocha" => Some(Self::catppuccin_mocha()),
"tokyo-night" | "tokyonight" => Some(Self::tokyo_night()),
"monokai" => Some(Self::monokai()),
"ayu" | "ayu-dark" => Some(Self::ayu_dark()),
_ => None,
}
}
pub const fn empty() -> Self {
Self { styles: [Style::new(); StyleRole::COUNT] }
}
pub fn dark() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(224, 138, 90))
.with(StyleRole::Field, Style::new().fg_rgb(110, 180, 235))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(180, 145, 225))
.with(StyleRole::Key, Style::new().fg_rgb(105, 205, 195))
.with(StyleRole::String, Style::new().fg_rgb(142, 198, 128))
.with(StyleRole::Number, Style::new().fg_rgb(235, 190, 95))
.with(StyleRole::Boolean, Style::new().fg_rgb(195, 135, 185))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(140, 145, 155))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(115, 120, 130))
.with(StyleRole::Index, Style::new().fg_rgb(115, 185, 195))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(130, 135, 145))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(235, 95, 95))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(225, 180, 75))
.with(StyleRole::Error, Style::new().bold().fg_rgb(240, 85, 85))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(120, 125, 135))
}
pub fn light() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(175, 75, 25))
.with(StyleRole::Field, Style::new().fg_rgb(30, 95, 160))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(115, 45, 150))
.with(StyleRole::Key, Style::new().fg_rgb(20, 115, 120))
.with(StyleRole::String, Style::new().fg_rgb(35, 125, 55))
.with(StyleRole::Number, Style::new().fg_rgb(165, 105, 15))
.with(StyleRole::Boolean, Style::new().fg_rgb(140, 35, 105))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(110, 115, 125))
.with(StyleRole::Punctuation, Style::new().fg_rgb(120, 125, 135))
.with(StyleRole::Index, Style::new().fg_rgb(40, 105, 135))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(105, 110, 120))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(185, 30, 30))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(155, 95, 20))
.with(StyleRole::Error, Style::new().bold().fg_rgb(195, 20, 20))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(110, 115, 125))
}
pub fn monochrome() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold())
.with(StyleRole::Field, Style::new())
.with(StyleRole::Variant, Style::new().bold().underline())
.with(StyleRole::Key, Style::new().underline())
.with(StyleRole::String, Style::new())
.with(StyleRole::Number, Style::new().bold())
.with(StyleRole::Boolean, Style::new().bold())
.with(StyleRole::Null, Style::new().dimmed().italic())
.with(StyleRole::Punctuation, Style::new().dimmed())
.with(StyleRole::Index, Style::new().dimmed())
.with(StyleRole::Metadata, Style::new().dimmed())
.with(StyleRole::Sensitive, Style::new().bold().underline())
.with(StyleRole::Truncated, Style::new().dimmed().italic())
.with(StyleRole::Error, Style::new().bold().underline())
.with(StyleRole::Muted, Style::new().dimmed())
}
pub fn ansi16() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_ansi(AnsiColor::Yellow))
.with(StyleRole::Field, Style::new().fg_ansi(AnsiColor::Cyan))
.with(StyleRole::Variant, Style::new().bold().fg_ansi(AnsiColor::Magenta))
.with(StyleRole::Key, Style::new().fg_ansi(AnsiColor::Cyan))
.with(StyleRole::String, Style::new().fg_ansi(AnsiColor::Green))
.with(StyleRole::Number, Style::new().fg_ansi(AnsiColor::Yellow))
.with(StyleRole::Boolean, Style::new().fg_ansi(AnsiColor::Magenta))
.with(StyleRole::Null, Style::new().fg_ansi(AnsiColor::BrightBlack))
.with(StyleRole::Punctuation, Style::new().fg_ansi(AnsiColor::BrightBlack))
.with(StyleRole::Index, Style::new().fg_ansi(AnsiColor::Cyan))
.with(StyleRole::Metadata, Style::new().fg_ansi(AnsiColor::BrightBlack))
.with(StyleRole::Sensitive, Style::new().bold().fg_ansi(AnsiColor::BrightRed))
.with(StyleRole::Truncated, Style::new().fg_ansi(AnsiColor::Yellow))
.with(StyleRole::Error, Style::new().bold().fg_ansi(AnsiColor::Red))
.with(StyleRole::Muted, Style::new().fg_ansi(AnsiColor::BrightBlack))
}
pub fn gruvbox() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(254, 128, 25))
.with(StyleRole::Field, Style::new().fg_rgb(131, 165, 152))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(211, 134, 155))
.with(StyleRole::Key, Style::new().fg_rgb(142, 192, 124))
.with(StyleRole::String, Style::new().fg_rgb(184, 187, 38))
.with(StyleRole::Number, Style::new().fg_rgb(250, 189, 47))
.with(StyleRole::Boolean, Style::new().fg_rgb(211, 134, 155))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(146, 131, 116))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(146, 131, 116))
.with(StyleRole::Index, Style::new().fg_rgb(142, 192, 124))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(146, 131, 116))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(251, 73, 52))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(250, 189, 47))
.with(StyleRole::Error, Style::new().bold().fg_rgb(251, 73, 52))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(102, 92, 84))
}
pub fn nord() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(208, 135, 112))
.with(StyleRole::Field, Style::new().fg_rgb(136, 192, 208))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(180, 142, 173))
.with(StyleRole::Key, Style::new().fg_rgb(129, 161, 193))
.with(StyleRole::String, Style::new().fg_rgb(163, 190, 140))
.with(StyleRole::Number, Style::new().fg_rgb(235, 203, 139))
.with(StyleRole::Boolean, Style::new().fg_rgb(180, 142, 173))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(147, 156, 172))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(97, 110, 136))
.with(StyleRole::Index, Style::new().fg_rgb(136, 192, 208))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(108, 120, 144))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(191, 97, 106))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(235, 203, 139))
.with(StyleRole::Error, Style::new().bold().fg_rgb(191, 97, 106))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(76, 86, 106))
}
pub fn dracula() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(255, 184, 108))
.with(StyleRole::Field, Style::new().fg_rgb(139, 233, 253))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(189, 147, 249))
.with(StyleRole::Key, Style::new().fg_rgb(139, 233, 253))
.with(StyleRole::String, Style::new().fg_rgb(80, 250, 123))
.with(StyleRole::Number, Style::new().fg_rgb(241, 250, 140))
.with(StyleRole::Boolean, Style::new().fg_rgb(255, 121, 198))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(98, 114, 164))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(98, 114, 164))
.with(StyleRole::Index, Style::new().fg_rgb(139, 233, 253))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(98, 114, 164))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(255, 85, 85))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(241, 250, 140))
.with(StyleRole::Error, Style::new().bold().fg_rgb(255, 85, 85))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(98, 114, 164))
}
pub fn solarized_dark() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(203, 75, 22))
.with(StyleRole::Field, Style::new().fg_rgb(38, 139, 210))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(108, 113, 196))
.with(StyleRole::Key, Style::new().fg_rgb(42, 161, 152))
.with(StyleRole::String, Style::new().fg_rgb(133, 153, 0))
.with(StyleRole::Number, Style::new().fg_rgb(181, 137, 0))
.with(StyleRole::Boolean, Style::new().fg_rgb(211, 54, 130))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(101, 123, 131))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(88, 110, 117))
.with(StyleRole::Index, Style::new().fg_rgb(42, 161, 152))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(88, 110, 117))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(220, 50, 47))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(181, 137, 0))
.with(StyleRole::Error, Style::new().bold().fg_rgb(220, 50, 47))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(88, 110, 117))
}
pub fn solarized_light() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(203, 75, 22))
.with(StyleRole::Field, Style::new().fg_rgb(38, 139, 210))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(108, 113, 196))
.with(StyleRole::Key, Style::new().fg_rgb(42, 161, 152))
.with(StyleRole::String, Style::new().fg_rgb(133, 153, 0))
.with(StyleRole::Number, Style::new().fg_rgb(181, 137, 0))
.with(StyleRole::Boolean, Style::new().fg_rgb(211, 54, 130))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(147, 161, 161))
.with(StyleRole::Punctuation, Style::new().fg_rgb(101, 123, 131))
.with(StyleRole::Index, Style::new().fg_rgb(42, 161, 152))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(131, 148, 150))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(220, 50, 47))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(181, 137, 0))
.with(StyleRole::Error, Style::new().bold().fg_rgb(220, 50, 47))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(147, 161, 161))
}
pub fn one_dark() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(229, 192, 123))
.with(StyleRole::Field, Style::new().fg_rgb(97, 175, 239))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(198, 120, 221))
.with(StyleRole::Key, Style::new().fg_rgb(86, 182, 194))
.with(StyleRole::String, Style::new().fg_rgb(152, 195, 121))
.with(StyleRole::Number, Style::new().fg_rgb(209, 154, 102))
.with(StyleRole::Boolean, Style::new().fg_rgb(198, 120, 221))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(120, 128, 142))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(92, 99, 112))
.with(StyleRole::Index, Style::new().fg_rgb(86, 182, 194))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(92, 99, 112))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(224, 108, 117))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(229, 192, 123))
.with(StyleRole::Error, Style::new().bold().fg_rgb(224, 108, 117))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(92, 99, 112))
}
pub fn catppuccin_mocha() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(250, 179, 135))
.with(StyleRole::Field, Style::new().fg_rgb(137, 180, 250))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(203, 166, 247))
.with(StyleRole::Key, Style::new().fg_rgb(148, 226, 213))
.with(StyleRole::String, Style::new().fg_rgb(166, 227, 161))
.with(StyleRole::Number, Style::new().fg_rgb(249, 226, 175))
.with(StyleRole::Boolean, Style::new().fg_rgb(245, 194, 231))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(147, 153, 178))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(108, 112, 134))
.with(StyleRole::Index, Style::new().fg_rgb(137, 220, 235))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(166, 173, 200))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(243, 139, 168))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(249, 226, 175))
.with(StyleRole::Error, Style::new().bold().fg_rgb(243, 139, 168))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(88, 91, 112))
}
pub fn catppuccin() -> Self {
Self::catppuccin_mocha()
}
pub fn tokyo_night() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(255, 158, 100))
.with(StyleRole::Field, Style::new().fg_rgb(122, 162, 247))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(187, 154, 247))
.with(StyleRole::Key, Style::new().fg_rgb(125, 207, 255))
.with(StyleRole::String, Style::new().fg_rgb(158, 206, 106))
.with(StyleRole::Number, Style::new().fg_rgb(224, 175, 104))
.with(StyleRole::Boolean, Style::new().fg_rgb(187, 154, 247))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(120, 124, 153))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(86, 95, 137))
.with(StyleRole::Index, Style::new().fg_rgb(125, 207, 255))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(86, 95, 137))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(247, 118, 142))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(224, 175, 104))
.with(StyleRole::Error, Style::new().bold().fg_rgb(247, 118, 142))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(86, 95, 137))
}
pub fn monokai() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(253, 151, 31))
.with(StyleRole::Field, Style::new().fg_rgb(102, 217, 239))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(174, 129, 255))
.with(StyleRole::Key, Style::new().fg_rgb(102, 217, 239))
.with(StyleRole::String, Style::new().fg_rgb(166, 226, 46))
.with(StyleRole::Number, Style::new().fg_rgb(230, 219, 116))
.with(StyleRole::Boolean, Style::new().fg_rgb(249, 38, 114))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(140, 138, 125))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(117, 113, 94))
.with(StyleRole::Index, Style::new().fg_rgb(102, 217, 239))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(117, 113, 94))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(249, 38, 114))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(230, 219, 116))
.with(StyleRole::Error, Style::new().bold().fg_rgb(249, 38, 114))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(117, 113, 94))
}
pub fn ayu_dark() -> Self {
Self::empty()
.with(StyleRole::Type, Style::new().bold().fg_rgb(255, 153, 64))
.with(StyleRole::Field, Style::new().fg_rgb(54, 163, 217))
.with(StyleRole::Variant, Style::new().bold().fg_rgb(212, 191, 255))
.with(StyleRole::Key, Style::new().fg_rgb(149, 230, 203))
.with(StyleRole::String, Style::new().fg_rgb(184, 204, 82))
.with(StyleRole::Number, Style::new().fg_rgb(231, 197, 71))
.with(StyleRole::Boolean, Style::new().fg_rgb(212, 191, 255))
.with(StyleRole::Null, Style::new().dimmed().fg_rgb(120, 126, 135))
.with(StyleRole::Punctuation, Style::new().dimmed().fg_rgb(92, 103, 115))
.with(StyleRole::Index, Style::new().fg_rgb(95, 180, 210))
.with(StyleRole::Metadata, Style::new().dimmed().fg_rgb(92, 103, 115))
.with(StyleRole::Sensitive, Style::new().bold().fg_rgb(240, 113, 120))
.with(StyleRole::Truncated, Style::new().italic().fg_rgb(231, 197, 71))
.with(StyleRole::Error, Style::new().bold().fg_rgb(240, 113, 120))
.with(StyleRole::Muted, Style::new().dimmed().fg_rgb(92, 103, 115))
}
pub const fn with(mut self, role: StyleRole, style: Style) -> Self {
self.styles[role.index()] = style;
self
}
#[inline]
pub const fn style(&self, role: StyleRole) -> Style {
self.styles[role.index()]
}
pub fn adapt_to(self, level: ColorLevel) -> Self {
match level {
ColorLevel::Auto => {
let detected = ColorLevel::detect();
if detected == ColorLevel::Auto { self } else { self.adapt_to(detected) }
}
ColorLevel::Truecolor => self,
ColorLevel::Ansi256 => self.to_ansi256(),
ColorLevel::Ansi16 => self.to_ansi16(),
ColorLevel::None => self.to_monochrome(),
}
}
pub fn to_ansi256(self) -> Self {
let mut new_theme = self;
let mut i = 0;
while i < StyleRole::COUNT {
new_theme.styles[i] = new_theme.styles[i].to_ansi256();
i += 1;
}
new_theme
}
pub fn to_ansi16(self) -> Self {
let mut new_theme = self;
let mut i = 0;
while i < StyleRole::COUNT {
new_theme.styles[i] = new_theme.styles[i].to_ansi16();
i += 1;
}
new_theme
}
pub fn to_monochrome(self) -> Self {
let mut new_theme = self;
let mut i = 0;
while i < StyleRole::COUNT {
new_theme.styles[i] = new_theme.styles[i].to_monochrome();
i += 1;
}
new_theme
}
pub fn is_plain(&self) -> bool {
self.styles.iter().all(|s| s.is_plain())
}
}