registry.rs

  1use std::collections::HashMap;
  2use std::sync::Arc;
  3
  4use anyhow::{anyhow, Result};
  5use gpui::{HighlightStyle, SharedString};
  6use refineable::Refineable;
  7
  8use crate::{
  9    Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme, ThemeColors,
 10    ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
 11};
 12
 13#[derive(Debug, Clone)]
 14pub struct ThemeMeta {
 15    pub name: SharedString,
 16    pub appearance: Appearance,
 17}
 18
 19pub struct ThemeRegistry {
 20    themes: HashMap<SharedString, Arc<Theme>>,
 21}
 22
 23impl ThemeRegistry {
 24    fn insert_theme_families(&mut self, families: impl IntoIterator<Item = ThemeFamily>) {
 25        for family in families.into_iter() {
 26            self.insert_themes(family.themes);
 27        }
 28    }
 29
 30    fn insert_themes(&mut self, themes: impl IntoIterator<Item = Theme>) {
 31        for theme in themes.into_iter() {
 32            self.themes.insert(theme.name.clone(), Arc::new(theme));
 33        }
 34    }
 35
 36    #[allow(unused)]
 37    fn insert_user_theme_families(&mut self, families: impl IntoIterator<Item = UserThemeFamily>) {
 38        for family in families.into_iter() {
 39            self.insert_user_themes(family.themes);
 40        }
 41    }
 42
 43    #[allow(unused)]
 44    fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
 45        self.insert_themes(themes.into_iter().map(|user_theme| {
 46            let mut theme_colors = match user_theme.appearance {
 47                Appearance::Light => ThemeColors::light(),
 48                Appearance::Dark => ThemeColors::dark(),
 49            };
 50            theme_colors.refine(&user_theme.styles.colors);
 51
 52            let mut status_colors = match user_theme.appearance {
 53                Appearance::Light => StatusColors::light(),
 54                Appearance::Dark => StatusColors::dark(),
 55            };
 56            status_colors.refine(&user_theme.styles.status);
 57
 58            let mut player_colors = match user_theme.appearance {
 59                Appearance::Light => PlayerColors::light(),
 60                Appearance::Dark => PlayerColors::dark(),
 61            };
 62            if let Some(player_colors_from_theme) = user_theme.styles.player {
 63                player_colors = player_colors_from_theme;
 64            }
 65
 66            let mut syntax_colors = match user_theme.appearance {
 67                Appearance::Light => SyntaxTheme::light(),
 68                Appearance::Dark => SyntaxTheme::dark(),
 69            };
 70            if let Some(user_syntax) = user_theme.styles.syntax {
 71                syntax_colors.highlights = user_syntax
 72                    .highlights
 73                    .iter()
 74                    .map(|(syntax_token, highlight)| {
 75                        (
 76                            syntax_token.clone(),
 77                            HighlightStyle {
 78                                color: highlight.color,
 79                                font_style: highlight.font_style.map(Into::into),
 80                                font_weight: highlight.font_weight.map(Into::into),
 81                                ..Default::default()
 82                            },
 83                        )
 84                    })
 85                    .collect::<Vec<_>>();
 86            }
 87
 88            Theme {
 89                id: uuid::Uuid::new_v4().to_string(),
 90                name: user_theme.name.into(),
 91                appearance: user_theme.appearance,
 92                styles: ThemeStyles {
 93                    system: SystemColors::default(),
 94                    colors: theme_colors,
 95                    status: status_colors,
 96                    player: player_colors,
 97                    syntax: Arc::new(syntax_colors),
 98                    accents: Vec::new(),
 99                },
100            }
101        }));
102    }
103
104    pub fn clear(&mut self) {
105        self.themes.clear();
106    }
107
108    pub fn list_names(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
109        self.themes.keys().cloned()
110    }
111
112    pub fn list(&self, _staff: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
113        self.themes.values().map(|theme| ThemeMeta {
114            name: theme.name.clone(),
115            appearance: theme.appearance(),
116        })
117    }
118
119    pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
120        self.themes
121            .get(name)
122            .ok_or_else(|| anyhow!("theme not found: {}", name))
123            .cloned()
124    }
125
126    pub fn load_user_themes(&mut self) {
127        #[cfg(not(feature = "importing-themes"))]
128        self.insert_user_theme_families(crate::all_user_themes());
129    }
130}
131
132impl Default for ThemeRegistry {
133    fn default() -> Self {
134        let mut registry = Self {
135            themes: HashMap::default(),
136        };
137
138        // We're loading our new versions of the One themes by default, as
139        // we need them to be loaded for tests.
140        //
141        // These themes will get overwritten when `load_user_themes` is called
142        // when Zed starts, so the One variants used will be the ones ported from Zed1.
143        registry.insert_theme_families([crate::one_themes::one_family()]);
144
145        registry
146    }
147}