theme.rs

  1use anyhow::{anyhow, Context, Result};
  2use gpui::{
  3    color::Color,
  4    elements::{ContainerStyle, LabelStyle},
  5    fonts::TextStyle,
  6    AssetSource,
  7};
  8use json::{Map, Value};
  9use parking_lot::Mutex;
 10use serde::{Deserialize, Deserializer};
 11use serde_json as json;
 12use std::{collections::HashMap, fmt, mem, sync::Arc};
 13
 14const DEFAULT_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
 15pub const DEFAULT_THEME_NAME: &'static str = "dark";
 16
 17pub struct ThemeRegistry {
 18    assets: Box<dyn AssetSource>,
 19    themes: Mutex<HashMap<String, Arc<Theme>>>,
 20    theme_data: Mutex<HashMap<String, Arc<Value>>>,
 21}
 22
 23#[derive(Clone, Debug)]
 24pub struct HighlightMap(Arc<[HighlightId]>);
 25
 26#[derive(Clone, Copy, Debug)]
 27pub struct HighlightId(u32);
 28
 29#[derive(Debug, Default, Deserialize)]
 30pub struct Theme {
 31    #[serde(default)]
 32    pub name: String,
 33    pub workspace: Workspace,
 34    pub tab: Tab,
 35    pub active_tab: Tab,
 36    pub sidebar: ContainerStyle,
 37    pub sidebar_icon: SidebarIcon,
 38    pub active_sidebar_icon: SidebarIcon,
 39    pub selector: Selector,
 40    pub editor: Editor,
 41    #[serde(deserialize_with = "deserialize_syntax_theme")]
 42    pub syntax: Vec<(String, TextStyle)>,
 43}
 44
 45#[derive(Debug, Default, Deserialize)]
 46pub struct Workspace {
 47    pub background: Color,
 48}
 49
 50#[derive(Debug, Deserialize)]
 51pub struct Editor {
 52    pub background: Color,
 53    pub gutter_background: Color,
 54    pub active_line_background: Color,
 55    pub line_number: Color,
 56    pub line_number_active: Color,
 57    pub text: Color,
 58    pub replicas: Vec<Replica>,
 59}
 60
 61#[derive(Clone, Copy, Debug, Default, Deserialize)]
 62pub struct Replica {
 63    pub cursor: Color,
 64    pub selection: Color,
 65}
 66
 67#[derive(Debug, Default, Deserialize)]
 68pub struct Tab {
 69    #[serde(flatten)]
 70    pub container: ContainerStyle,
 71    #[serde(flatten)]
 72    pub label: LabelStyle,
 73    pub icon_close: Color,
 74    pub icon_dirty: Color,
 75    pub icon_conflict: Color,
 76}
 77
 78#[derive(Debug, Default, Deserialize)]
 79pub struct SidebarIcon {
 80    pub color: Color,
 81}
 82
 83#[derive(Debug, Default, Deserialize)]
 84pub struct Selector {
 85    #[serde(flatten)]
 86    pub container: ContainerStyle,
 87    #[serde(flatten)]
 88    pub label: LabelStyle,
 89
 90    pub item: SelectorItem,
 91    pub active_item: SelectorItem,
 92}
 93
 94#[derive(Debug, Default, Deserialize)]
 95pub struct SelectorItem {
 96    #[serde(flatten)]
 97    pub container: ContainerStyle,
 98    #[serde(flatten)]
 99    pub label: LabelStyle,
100}
101
102#[derive(Default)]
103struct KeyPathReferenceSet {
104    references: Vec<KeyPathReference>,
105    reference_ids_by_source: Vec<usize>,
106    reference_ids_by_target: Vec<usize>,
107    dependencies: Vec<(usize, usize)>,
108    dependency_counts: Vec<usize>,
109}
110
111#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
112struct KeyPathReference {
113    target: KeyPath,
114    source: KeyPath,
115}
116
117#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
118struct KeyPath(Vec<Key>);
119
120#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
121enum Key {
122    Array(usize),
123    Object(String),
124}
125
126impl Default for Editor {
127    fn default() -> Self {
128        Self {
129            background: Default::default(),
130            gutter_background: Default::default(),
131            active_line_background: Default::default(),
132            line_number: Default::default(),
133            line_number_active: Default::default(),
134            text: Default::default(),
135            replicas: vec![Replica::default()],
136        }
137    }
138}
139
140impl ThemeRegistry {
141    pub fn new(source: impl AssetSource) -> Arc<Self> {
142        Arc::new(Self {
143            assets: Box::new(source),
144            themes: Default::default(),
145            theme_data: Default::default(),
146        })
147    }
148
149    pub fn list(&self) -> impl Iterator<Item = String> {
150        self.assets.list("themes/").into_iter().filter_map(|path| {
151            let filename = path.strip_prefix("themes/")?;
152            let theme_name = filename.strip_suffix(".toml")?;
153            if theme_name.starts_with('_') {
154                None
155            } else {
156                Some(theme_name.to_string())
157            }
158        })
159    }
160
161    pub fn clear(&self) {
162        self.theme_data.lock().clear();
163        self.themes.lock().clear();
164    }
165
166    pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
167        if let Some(theme) = self.themes.lock().get(name) {
168            return Ok(theme.clone());
169        }
170
171        let theme_data = self.load(name, true)?;
172        let mut theme = serde_json::from_value::<Theme>(theme_data.as_ref().clone())?;
173        theme.name = name.into();
174        let theme = Arc::new(theme);
175        self.themes.lock().insert(name.to_string(), theme.clone());
176        Ok(theme)
177    }
178
179    fn load(&self, name: &str, evaluate_references: bool) -> Result<Arc<Value>> {
180        if let Some(data) = self.theme_data.lock().get(name) {
181            return Ok(data.clone());
182        }
183
184        let asset_path = format!("themes/{}.toml", name);
185        let source_code = self
186            .assets
187            .load(&asset_path)
188            .with_context(|| format!("failed to load theme file {}", asset_path))?;
189
190        let mut theme_data: Map<String, Value> = toml::from_slice(source_code.as_ref())
191            .with_context(|| format!("failed to parse {}.toml", name))?;
192
193        // If this theme extends another base theme, deeply merge it into the base theme's data
194        if let Some(base_name) = theme_data
195            .get("extends")
196            .and_then(|name| name.as_str())
197            .map(str::to_string)
198        {
199            let base_theme_data = self
200                .load(&base_name, false)
201                .with_context(|| format!("failed to load base theme {}", base_name))?
202                .as_ref()
203                .clone();
204            if let Value::Object(mut base_theme_object) = base_theme_data {
205                deep_merge_json(&mut base_theme_object, theme_data);
206                theme_data = base_theme_object;
207            }
208        }
209
210        // Find all of the key path references in the object, and then sort them according
211        // to their dependencies.
212        if evaluate_references {
213            let mut key_path = KeyPath::default();
214            let mut references = KeyPathReferenceSet::default();
215            for (key, value) in theme_data.iter() {
216                key_path.0.push(Key::Object(key.clone()));
217                find_references(value, &mut key_path, &mut references);
218                key_path.0.pop();
219            }
220            let sorted_references = references
221                .top_sort()
222                .map_err(|key_paths| anyhow!("cycle for key paths: {:?}", key_paths))?;
223
224            // Now update objects to include the fields of objects they extend
225            for KeyPathReference { source, target } in sorted_references {
226                if let Some(source) = value_at(&mut theme_data, &source).cloned() {
227                    let target = value_at(&mut theme_data, &target).unwrap();
228                    if let Value::Object(target_object) = target.take() {
229                        if let Value::Object(mut source_object) = source {
230                            deep_merge_json(&mut source_object, target_object);
231                            *target = Value::Object(source_object);
232                        } else {
233                            Err(anyhow!("extended key path {} is not an object", source))?;
234                        }
235                    } else {
236                        *target = source;
237                    }
238                } else {
239                    Err(anyhow!("invalid key path '{}'", source))?;
240                }
241            }
242        }
243
244        let result = Arc::new(Value::Object(theme_data));
245        self.theme_data
246            .lock()
247            .insert(name.to_string(), result.clone());
248
249        Ok(result)
250    }
251}
252
253impl Theme {
254    pub fn highlight_style(&self, id: HighlightId) -> TextStyle {
255        self.syntax
256            .get(id.0 as usize)
257            .map(|entry| entry.1.clone())
258            .unwrap_or_else(|| TextStyle {
259                color: self.editor.text,
260                font_properties: Default::default(),
261            })
262    }
263
264    #[cfg(test)]
265    pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
266        self.syntax.get(id.0 as usize).map(|e| e.0.as_str())
267    }
268}
269
270impl HighlightMap {
271    pub fn new(capture_names: &[String], theme: &Theme) -> Self {
272        // For each capture name in the highlight query, find the longest
273        // key in the theme's syntax styles that matches all of the
274        // dot-separated components of the capture name.
275        HighlightMap(
276            capture_names
277                .iter()
278                .map(|capture_name| {
279                    theme
280                        .syntax
281                        .iter()
282                        .enumerate()
283                        .filter_map(|(i, (key, _))| {
284                            let mut len = 0;
285                            let capture_parts = capture_name.split('.');
286                            for key_part in key.split('.') {
287                                if capture_parts.clone().any(|part| part == key_part) {
288                                    len += 1;
289                                } else {
290                                    return None;
291                                }
292                            }
293                            Some((i, len))
294                        })
295                        .max_by_key(|(_, len)| *len)
296                        .map_or(DEFAULT_HIGHLIGHT_ID, |(i, _)| HighlightId(i as u32))
297                })
298                .collect(),
299        )
300    }
301
302    pub fn get(&self, capture_id: u32) -> HighlightId {
303        self.0
304            .get(capture_id as usize)
305            .copied()
306            .unwrap_or(DEFAULT_HIGHLIGHT_ID)
307    }
308}
309
310impl KeyPathReferenceSet {
311    fn insert(&mut self, reference: KeyPathReference) {
312        let id = self.references.len();
313        let source_ix = self
314            .reference_ids_by_source
315            .binary_search_by_key(&&reference.source, |id| &self.references[*id].source)
316            .unwrap_or_else(|i| i);
317        let target_ix = self
318            .reference_ids_by_target
319            .binary_search_by_key(&&reference.target, |id| &self.references[*id].target)
320            .unwrap_or_else(|i| i);
321
322        self.populate_dependencies(id, &reference);
323        self.reference_ids_by_source.insert(source_ix, id);
324        self.reference_ids_by_target.insert(target_ix, id);
325        self.references.push(reference);
326    }
327
328    fn top_sort(mut self) -> Result<Vec<KeyPathReference>, Vec<KeyPath>> {
329        let mut results = Vec::with_capacity(self.references.len());
330        let mut root_ids = Vec::with_capacity(self.references.len());
331
332        // Find the initial set of references that have no dependencies.
333        for (id, dep_count) in self.dependency_counts.iter().enumerate() {
334            if *dep_count == 0 {
335                root_ids.push(id);
336            }
337        }
338
339        while results.len() < root_ids.len() {
340            // Just to guarantee a stable result when the inputs are randomized,
341            // sort references lexicographically in absence of any dependency relationship.
342            root_ids[results.len()..].sort_by_key(|id| &self.references[*id]);
343
344            let root_id = root_ids[results.len()];
345            let root = mem::take(&mut self.references[root_id]);
346            results.push(root);
347
348            // Remove this reference as a dependency from any of its dependent references.
349            if let Ok(dep_ix) = self
350                .dependencies
351                .binary_search_by_key(&root_id, |edge| edge.0)
352            {
353                let mut first_dep_ix = dep_ix;
354                let mut last_dep_ix = dep_ix + 1;
355                while first_dep_ix > 0 && self.dependencies[first_dep_ix - 1].0 == root_id {
356                    first_dep_ix -= 1;
357                }
358                while last_dep_ix < self.dependencies.len()
359                    && self.dependencies[last_dep_ix].0 == root_id
360                {
361                    last_dep_ix += 1;
362                }
363
364                // If any reference no longer has any dependencies, then then mark it as a root.
365                // Preserve the references' original order where possible.
366                for (_, successor_id) in self.dependencies.drain(first_dep_ix..last_dep_ix) {
367                    self.dependency_counts[successor_id] -= 1;
368                    if self.dependency_counts[successor_id] == 0 {
369                        root_ids.push(successor_id);
370                    }
371                }
372            }
373        }
374
375        // If any references never became roots, then there are reference cycles
376        // in the set. Return an error containing all of the key paths that are
377        // directly involved in cycles.
378        if results.len() < self.references.len() {
379            let mut cycle_ref_ids = (0..self.references.len())
380                .filter(|id| !root_ids.contains(id))
381                .collect::<Vec<_>>();
382
383            // Iteratively remove any references that have no dependencies,
384            // so that the error will only indicate which key paths are directly
385            // involved in the cycles.
386            let mut done = false;
387            while !done {
388                done = true;
389                cycle_ref_ids.retain(|id| {
390                    if self.dependencies.iter().any(|dep| dep.0 == *id) {
391                        true
392                    } else {
393                        done = false;
394                        self.dependencies.retain(|dep| dep.1 != *id);
395                        false
396                    }
397                });
398            }
399
400            let mut cycle_key_paths = Vec::new();
401            for id in cycle_ref_ids {
402                let reference = &self.references[id];
403                cycle_key_paths.push(reference.target.clone());
404                cycle_key_paths.push(reference.source.clone());
405            }
406            cycle_key_paths.sort_unstable();
407            return Err(cycle_key_paths);
408        }
409
410        Ok(results)
411    }
412
413    fn populate_dependencies(&mut self, new_id: usize, new_reference: &KeyPathReference) {
414        self.dependency_counts.push(0);
415
416        // If an existing reference's source path starts with the new reference's
417        // target path, then insert this new reference before that existing reference.
418        for id in Self::reference_ids_for_key_path(
419            &new_reference.target.0,
420            &self.references,
421            &self.reference_ids_by_source,
422            KeyPathReference::source,
423            KeyPath::starts_with,
424        ) {
425            Self::add_dependency(
426                (new_id, id),
427                &mut self.dependencies,
428                &mut self.dependency_counts,
429            );
430        }
431
432        // If an existing reference's target path starts with the new reference's
433        // source path, then insert this new reference after that existing reference.
434        for id in Self::reference_ids_for_key_path(
435            &new_reference.source.0,
436            &self.references,
437            &self.reference_ids_by_target,
438            KeyPathReference::target,
439            KeyPath::starts_with,
440        ) {
441            Self::add_dependency(
442                (id, new_id),
443                &mut self.dependencies,
444                &mut self.dependency_counts,
445            );
446        }
447
448        // If an existing reference's source path is a prefix of the new reference's
449        // target path, then insert this new reference before that existing reference.
450        for prefix in new_reference.target.prefixes() {
451            for id in Self::reference_ids_for_key_path(
452                prefix,
453                &self.references,
454                &self.reference_ids_by_source,
455                KeyPathReference::source,
456                PartialEq::eq,
457            ) {
458                Self::add_dependency(
459                    (new_id, id),
460                    &mut self.dependencies,
461                    &mut self.dependency_counts,
462                );
463            }
464        }
465
466        // If an existing reference's target path is a prefix of the new reference's
467        // source path, then insert this new reference after that existing reference.
468        for prefix in new_reference.source.prefixes() {
469            for id in Self::reference_ids_for_key_path(
470                prefix,
471                &self.references,
472                &self.reference_ids_by_target,
473                KeyPathReference::target,
474                PartialEq::eq,
475            ) {
476                Self::add_dependency(
477                    (id, new_id),
478                    &mut self.dependencies,
479                    &mut self.dependency_counts,
480                );
481            }
482        }
483    }
484
485    // Find all existing references that satisfy a given predicate with respect
486    // to a given key path. Use a sorted array of reference ids in order to avoid
487    // performing unnecessary comparisons.
488    fn reference_ids_for_key_path<'a>(
489        key_path: &[Key],
490        references: &[KeyPathReference],
491        sorted_reference_ids: &'a [usize],
492        reference_attribute: impl Fn(&KeyPathReference) -> &KeyPath,
493        predicate: impl Fn(&KeyPath, &[Key]) -> bool,
494    ) -> impl Iterator<Item = usize> + 'a {
495        let ix = sorted_reference_ids
496            .binary_search_by_key(&key_path, |id| &reference_attribute(&references[*id]).0)
497            .unwrap_or_else(|i| i);
498
499        let mut start_ix = ix;
500        while start_ix > 0 {
501            let reference_id = sorted_reference_ids[start_ix - 1];
502            let reference = &references[reference_id];
503            if !predicate(&reference_attribute(reference), key_path) {
504                break;
505            }
506            start_ix -= 1;
507        }
508
509        let mut end_ix = ix;
510        while end_ix < sorted_reference_ids.len() {
511            let reference_id = sorted_reference_ids[end_ix];
512            let reference = &references[reference_id];
513            if !predicate(&reference_attribute(reference), key_path) {
514                break;
515            }
516            end_ix += 1;
517        }
518
519        sorted_reference_ids[start_ix..end_ix].iter().copied()
520    }
521
522    fn add_dependency(
523        (predecessor, successor): (usize, usize),
524        dependencies: &mut Vec<(usize, usize)>,
525        dependency_counts: &mut Vec<usize>,
526    ) {
527        let dependency = (predecessor, successor);
528        if let Err(i) = dependencies.binary_search(&dependency) {
529            dependencies.insert(i, dependency);
530        }
531        dependency_counts[successor] += 1;
532    }
533}
534
535impl KeyPathReference {
536    fn source(&self) -> &KeyPath {
537        &self.source
538    }
539
540    fn target(&self) -> &KeyPath {
541        &self.target
542    }
543}
544
545impl KeyPath {
546    fn new(string: &str) -> Self {
547        Self(
548            string
549                .split(".")
550                .map(|key| Key::Object(key.to_string()))
551                .collect(),
552        )
553    }
554
555    fn starts_with(&self, other: &[Key]) -> bool {
556        self.0.starts_with(&other)
557    }
558
559    fn prefixes(&self) -> impl Iterator<Item = &[Key]> {
560        (1..self.0.len()).map(move |end_ix| &self.0[0..end_ix])
561    }
562}
563
564impl PartialEq<[Key]> for KeyPath {
565    fn eq(&self, other: &[Key]) -> bool {
566        self.0.eq(other)
567    }
568}
569
570impl fmt::Debug for KeyPathReference {
571    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
572        write!(
573            f,
574            "KeyPathReference {{ {} <- {} }}",
575            self.target, self.source
576        )
577    }
578}
579
580impl fmt::Display for KeyPath {
581    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582        for (i, key) in self.0.iter().enumerate() {
583            match key {
584                Key::Array(index) => write!(f, "[{}]", index)?,
585                Key::Object(key) => {
586                    if i > 0 {
587                        ".".fmt(f)?;
588                    }
589                    key.fmt(f)?;
590                }
591            }
592        }
593        Ok(())
594    }
595}
596
597impl Default for HighlightMap {
598    fn default() -> Self {
599        Self(Arc::new([]))
600    }
601}
602
603impl Default for HighlightId {
604    fn default() -> Self {
605        DEFAULT_HIGHLIGHT_ID
606    }
607}
608
609fn deep_merge_json(base: &mut Map<String, Value>, extension: Map<String, Value>) {
610    for (key, extension_value) in extension {
611        if let Value::Object(extension_object) = extension_value {
612            if let Some(base_object) = base.get_mut(&key).and_then(|value| value.as_object_mut()) {
613                deep_merge_json(base_object, extension_object);
614            } else {
615                base.insert(key, Value::Object(extension_object));
616            }
617        } else {
618            base.insert(key, extension_value);
619        }
620    }
621}
622
623fn find_references(value: &Value, key_path: &mut KeyPath, references: &mut KeyPathReferenceSet) {
624    match value {
625        Value::Array(vec) => {
626            for (ix, value) in vec.iter().enumerate() {
627                key_path.0.push(Key::Array(ix));
628                find_references(value, key_path, references);
629                key_path.0.pop();
630            }
631        }
632        Value::Object(map) => {
633            for (key, value) in map.iter() {
634                if key == "extends" {
635                    if let Some(source_path) = value.as_str().and_then(|s| s.strip_prefix("$")) {
636                        references.insert(KeyPathReference {
637                            source: KeyPath::new(source_path),
638                            target: key_path.clone(),
639                        });
640                    }
641                } else {
642                    key_path.0.push(Key::Object(key.to_string()));
643                    find_references(value, key_path, references);
644                    key_path.0.pop();
645                }
646            }
647        }
648        Value::String(string) => {
649            if let Some(source_path) = string.strip_prefix("$") {
650                references.insert(KeyPathReference {
651                    source: KeyPath::new(source_path),
652                    target: key_path.clone(),
653                });
654            }
655        }
656        _ => {}
657    }
658}
659
660fn value_at<'a>(object: &'a mut Map<String, Value>, key_path: &KeyPath) -> Option<&'a mut Value> {
661    let mut key_path = key_path.0.iter();
662    if let Some(Key::Object(first_key)) = key_path.next() {
663        let mut cur_value = object.get_mut(first_key);
664        for key in key_path {
665            if let Some(value) = cur_value {
666                match key {
667                    Key::Array(ix) => cur_value = value.get_mut(ix),
668                    Key::Object(key) => cur_value = value.get_mut(key),
669                }
670            } else {
671                return None;
672            }
673        }
674        cur_value
675    } else {
676        None
677    }
678}
679
680pub fn deserialize_syntax_theme<'de, D>(
681    deserializer: D,
682) -> Result<Vec<(String, TextStyle)>, D::Error>
683where
684    D: Deserializer<'de>,
685{
686    let mut result = Vec::<(String, TextStyle)>::new();
687
688    let syntax_data: HashMap<String, TextStyle> = Deserialize::deserialize(deserializer)?;
689    for (key, style) in syntax_data {
690        match result.binary_search_by(|(needle, _)| needle.cmp(&key)) {
691            Ok(i) | Err(i) => {
692                result.insert(i, (key, style));
693            }
694        }
695    }
696
697    Ok(result)
698}
699
700#[cfg(test)]
701mod tests {
702    use rand::{prelude::StdRng, Rng};
703
704    use super::*;
705    use crate::assets::Assets;
706
707    #[test]
708    fn test_bundled_themes() {
709        let registry = ThemeRegistry::new(Assets);
710        let mut has_default_theme = false;
711        for theme_name in registry.list() {
712            let theme = registry.get(&theme_name).unwrap();
713            if theme.name == DEFAULT_THEME_NAME {
714                has_default_theme = true;
715            }
716            assert_eq!(theme.name, theme_name);
717        }
718        assert!(has_default_theme);
719    }
720
721    #[test]
722    fn test_theme_extension() {
723        let assets = TestAssets(&[
724            (
725                "themes/_base.toml",
726                r##"
727                [ui.active_tab]
728                extends = "$ui.tab"
729                border.color = "#666666"
730                text = "$text_colors.bright"
731
732                [ui.tab]
733                extends = "$ui.element"
734                text = "$text_colors.dull"
735
736                [ui.element]
737                background = "#111111"
738                border = {width = 2.0, color = "#00000000"}
739
740                [editor]
741                background = "#222222"
742                default_text = "$text_colors.regular"
743                "##,
744            ),
745            (
746                "themes/light.toml",
747                r##"
748                extends = "_base"
749
750                [text_colors]
751                bright = "#ffffff"
752                regular = "#eeeeee"
753                dull = "#dddddd"
754
755                [editor]
756                background = "#232323"
757                "##,
758            ),
759        ]);
760
761        let registry = ThemeRegistry::new(assets);
762        let theme_data = registry.load("light", true).unwrap();
763        assert_eq!(
764            theme_data.as_ref(),
765            &serde_json::json!({
766              "ui": {
767                "active_tab": {
768                  "background": "#111111",
769                  "border": {
770                    "width": 2.0,
771                    "color": "#666666"
772                  },
773                  "extends": "$ui.tab",
774                  "text": "#ffffff"
775                },
776                "tab": {
777                  "background": "#111111",
778                  "border": {
779                    "width": 2.0,
780                    "color": "#00000000"
781                  },
782                  "extends": "$ui.element",
783                  "text": "#dddddd"
784                },
785                "element": {
786                  "background": "#111111",
787                  "border": {
788                    "width": 2.0,
789                    "color": "#00000000"
790                  }
791                }
792              },
793              "editor": {
794                "background": "#232323",
795                "default_text": "#eeeeee"
796              },
797              "extends": "_base",
798              "text_colors": {
799                "bright": "#ffffff",
800                "regular": "#eeeeee",
801                "dull": "#dddddd"
802              }
803            })
804        );
805    }
806
807    #[test]
808    fn test_highlight_map() {
809        let theme = Theme {
810            name: "test".into(),
811            syntax: [
812                ("function", Color::from_u32(0x100000ff)),
813                ("function.method", Color::from_u32(0x200000ff)),
814                ("function.async", Color::from_u32(0x300000ff)),
815                ("variable.builtin.self.rust", Color::from_u32(0x400000ff)),
816                ("variable.builtin", Color::from_u32(0x500000ff)),
817                ("variable", Color::from_u32(0x600000ff)),
818            ]
819            .iter()
820            .map(|(name, color)| (name.to_string(), (*color).into()))
821            .collect(),
822            ..Default::default()
823        };
824
825        let capture_names = &[
826            "function.special".to_string(),
827            "function.async.rust".to_string(),
828            "variable.builtin.self".to_string(),
829        ];
830
831        let map = HighlightMap::new(capture_names, &theme);
832        assert_eq!(theme.highlight_name(map.get(0)), Some("function"));
833        assert_eq!(theme.highlight_name(map.get(1)), Some("function.async"));
834        assert_eq!(theme.highlight_name(map.get(2)), Some("variable.builtin"));
835    }
836
837    #[test]
838    fn test_key_path_reference_set_simple() {
839        let input_references = build_refs(&[
840            ("r", "a"),
841            ("a.b.c", "d"),
842            ("d.e", "f"),
843            ("t.u", "v"),
844            ("v.w", "x"),
845            ("v.y", "x"),
846            ("d.h", "i"),
847            ("v.z", "x"),
848            ("f.g", "d.h"),
849        ]);
850        let expected_references = build_refs(&[
851            ("d.h", "i"),
852            ("f.g", "d.h"),
853            ("d.e", "f"),
854            ("a.b.c", "d"),
855            ("r", "a"),
856            ("v.w", "x"),
857            ("v.y", "x"),
858            ("v.z", "x"),
859            ("t.u", "v"),
860        ])
861        .collect::<Vec<_>>();
862
863        let mut reference_set = KeyPathReferenceSet::default();
864        for reference in input_references {
865            reference_set.insert(reference);
866        }
867        assert_eq!(reference_set.top_sort().unwrap(), expected_references);
868    }
869
870    #[test]
871    fn test_key_path_reference_set_with_cycles() {
872        let input_references = build_refs(&[
873            ("x", "a.b"),
874            ("y", "x.c"),
875            ("a.b.c", "d.e"),
876            ("d.e.f", "g.h"),
877            ("g.h.i", "a"),
878        ]);
879
880        let mut reference_set = KeyPathReferenceSet::default();
881        for reference in input_references {
882            reference_set.insert(reference);
883        }
884
885        assert_eq!(
886            reference_set.top_sort().unwrap_err(),
887            &[
888                KeyPath::new("a"),
889                KeyPath::new("a.b.c"),
890                KeyPath::new("d.e"),
891                KeyPath::new("d.e.f"),
892                KeyPath::new("g.h"),
893                KeyPath::new("g.h.i"),
894            ]
895        );
896    }
897
898    #[gpui::test(iterations = 20)]
899    async fn test_key_path_reference_set_random(mut rng: StdRng) {
900        let examples: &[&[_]] = &[
901            &[
902                ("n.d.h", "i"),
903                ("f.g", "n.d.h"),
904                ("n.d.e", "f"),
905                ("a.b.c", "n.d"),
906                ("r", "a"),
907                ("q.q.q", "r.s"),
908                ("r.t", "q"),
909                ("x.x", "r.r"),
910                ("v.w", "x"),
911                ("v.y", "x"),
912                ("v.z", "x"),
913                ("t.u", "v"),
914            ],
915            &[
916                ("w.x.y.z", "t.u.z"),
917                ("x", "w.x"),
918                ("a.b.c1", "x.b1.c"),
919                ("a.b.c2", "x.b2.c"),
920            ],
921            &[
922                ("x.y", "m.n.n.o.q"),
923                ("x.y.z", "m.n.n.o.p"),
924                ("u.v.w", "x.y.z"),
925                ("a.b.c.d", "u.v"),
926                ("a.b.c.d.e", "u.v"),
927                ("a.b.c.d.f", "u.v"),
928                ("a.b.c.d.g", "u.v"),
929            ],
930        ];
931
932        for example in examples {
933            let expected_references = build_refs(example).collect::<Vec<_>>();
934            let mut input_references = expected_references.clone();
935            input_references.sort_by_key(|_| rng.gen_range(0..1000));
936            let mut reference_set = KeyPathReferenceSet::default();
937            for reference in input_references {
938                reference_set.insert(reference);
939            }
940            assert_eq!(reference_set.top_sort().unwrap(), expected_references);
941        }
942    }
943
944    fn build_refs<'a>(rows: &'a [(&str, &str)]) -> impl Iterator<Item = KeyPathReference> + 'a {
945        rows.iter().map(|(target, source)| KeyPathReference {
946            target: KeyPath::new(target),
947            source: KeyPath::new(source),
948        })
949    }
950
951    struct TestAssets(&'static [(&'static str, &'static str)]);
952
953    impl AssetSource for TestAssets {
954        fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
955            if let Some(row) = self.0.iter().find(|e| e.0 == path) {
956                Ok(row.1.as_bytes().into())
957            } else {
958                Err(anyhow!("no such path {}", path))
959            }
960        }
961
962        fn list(&self, prefix: &str) -> Vec<std::borrow::Cow<'static, str>> {
963            self.0
964                .iter()
965                .copied()
966                .filter_map(|(path, _)| {
967                    if path.starts_with(prefix) {
968                        Some(path.into())
969                    } else {
970                        None
971                    }
972                })
973                .collect()
974        }
975    }
976}