highlight_map.rs

  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}