Acord/viewport/src/syntax.rs

140 lines
5.1 KiB
Rust

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<HighlightSpan>,
line_offsets: Vec<usize>,
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<usize>, 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
}
}