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}