1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{anyhow, Result};
5use gpui::{HighlightStyle, SharedString};
6use refineable::Refineable;
7
8use crate::{
9 zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
10 ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
11};
12
13pub struct ThemeRegistry {
14 themes: HashMap<SharedString, Arc<Theme>>,
15}
16
17impl ThemeRegistry {
18 fn insert_theme_families(&mut self, families: impl IntoIterator<Item = ThemeFamily>) {
19 for family in families.into_iter() {
20 self.insert_themes(family.themes);
21 }
22 }
23
24 fn insert_themes(&mut self, themes: impl IntoIterator<Item = Theme>) {
25 for theme in themes.into_iter() {
26 self.themes.insert(theme.name.clone(), Arc::new(theme));
27 }
28 }
29
30 fn insert_user_theme_familes(&mut self, families: impl IntoIterator<Item = UserThemeFamily>) {
31 for family in families.into_iter() {
32 self.insert_user_themes(family.themes);
33 }
34 }
35
36 fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
37 self.insert_themes(themes.into_iter().map(|user_theme| {
38 let mut theme_colors = match user_theme.appearance {
39 Appearance::Light => ThemeColors::default_light(),
40 Appearance::Dark => ThemeColors::default_dark(),
41 };
42 theme_colors.refine(&user_theme.styles.colors);
43
44 let mut status_colors = StatusColors::default();
45 status_colors.refine(&user_theme.styles.status);
46
47 let mut syntax_colors = match user_theme.appearance {
48 Appearance::Light => SyntaxTheme::default_light(),
49 Appearance::Dark => SyntaxTheme::default_dark(),
50 };
51 if let Some(user_syntax) = user_theme.styles.syntax {
52 syntax_colors.highlights = user_syntax
53 .highlights
54 .iter()
55 .map(|(syntax_token, highlight)| {
56 (
57 syntax_token.clone(),
58 HighlightStyle {
59 color: highlight.color,
60 font_style: highlight.font_style.map(Into::into),
61 font_weight: highlight.font_weight.map(Into::into),
62 ..Default::default()
63 },
64 )
65 })
66 .collect::<Vec<_>>();
67 }
68
69 Theme {
70 id: uuid::Uuid::new_v4().to_string(),
71 name: user_theme.name.into(),
72 appearance: user_theme.appearance,
73 styles: ThemeStyles {
74 system: SystemColors::default(),
75 colors: theme_colors,
76 status: status_colors,
77 player: PlayerColors::default(),
78 syntax: Arc::new(syntax_colors),
79 },
80 }
81 }));
82 }
83
84 pub fn list_names(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
85 self.themes.keys().cloned()
86 }
87
88 pub fn list(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
89 self.themes.values().map(|theme| theme.name.clone())
90 }
91
92 pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
93 self.themes
94 .get(name)
95 .ok_or_else(|| anyhow!("theme not found: {}", name))
96 .cloned()
97 }
98}
99
100impl Default for ThemeRegistry {
101 fn default() -> Self {
102 let mut this = Self {
103 themes: HashMap::default(),
104 };
105
106 this.insert_theme_families([zed_pro_family()]);
107
108 #[cfg(not(feature = "importing-themes"))]
109 this.insert_user_theme_familes(crate::all_user_themes());
110
111 this
112 }
113}