Files
ARCADIA-Studio/crates/oa-desktop/src/highlight.rs
T

77 lines
2.7 KiB
Rust

//! Iced adapter for the core language lexer. Tokens are computed once per draft,
//! then split into line-local UTF-8 ranges (including multiline comments).
use iced::{
Color, Font, Theme,
advanced::text::{Highlighter, highlighter::Format},
};
use oa_core::language::{TokenClass, tokenize};
use std::{ops::Range, sync::Arc};
pub struct ArcadiaHighlighter {
lines: Vec<Vec<(Range<usize>, TokenClass)>>,
current: usize,
}
impl Highlighter for ArcadiaHighlighter {
type Settings = Arc<str>;
type Highlight = TokenClass;
type Iterator<'a> = std::vec::IntoIter<(Range<usize>, TokenClass)>;
fn new(source: &Self::Settings) -> Self {
let mut offsets = vec![0];
offsets.extend(source.match_indices('\n').map(|(offset, _)| offset + 1));
let mut lines = vec![Vec::new(); offsets.len()];
for token in tokenize(source) {
if matches!(token.class, TokenClass::Whitespace) {
continue;
}
let first = offsets
.partition_point(|offset| *offset <= token.span.start)
.saturating_sub(1);
for line in first..offsets.len() {
let start = offsets[line];
if start >= token.span.end {
break;
}
let end = offsets.get(line + 1).copied().unwrap_or(source.len());
let local = token.span.start.max(start) - start..token.span.end.min(end) - start;
if !local.is_empty() {
lines[line].push((local, token.class));
}
}
}
Self { lines, current: 0 }
}
fn update(&mut self, source: &Self::Settings) {
*self = Self::new(source);
}
fn change_line(&mut self, line: usize) {
self.current = line;
}
fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> {
let spans = self.lines.get(self.current).cloned().unwrap_or_default();
self.current += 1;
spans.into_iter()
}
fn current_line(&self) -> usize {
self.current
}
}
pub fn format(class: &TokenClass, _theme: &Theme) -> Format<Font> {
let color = match class {
TokenClass::Keyword => Color::from_rgb8(118, 62, 160),
TokenClass::Identifier => Color::from_rgb8(20, 93, 139),
TokenClass::String => Color::from_rgb8(24, 113, 79),
TokenClass::Number => Color::from_rgb8(163, 78, 30),
TokenClass::Comment => Color::from_rgb8(111, 121, 132),
TokenClass::Error => Color::from_rgb8(186, 38, 62),
TokenClass::Punctuation | TokenClass::Whitespace => Color::from_rgb8(91, 101, 117),
};
Format {
color: Some(color),
font: None,
}
}