Acord/viewport/src/syntax.rs

142 lines
3.7 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};
use crate::palette;
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 {
let p = palette::current();
match kind {
0 => p.mauve,
1 => p.blue,
2 => p.teal,
3 => p.yellow,
4 => p.yellow,
5 => p.teal,
6 => p.peach,
7 => p.peach,
8 => p.green,
9 => p.peach,
10 => p.overlay0,
11 => p.text,
12 => p.red,
13 => p.flamingo,
14 => p.sky,
15 => p.overlay2,
16 => p.overlay2,
17 => p.overlay2,
18 => p.blue,
19 => p.mauve,
20 => p.yellow,
21 => p.teal,
22 => p.red,
23 => p.text,
24 => p.green,
25 => p.maroon,
_ => p.text,
}
}