Something went wrong. Try again.
small gleam coding and (not yet) persistent agent daemon with a detachable cli
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100//! Syntax colors for a whole file, looked up by byte offset.
use arborium::Highlighter;use arborium_highlight::{FlatToken, spans_to_flat_tokens};use arborium_theme::highlights;use arborium_theme::theme::{Color, Theme};
/// Which grammar colored the file, or why none did.pub enum Language { Highlighted(String), /// Named after the file, but not one of the grammars compiled in. Unsupported(String), /// The grammar exists but could not highlight this file. Failed(String), Unknown,}
pub struct Colors { tokens: Vec<(FlatToken, Color)>, pub foreground: Color, pub background: Color, pub language: Language,}
impl Colors { /// Highlights all of `source`, so a range starting inside a comment or a /// string is colored as it would be in the whole file. pub fn new(source: &str, language: Option<&str>, theme: &Theme) -> Colors { let foreground = theme.foreground.unwrap_or(Color::new(0, 0, 0)); let background = theme.background.unwrap_or(Color::new(255, 255, 255)); let plain = |language| Colors { tokens: Vec::new(), foreground, background, language, }; let Some(name) = language else { return plain(Language::Unknown); }; let spans = match Highlighter::new().highlight_spans(name, source) { Ok(spans) => spans, Err(arborium::Error::UnsupportedLanguage { .. }) => { return plain(Language::Unsupported(name.to_string())); } Err(error) => return plain(Language::Failed(error.to_string())), }; let tokens = spans_to_flat_tokens(source, spans) .into_iter() .filter_map(|token| tag_color(theme, token.tag).map(|color| (token, color))) .collect(); Colors { tokens, foreground, background, language: Language::Highlighted(name.to_string()), } }
/// The token covering each byte from `from` on, for a caller walking forward. pub fn cursor(&self, from: usize) -> Cursor<'_> { let index = self .tokens .partition_point(|(token, _)| (token.end as usize) <= from); Cursor { colors: self, index, } }}
pub struct Cursor<'a> { colors: &'a Colors, index: usize,}
impl Cursor<'_> { /// The color at byte `offset`; offsets must not decrease between calls. pub fn at(&mut self, offset: usize) -> Color { let tokens = &self.colors.tokens; while self.index < tokens.len() && (tokens[self.index].0.end as usize) <= offset { self.index += 1; } match tokens.get(self.index) { Some((token, color)) if token.start as usize <= offset => *color, _ => self.colors.foreground, } }}
/// A child tag with no color of its own takes its parent's, as the theme's CSS would.fn tag_color(theme: &Theme, tag: &str) -> Option<Color> { let index = (0..highlights::COUNT).find(|&index| highlights::tag(index) == Some(tag))?; theme.style(index).and_then(|style| style.fg).or_else(|| { let parent = highlights::HIGHLIGHTS[index].parent_tag; (!parent.is_empty()) .then(|| tag_color(theme, parent)) .flatten() })}