use ratada::style::{dim, fg, to_ratatui};
use ratatui::style::{Color, Modifier, Style};
use crate::config::HighlightColors;
use crate::domain::highlight::TokenKind;
use crate::theme::Palette;
#[derive(Debug, Clone, Copy)]
pub struct CaretColors {
pub cursor: Color,
pub selection: Color,
}
impl CaretColors {
pub fn from_palette(palette: &Palette) -> Self {
CaretColors {
cursor: to_ratatui(palette.cursor),
selection: to_ratatui(palette.selection),
}
}
}
#[derive(Debug, Clone)]
pub struct Highlight {
function: Style,
constant: Style,
operator: Style,
number: Style,
variable: Style,
unit: Style,
paren: Style,
ans: Style,
comment: Style,
plain: Style,
}
impl Highlight {
pub fn new(colors: &HighlightColors) -> Self {
Highlight {
function: fg(colors.function),
constant: fg(colors.constant),
operator: fg(colors.operator).add_modifier(Modifier::BOLD),
number: fg(colors.number),
variable: fg(colors.variable),
unit: fg(colors.unit),
paren: dim(),
ans: fg(colors.ans).add_modifier(Modifier::UNDERLINED),
comment: fg(colors.comment).add_modifier(Modifier::ITALIC),
plain: Style::default(),
}
}
pub fn style(&self, kind: TokenKind) -> Style {
match kind {
TokenKind::Function => self.function,
TokenKind::Constant => self.constant,
TokenKind::Operator => self.operator,
TokenKind::Number => self.number,
TokenKind::Variable => self.variable,
TokenKind::Unit => self.unit,
TokenKind::Paren => self.paren,
TokenKind::Ans => self.ans,
TokenKind::Comment => self.comment,
TokenKind::Plain => self.plain,
}
}
}
pub fn styles_for(kinds: &[TokenKind], highlight: &Highlight) -> Vec<Style> {
kinds.iter().map(|kind| highlight.style(*kind)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
#[test]
fn highlight_maps_the_token_colours_and_adds_the_modifiers() {
let colors = HighlightColors::default();
let highlight = Highlight::new(&colors);
assert_eq!(
highlight.style(TokenKind::Function).fg,
Some(to_ratatui(colors.function)),
);
let ans = highlight.style(TokenKind::Ans);
assert_eq!(ans.fg, Some(to_ratatui(colors.ans)));
assert!(ans.add_modifier.contains(Modifier::UNDERLINED));
let operator = highlight.style(TokenKind::Operator);
assert_eq!(operator.fg, Some(to_ratatui(colors.operator)));
assert!(operator.add_modifier.contains(Modifier::BOLD));
let comment = highlight.style(TokenKind::Comment);
assert!(comment.add_modifier.contains(Modifier::ITALIC));
assert!(
highlight
.style(TokenKind::Paren)
.add_modifier
.contains(Modifier::DIM)
);
}
#[test]
fn the_caret_reads_its_colours_from_the_palette() {
let palette = Config::default().palette();
let caret = CaretColors::from_palette(&palette);
assert_eq!(caret.cursor, to_ratatui(palette.cursor));
assert_eq!(caret.selection, to_ratatui(palette.selection));
assert_ne!(caret.cursor, caret.selection);
}
#[test]
fn styles_for_maps_one_style_per_character() {
let highlight = Highlight::new(&HighlightColors::default());
let kinds = [TokenKind::Number, TokenKind::Operator, TokenKind::Number];
let styles = styles_for(&kinds, &highlight);
assert_eq!(styles.len(), 3);
assert!(styles[1].add_modifier.contains(Modifier::BOLD));
}
}