use std::ops::Range; use iced_wgpu::core::text::highlighter; use iced_wgpu::core::Color; use acord_core::highlight::{highlight_source, HighlightSpan}; use crate::editor::{RESULT_PREFIX, ERROR_PREFIX}; pub const EVAL_RESULT_KIND: u8 = 24; pub const EVAL_ERROR_KIND: u8 = 25; #[derive(Clone, PartialEq)] pub struct SyntaxSettings { pub lang: String, pub source: String, } #[derive(Clone, Copy, Debug)] pub struct SyntaxHighlight { pub kind: u8, } pub struct SyntaxHighlighter { lang: String, spans: Vec, line_offsets: Vec, current_line: usize, } impl SyntaxHighlighter { fn rebuild(&mut self, source: &str) { self.spans = highlight_source(source, &self.lang); self.line_offsets.clear(); let mut offset = 0; for line in source.split('\n') { self.line_offsets.push(offset); offset += line.len() + 1; } self.current_line = 0; } } impl highlighter::Highlighter for SyntaxHighlighter { type Settings = SyntaxSettings; type Highlight = SyntaxHighlight; type Iterator<'a> = std::vec::IntoIter<(Range, SyntaxHighlight)>; fn new(settings: &Self::Settings) -> Self { let mut h = SyntaxHighlighter { lang: settings.lang.clone(), spans: Vec::new(), line_offsets: Vec::new(), current_line: 0, }; h.rebuild(&settings.source); h } fn update(&mut self, new_settings: &Self::Settings) { self.lang = new_settings.lang.clone(); self.rebuild(&new_settings.source); } fn change_line(&mut self, line: usize) { self.current_line = self.current_line.min(line); } fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> { let ln = self.current_line; self.current_line += 1; let trimmed = _line.trim_start(); if trimmed.starts_with(RESULT_PREFIX) { return vec![(0.._line.len(), SyntaxHighlight { kind: EVAL_RESULT_KIND })].into_iter(); } if trimmed.starts_with(ERROR_PREFIX) { return vec![(0.._line.len(), SyntaxHighlight { kind: EVAL_ERROR_KIND })].into_iter(); } if ln >= self.line_offsets.len() { return Vec::new().into_iter(); } let line_start = self.line_offsets[ln]; let line_end = if ln + 1 < self.line_offsets.len() { self.line_offsets[ln + 1] - 1 } else { line_start + _line.len() }; let mut result = Vec::new(); for span in &self.spans { if span.end <= line_start || span.start >= line_end { continue; } let start = span.start.max(line_start) - line_start; let end = span.end.min(line_end) - line_start; if start < end { result.push((start..end, SyntaxHighlight { kind: span.kind })); } } result.into_iter() } fn current_line(&self) -> usize { self.current_line } } pub fn highlight_color(kind: u8) -> Color { match kind { 0 => Color::from_rgb(0.804, 0.569, 0.945), // keyword - mauve 1 => Color::from_rgb(0.537, 0.706, 0.980), // function - blue 2 => Color::from_rgb(0.604, 0.831, 0.898), // function.builtin - teal 3 => Color::from_rgb(0.976, 0.827, 0.522), // type - yellow 4 => Color::from_rgb(0.976, 0.827, 0.522), // type.builtin - yellow 5 => Color::from_rgb(0.569, 0.878, 0.800), // constructor - teal 6 => Color::from_rgb(0.988, 0.702, 0.529), // constant - peach 7 => Color::from_rgb(0.988, 0.702, 0.529), // constant.builtin - peach 8 => Color::from_rgb(0.651, 0.890, 0.631), // string - green 9 => Color::from_rgb(0.988, 0.702, 0.529), // number - peach 10 => Color::from_rgb(0.424, 0.443, 0.537), // comment - overlay0 11 => Color::from_rgb(0.804, 0.839, 0.957), // variable - text 12 => Color::from_rgb(0.949, 0.604, 0.584), // variable.builtin - red 13 => Color::from_rgb(0.949, 0.773, 0.584), // variable.parameter - flamingo 14 => Color::from_rgb(0.604, 0.831, 0.898), // operator - sky 15 => Color::from_rgb(0.580, 0.612, 0.733), // punctuation - overlay2 16 => Color::from_rgb(0.580, 0.612, 0.733), // punctuation.bracket - overlay2 17 => Color::from_rgb(0.580, 0.612, 0.733), // punctuation.delimiter - overlay2 18 => Color::from_rgb(0.537, 0.706, 0.980), // property - blue 19 => Color::from_rgb(0.804, 0.569, 0.945), // tag - mauve 20 => Color::from_rgb(0.976, 0.827, 0.522), // attribute - yellow 21 => Color::from_rgb(0.569, 0.878, 0.800), // label - teal 22 => Color::from_rgb(0.949, 0.604, 0.584), // escape - red 23 => Color::from_rgb(0.804, 0.839, 0.957), // embedded - text 24 => Color::from_rgb(0.651, 0.890, 0.631), // eval result - green 25 => Color::from_rgb(0.890, 0.400, 0.400), // eval error - muted red _ => Color::from_rgb(0.804, 0.839, 0.957), // default text } }