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