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 const TABSTOP_INSERT_ID: HighlightId = HighlightId(u32::MAX - 1);
55 pub const TABSTOP_REPLACE_ID: HighlightId = HighlightId(u32::MAX - 2);
56
57 pub(crate) fn is_default(&self) -> bool {
58 *self == DEFAULT_SYNTAX_HIGHLIGHT_ID
59 }
60
61 pub fn style(&self, theme: &SyntaxTheme) -> Option<HighlightStyle> {
62 theme.highlights.get(self.0 as usize).map(|entry| entry.1)
63 }
64
65 pub fn name<'a>(&self, theme: &'a SyntaxTheme) -> Option<&'a str> {
66 theme.highlights.get(self.0 as usize).map(|e| e.0.as_str())
67 }
68}
69
70impl Default for HighlightMap {
71 fn default() -> Self {
72 Self(Arc::new([]))
73 }
74}
75
76impl Default for HighlightId {
77 fn default() -> Self {
78 DEFAULT_SYNTAX_HIGHLIGHT_ID
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use gpui::rgba;
86
87 #[test]
88 fn test_highlight_map() {
89 let theme = SyntaxTheme {
90 highlights: [
91 ("function", rgba(0x100000ff)),
92 ("function.method", rgba(0x200000ff)),
93 ("function.async", rgba(0x300000ff)),
94 ("variable.builtin.self.rust", rgba(0x400000ff)),
95 ("variable.builtin", rgba(0x500000ff)),
96 ("variable", rgba(0x600000ff)),
97 ]
98 .iter()
99 .map(|(name, color)| (name.to_string(), (*color).into()))
100 .collect(),
101 };
102
103 let capture_names = &[
104 "function.special",
105 "function.async.rust",
106 "variable.builtin.self",
107 ];
108
109 let map = HighlightMap::new(capture_names, &theme);
110 assert_eq!(map.get(0).name(&theme), Some("function"));
111 assert_eq!(map.get(1).name(&theme), Some("function.async"));
112 assert_eq!(map.get(2).name(&theme), Some("variable.builtin"));
113 }
114}