1use std::collections::HashMap;
2use std::sync::Arc;
3
4use anyhow::{anyhow, Result};
5use gpui::{HighlightStyle, SharedString};
6use refineable::Refineable;
7
8use crate::{
9 one_themes::one_family, zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme,
10 SystemColors, Theme, ThemeColors, 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_familes(&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 = StatusColors::dark();
53 status_colors.refine(&user_theme.styles.status);
54
55 let mut syntax_colors = match user_theme.appearance {
56 Appearance::Light => SyntaxTheme::light(),
57 Appearance::Dark => SyntaxTheme::dark(),
58 };
59 if let Some(user_syntax) = user_theme.styles.syntax {
60 syntax_colors.highlights = user_syntax
61 .highlights
62 .iter()
63 .map(|(syntax_token, highlight)| {
64 (
65 syntax_token.clone(),
66 HighlightStyle {
67 color: highlight.color,
68 font_style: highlight.font_style.map(Into::into),
69 font_weight: highlight.font_weight.map(Into::into),
70 ..Default::default()
71 },
72 )
73 })
74 .collect::<Vec<_>>();
75 }
76
77 Theme {
78 id: uuid::Uuid::new_v4().to_string(),
79 name: user_theme.name.into(),
80 appearance: user_theme.appearance,
81 styles: ThemeStyles {
82 system: SystemColors::default(),
83 colors: theme_colors,
84 status: status_colors,
85 player: match user_theme.appearance {
86 Appearance::Light => PlayerColors::light(),
87 Appearance::Dark => PlayerColors::dark(),
88 },
89 syntax: Arc::new(syntax_colors),
90 accents: Vec::new(),
91 },
92 }
93 }));
94 }
95
96 pub fn clear(&mut self) {
97 self.themes.clear();
98 }
99
100 pub fn list_names(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
101 self.themes.keys().cloned()
102 }
103
104 pub fn list(&self, _staff: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
105 self.themes.values().map(|theme| ThemeMeta {
106 name: theme.name.clone(),
107 appearance: theme.appearance(),
108 })
109 }
110
111 pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
112 self.themes
113 .get(name)
114 .ok_or_else(|| anyhow!("theme not found: {}", name))
115 .cloned()
116 }
117
118 pub fn load_user_themes(&mut self) {
119 #[cfg(not(feature = "importing-themes"))]
120 self.insert_user_theme_familes(crate::all_user_themes());
121 }
122}
123
124impl Default for ThemeRegistry {
125 fn default() -> Self {
126 let mut this = Self {
127 themes: HashMap::default(),
128 };
129
130 this.insert_theme_families([zed_pro_family(), one_family()]);
131
132 this
133 }
134}