1use gpui::HighlightStyle;
2use std::sync::Arc;
3use theme::SyntaxTheme;
4
5#[derive(Clone, Debug)]
6pub struct HighlightMap(Arc<[HighlightId]>);
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct HighlightId(pub u32);
10
11const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
12
13impl HighlightMap {
14 pub(crate) fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
15 // For each capture name in the highlight query, find the longest
16 // key in the theme's syntax styles that matches all of the
17 // dot-separated components of the capture name.
18 HighlightMap(
19 capture_names
20 .iter()
21 .map(|capture_name| {
22 theme
23 .highlights
24 .iter()
25 .enumerate()
26 .filter_map(|(i, (key, _))| {
27 let mut len = 0;
28 let capture_parts = capture_name.split('.');
29 for key_part in key.split('.') {
30 if capture_parts.clone().any(|part| part == key_part) {
31 len += 1;
32 } else {
33 return None;
34 }
35 }
36 Some((i, len))
37 })
38 .max_by_key(|(_, len)| *len)
39 .map_or(DEFAULT_SYNTAX_HIGHLIGHT_ID, |(i, _)| HighlightId(i as u32))
40 })
41 .collect(),
42 )
43 }
44
45 pub fn get(&self, capture_id: u32) -> HighlightId {
46 self.0
47 .get(capture_id as usize)
48 .copied()
49 .unwrap_or(DEFAULT_SYNTAX_HIGHLIGHT_ID)
50 }
51}
52
53impl HighlightId {
54 pub(crate) fn is_default(&self) -> bool {
55 *self == DEFAULT_SYNTAX_HIGHLIGHT_ID
56 }
57
58 pub fn style(&self, theme: &SyntaxTheme) -> Option<HighlightStyle> {
59 theme.highlights.get(self.0 as usize).map(|entry| entry.1)
60 }
61
62 pub fn name<'a>(&self, theme: &'a SyntaxTheme) -> Option<&'a str> {
63 theme.highlights.get(self.0 as usize).map(|e| e.0.as_str())
64 }
65}
66
67impl Default for HighlightMap {
68 fn default() -> Self {
69 Self(Arc::new([]))
70 }
71}
72
73impl Default for HighlightId {
74 fn default() -> Self {
75 DEFAULT_SYNTAX_HIGHLIGHT_ID
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use gpui::rgba;
83
84 #[test]
85 fn test_highlight_map() {
86 let theme = SyntaxTheme {
87 highlights: [
88 ("function", rgba(0x100000ff)),
89 ("function.method", rgba(0x200000ff)),
90 ("function.async", rgba(0x300000ff)),
91 ("variable.builtin.self.rust", rgba(0x400000ff)),
92 ("variable.builtin", rgba(0x500000ff)),
93 ("variable", rgba(0x600000ff)),
94 ]
95 .iter()
96 .map(|(name, color)| (name.to_string(), (*color).into()))
97 .collect(),
98 };
99
100 let capture_names = &[
101 "function.special",
102 "function.async.rust",
103 "variable.builtin.self",
104 ];
105
106 let map = HighlightMap::new(capture_names, &theme);
107 assert_eq!(map.get(0).name(&theme), Some("function"));
108 assert_eq!(map.get(1).name(&theme), Some("function.async"));
109 assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
110 }
111}