use std::sync::atomic::{AtomicU8, Ordering};
const MODE_DARK: u8 = 0;
const MODE_LIGHT: u8 = 1;
static MODE: AtomicU8 = AtomicU8::new(MODE_DARK);
pub fn set_theme_mode(light: bool) {
MODE.store(if light { MODE_LIGHT } else { MODE_DARK }, Ordering::Relaxed);
}
#[inline]
fn is_light() -> bool {
MODE.load(Ordering::Relaxed) == MODE_LIGHT
}
#[inline]
pub(super) fn is_light_for_highlight() -> bool {
is_light()
}
#[inline]
pub fn is_light_for_render() -> bool {
is_light()
}
pub fn keyword() -> &'static str {
if is_light() { "\x1b[38;2;74;0;114m" } else { "\x1b[38;2;198;120;221m" }
}
pub fn string() -> &'static str {
if is_light() { "\x1b[38;2;0;100;0m" } else { "\x1b[38;2;152;195;121m" }
}
pub fn number() -> &'static str {
if is_light() { "\x1b[38;2;102;51;0m" } else { "\x1b[38;2;209;154;102m" }
}
pub fn comment() -> &'static str {
if is_light() { "\x1b[3;38;2;74;80;96m" } else { "\x1b[3;38;2;124;132;153m" }
}
pub fn function() -> &'static str {
if is_light() { "\x1b[38;2;0;33;113m" } else { "\x1b[38;2;97;175;239m" }
}
pub fn type_color() -> &'static str {
if is_light() { "\x1b[38;2;91;58;0m" } else { "\x1b[38;2;229;192;123m" }
}
pub fn variable() -> &'static str { "" }
pub fn punctuation() -> &'static str { "" }
pub const RESET: &str = "\x1b[23;39m";
pub fn md_heading_open() -> &'static str {
if is_light() { "\x1b[1;34m" } else { "\x1b[1;96m" }
}
pub const MD_HEADING_CLOSE: &str = "\x1b[22;39m";
pub fn md_inline_code_open() -> &'static str {
if is_light() { "\x1b[1;35m" } else { "\x1b[1;96m" }
}
pub const MD_INLINE_CODE_CLOSE: &str = "\x1b[22;39m";
pub const MD_BOLD_OPEN: &str = "\x1b[1m";
pub const MD_BOLD_CLOSE: &str = "\x1b[22m";
pub const MD_ITALIC_OPEN: &str = "\x1b[3m";
pub const MD_ITALIC_CLOSE: &str = "\x1b[23m";
pub const MD_MUTED_OPEN: &str = "\x1b[90m";
pub const MD_MUTED_CLOSE: &str = "\x1b[39m";
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static THEME_LOCK: Mutex<()> = Mutex::new(());
fn with_dark<F: FnOnce()>(f: F) {
let _g = THEME_LOCK.lock().unwrap();
set_theme_mode(false);
f();
set_theme_mode(false); }
fn with_light<F: FnOnce()>(f: F) {
let _g = THEME_LOCK.lock().unwrap();
set_theme_mode(true);
f();
set_theme_mode(false); }
#[test]
fn dark_keyword_is_legacy_soft_purple() {
with_dark(|| assert_eq!(keyword(), "\x1b[38;2;198;120;221m"));
}
#[test]
fn light_keyword_is_very_dark_violet() {
with_light(|| assert_eq!(keyword(), "\x1b[38;2;74;0;114m"));
}
#[test]
fn dark_function_is_legacy_blue() {
with_dark(|| assert_eq!(function(), "\x1b[38;2;97;175;239m"));
}
#[test]
fn light_function_is_very_dark_navy() {
with_light(|| assert_eq!(function(), "\x1b[38;2;0;33;113m"));
}
#[test]
fn variable_and_punctuation_stay_empty_in_both_palettes() {
with_dark(|| {
assert_eq!(variable(), "");
assert_eq!(punctuation(), "");
});
with_light(|| {
assert_eq!(variable(), "");
assert_eq!(punctuation(), "");
});
}
#[test]
fn comment_includes_italic_attr_in_both_palettes() {
with_dark(|| assert!(comment().starts_with("\x1b[3;38;2;"),
"dark comment must lead with SGR 3 + truecolor"));
with_light(|| assert!(comment().starts_with("\x1b[3;38;2;"),
"light comment must lead with SGR 3 + truecolor"));
}
#[test]
fn reset_closes_italic_and_fg() {
assert_eq!(RESET, "\x1b[23;39m");
}
#[test]
fn dark_md_heading_is_bold_bright_cyan() {
with_dark(|| assert_eq!(md_heading_open(), "\x1b[1;96m"));
}
#[test]
fn light_md_heading_is_bold_blue() {
with_light(|| assert_eq!(md_heading_open(), "\x1b[1;34m"));
}
#[test]
fn dark_md_inline_code_is_bold_bright_cyan() {
with_dark(|| assert_eq!(md_inline_code_open(), "\x1b[1;96m"));
}
#[test]
fn light_md_inline_code_is_bold_magenta() {
with_light(|| assert_eq!(md_inline_code_open(), "\x1b[1;35m"));
}
#[test]
fn close_codes_are_theme_invariant() {
assert_eq!(MD_HEADING_CLOSE, "\x1b[22;39m");
assert_eq!(MD_INLINE_CODE_CLOSE, "\x1b[22;39m");
assert_eq!(MD_BOLD_CLOSE, "\x1b[22m");
assert_eq!(MD_ITALIC_CLOSE, "\x1b[23m");
assert_eq!(MD_MUTED_CLOSE, "\x1b[39m");
}
}