1use std::cmp::Reverse;
9use std::{convert::Infallible, ptr, str::FromStr};
10
11use crate::Error;
12use crate::grammar::{Grammar, ScopeId, ScopeSpan};
13use crate::raw::{RawStyle, RawTheme};
14use crate::util::{next_char_boundary, previous_char_boundary, trim_line_end};
15
16#[derive(Debug)]
18pub struct Theme {
19 pub name: String,
21 pub default: Style,
23 rules: Vec<ThemeRule>,
25}
26
27#[derive(Debug)]
29struct ThemeRule {
30 selector: String,
32 style: Style,
34}
35
36#[derive(Debug, Default)]
42pub(crate) struct StyleCache {
43 key: Option<CacheKey>,
45 styles: Vec<Style>,
47}
48
49type CacheKey = (usize, usize, usize);
51
52#[derive(Debug, Default)]
54pub(crate) struct StyleScratch {
55 boundaries: Vec<usize>,
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61struct Segment {
62 start: usize,
64 end: usize,
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub struct StyleSpan {
71 pub start: usize,
73 pub end: usize,
75 pub scope: Option<ScopeId>,
77 pub style: Style,
79}
80
81#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
83pub struct Rgb {
84 pub r: u8,
86 pub g: u8,
88 pub b: u8,
90}
91
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
94pub struct ParseRgbError;
95
96#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
98pub struct FontStyle(u8);
99
100#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
102pub struct Style {
103 pub foreground: Option<Rgb>,
105 pub font_style: FontStyle,
107}
108
109impl Theme {
110 pub fn compile(raw: &RawTheme) -> Self {
112 let mut default = Style::default();
113 let mut rules = Vec::new();
114 let raw_rules = raw.settings.as_deref().or(raw.token_colors.as_deref());
115
116 for rule in raw_rules.unwrap_or_default() {
117 let style = Style::from_raw(&rule.settings);
118 let selectors = rule.scope_selectors();
119 if selectors.is_empty() {
120 default = style;
121 } else {
122 rules.extend(
123 selectors
124 .into_iter()
125 .map(|selector| ThemeRule { selector, style }),
126 );
127 }
128 }
129 Self {
130 name: raw.name.clone(),
131 default,
132 rules,
133 }
134 }
135
136 pub fn parse(input: &str) -> Result<Self, Error> {
138 input.parse()
139 }
140
141 pub fn style_for_scope(&self, scope: &str) -> Style {
145 let mut style = self.default;
146 let mut best = 0;
147 for rule in &self.rules {
148 if rule.matches(scope) && rule.selector.len() >= best {
149 best = rule.selector.len();
150 style = rule.style.merge(style);
151 }
152 }
153 style
154 }
155
156 pub fn style_spans(
158 &self,
159 grammar: &Grammar,
160 line: &str,
161 scopes: &[ScopeSpan],
162 ) -> Vec<StyleSpan> {
163 let mut cache = StyleCache::default();
164 let mut scratch = StyleScratch::default();
165 let mut output = Vec::new();
166
167 cache.refresh(self, grammar);
168 self.style_line_into(grammar, line, scopes, &cache, &mut scratch, &mut output);
169 output
170 }
171
172 pub(crate) fn style_line_into(
174 &self,
175 grammar: &Grammar,
176 line: &str,
177 scopes: &[ScopeSpan],
178 cache: &StyleCache,
179 scratch: &mut StyleScratch,
180 output: &mut Vec<StyleSpan>,
181 ) {
182 output.clear();
183 let line = trim_line_end(line);
184 if line.is_empty() {
185 return;
186 }
187 scratch.collect_boundaries(line, scopes);
188
189 for segment in scratch.segments() {
190 output.push(self.style_segment(grammar, scopes, cache, segment));
191 }
192 }
193
194 fn style_segment(
196 &self,
197 grammar: &Grammar,
198 scopes: &[ScopeSpan],
199 cache: &StyleCache,
200 segment: Segment,
201 ) -> StyleSpan {
202 let Some(span) = segment.best_covering(scopes, grammar) else {
203 return segment.styled(None, self.default);
204 };
205 let style = cache
206 .style_for(span.scope)
207 .unwrap_or_else(|| self.style_for_scope_id(grammar, span.scope));
208 segment.styled(Some(span.scope), style)
209 }
210
211 fn style_for_scope_id(&self, grammar: &Grammar, scope: ScopeId) -> Style {
213 grammar
214 .scopes
215 .get(scope.index())
216 .map_or(self.default, |name| self.style_for_scope(name))
217 }
218}
219
220impl FromStr for Theme {
221 type Err = Error;
222
223 fn from_str(input: &str) -> Result<Self, Self::Err> {
225 let raw = RawTheme::from_str(input)?;
226 Ok(Self::compile(&raw))
227 }
228}
229
230impl ThemeRule {
231 fn matches(&self, scope: &str) -> bool {
233 scope == self.selector
234 || (scope.len() > self.selector.len()
235 && scope.starts_with(&self.selector)
236 && scope.as_bytes().get(self.selector.len()) == Some(&b'.'))
237 }
238}
239
240impl StyleCache {
241 pub(crate) fn refresh(&mut self, theme: &Theme, grammar: &Grammar) {
243 let key = (
244 ptr::from_ref(theme).addr(),
245 ptr::from_ref(grammar).addr(),
246 grammar.scopes.len(),
247 );
248 if self.key == Some(key) {
249 return;
250 }
251 self.styles.clear();
252 self.styles.extend(
253 grammar
254 .scopes
255 .iter()
256 .map(|scope| theme.style_for_scope(scope)),
257 );
258 self.key = Some(key);
259 }
260
261 fn style_for(&self, scope: ScopeId) -> Option<Style> {
263 self.styles.get(scope.index()).copied()
264 }
265}
266
267impl StyleScratch {
268 pub(crate) fn clear_line(&mut self) {
270 self.boundaries.clear();
271 }
272
273 fn collect_boundaries(&mut self, line: &str, scopes: &[ScopeSpan]) {
275 self.boundaries.clear();
276 self.boundaries.push(0);
277 self.boundaries.push(line.len());
278
279 for span in scopes {
280 let start = next_char_boundary(line, span.start);
281 let end = previous_char_boundary(line, span.end);
282 if start < end {
283 self.boundaries.push(start);
284 self.boundaries.push(end);
285 }
286 }
287 self.boundaries.sort_unstable();
288 self.boundaries.dedup();
289 }
290
291 fn segments(&self) -> impl Iterator<Item = Segment> + '_ {
293 self.boundaries.windows(2).filter_map(|window| {
294 let [start, end] = *window else {
295 return None;
296 };
297 (start < end).then_some(Segment { start, end })
298 })
299 }
300}
301
302impl Segment {
303 fn styled(self, scope: Option<ScopeId>, style: Style) -> StyleSpan {
305 StyleSpan {
306 start: self.start,
307 end: self.end,
308 scope,
309 style,
310 }
311 }
312
313 fn best_covering<'a>(self, spans: &'a [ScopeSpan], grammar: &Grammar) -> Option<&'a ScopeSpan> {
315 spans
316 .iter()
317 .filter(|span| span.start <= self.start && span.end >= self.end)
318 .min_by_key(|span| {
319 let scope_len = grammar
320 .scopes
321 .get(span.scope.index())
322 .map_or(0, String::len);
323 (span.end - span.start, Reverse(scope_len))
324 })
325 }
326}
327
328impl FromStr for Rgb {
329 type Err = ParseRgbError;
330
331 fn from_str(input: &str) -> Result<Self, Self::Err> {
333 let hex = input.strip_prefix('#').ok_or(ParseRgbError)?;
334 if hex.len() != 6 {
335 return Err(ParseRgbError);
336 }
337 let r = u8::from_str_radix(&hex[0..2], 16).map_err(|_| ParseRgbError)?;
338 let g = u8::from_str_radix(&hex[2..4], 16).map_err(|_| ParseRgbError)?;
339 let b = u8::from_str_radix(&hex[4..6], 16).map_err(|_| ParseRgbError)?;
340
341 Ok(Self { r, g, b })
342 }
343}
344
345impl FontStyle {
346 pub const BOLD: Self = Self(1 << 0);
348 pub const ITALIC: Self = Self(1 << 1);
350 pub const UNDERLINE: Self = Self(1 << 2);
352 pub const STRIKETHROUGH: Self = Self(1 << 3);
354
355 pub const fn empty() -> Self {
357 Self(0)
358 }
359
360 pub const fn contains(self, other: Self) -> bool {
362 (self.0 & other.0) == other.0
363 }
364
365 pub fn insert(&mut self, other: Self) {
367 self.0 |= other.0;
368 }
369}
370
371impl FromStr for FontStyle {
372 type Err = Infallible;
373
374 fn from_str(input: &str) -> Result<Self, Self::Err> {
376 let mut style = Self::empty();
377 for flag in input.split_whitespace() {
378 match flag {
379 "bold" => style.insert(Self::BOLD),
380 "italic" => style.insert(Self::ITALIC),
381 "underline" => style.insert(Self::UNDERLINE),
382 "strikethrough" => style.insert(Self::STRIKETHROUGH),
383 _ => {}
384 }
385 }
386 Ok(style)
387 }
388}
389
390impl Style {
391 fn from_raw(raw: &RawStyle) -> Self {
393 let mut style = Self::default();
394 if let Some(foreground) = &raw.foreground {
395 style.foreground = foreground.parse().ok();
396 }
397 if let Some(flags) = &raw.font_style {
398 style.font_style = flags.parse().unwrap_or_else(|error| match error {});
399 }
400 style
401 }
402
403 pub fn merge(self, base: Self) -> Self {
405 let mut font_style = base.font_style;
406 font_style.insert(self.font_style);
407 Self {
408 foreground: self.foreground.or(base.foreground),
409 font_style,
410 }
411 }
412
413 pub const fn fg(mut self, color: Rgb) -> Self {
415 self.foreground = Some(color);
416 self
417 }
418
419 pub fn bold(mut self) -> Self {
421 self.font_style.insert(FontStyle::BOLD);
422 self
423 }
424
425 pub fn italic(mut self) -> Self {
427 self.font_style.insert(FontStyle::ITALIC);
428 self
429 }
430
431 pub fn underline(mut self) -> Self {
433 self.font_style.insert(FontStyle::UNDERLINE);
434 self
435 }
436
437 pub fn strikethrough(mut self) -> Self {
439 self.font_style.insert(FontStyle::STRIKETHROUGH);
440 self
441 }
442}