csskit_highlight/
ansi_highlight_cursor_stream.rs1use crate::{AnsiTheme, SemanticDecoration, TokenHighlighter};
2use chromashift::{Named, WcagColorContrast};
3use css_lexer::{ToSpan, Token};
4use css_parse::{SourceCursor, SourceCursorSink};
5use std::fmt::{Result, Write};
6
7#[cfg(feature = "anstyle")]
8use anstyle::Style;
9#[cfg(feature = "owo-colors")]
10#[cfg(not(feature = "anstyle"))]
11use owo_colors::OwoColorize;
12
13pub struct AnsiHighlightCursorStream<'h, W: Write, T: AnsiTheme> {
14 writer: W,
15 theme: T,
16 highlighter: &'h TokenHighlighter,
17 last_token: Option<Token>,
18 err: Result,
19}
20
21impl<'h, W: Write, T: AnsiTheme> AnsiHighlightCursorStream<'h, W, T> {
22 pub fn new(writer: W, highlighter: &'h TokenHighlighter, theme: T) -> Self {
23 Self { writer, highlighter, theme, last_token: None, err: Ok(()) }
24 }
25}
26
27impl<'a, 'h, W: Write, T: AnsiTheme> SourceCursorSink<'a> for AnsiHighlightCursorStream<'h, W, T> {
28 fn append(&mut self, c: SourceCursor<'a>) {
29 if self.err.is_err() {
30 return;
31 }
32 if let Some(last) = self.last_token
33 && last.needs_separator_for(c.token())
34 {
35 self.err = self.writer.write_char(' ');
36 }
37 self.last_token = Some(c.token());
38 if let Some(highlight) = self.highlighter.get(c.to_span()) {
39 if let SemanticDecoration::BackgroundColor(bg) = highlight.decoration() {
40 let fg = if bg.wcag_contrast_ratio(Named::White) > bg.wcag_contrast_ratio(Named::Black) {
41 Named::White
42 } else {
43 Named::Black
44 };
45 #[cfg(feature = "anstyle")]
46 {
47 let style = Style::new().bg_color(Some(bg.into())).fg_color(Some(fg.into()));
48 self.err = write!(&mut self.writer, "{style}{c}{style:#}");
49 }
50 #[cfg(feature = "owo-colors")]
51 #[cfg(not(feature = "anstyle"))]
52 {
53 use chromashift::Srgb;
54 let bg_srgb: Srgb = bg.into();
55 let fg_srgb: Srgb = fg.into();
56 self.err = write!(
57 &mut self.writer,
58 "{}",
59 c.to_string().truecolor(fg_srgb.red, fg_srgb.green, fg_srgb.blue).on_truecolor(
60 bg_srgb.red,
61 bg_srgb.green,
62 bg_srgb.blue
63 )
64 );
65 }
66 } else {
67 #[cfg(feature = "anstyle")]
68 {
69 let style = self.theme.get_anstyle(highlight.kind(), highlight.modifier());
70 self.err = write!(&mut self.writer, "{style}{c}{style:#}");
71 }
72 #[cfg(feature = "owo-colors")]
73 #[cfg(not(feature = "anstyle"))]
74 {
75 let style = self.theme.get_owo_style(highlight.kind(), highlight.modifier());
76 self.err = write!(&mut self.writer, "{}", style.style(c.to_string()));
77 }
78 }
79 } else {
80 self.err = write!(&mut self.writer, "{c}");
81 }
82 }
83}