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 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}