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, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
10 Theme, 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 #[allow(unused)]
31 fn insert_user_theme_familes(&mut self, families: impl IntoIterator<Item = UserThemeFamily>) {
32 for family in families.into_iter() {
33 self.insert_user_themes(family.themes);
34 }
35 }
36
37 #[allow(unused)]
38 fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
39 self.insert_themes(themes.into_iter().map(|user_theme| {
40 let mut theme_colors = match user_theme.appearance {
41 Appearance::Light => ThemeColors::light(),
42 Appearance::Dark => ThemeColors::dark(),
43 };
44 theme_colors.refine(&user_theme.styles.colors);
45
46 let mut status_colors = StatusColors::dark();
47 status_colors.refine(&user_theme.styles.status);
48
49 let mut syntax_colors = match user_theme.appearance {
50 Appearance::Light => SyntaxTheme::light(),
51 Appearance::Dark => SyntaxTheme::dark(),
52 };
53 if let Some(user_syntax) = user_theme.styles.syntax {
54 syntax_colors.highlights = user_syntax
55 .highlights
56 .iter()
57 .map(|(syntax_token, highlight)| {
58 (
59 syntax_token.clone(),
60 HighlightStyle {
61 color: highlight.color,
62 font_style: highlight.font_style.map(Into::into),
63 font_weight: highlight.font_weight.map(Into::into),
64 ..Default::default()
65 },
66 )
67 })
68 .collect::<Vec<_>>();
69 }
70
71 Theme {
72 id: uuid::Uuid::new_v4().to_string(),
73 name: user_theme.name.into(),
74 appearance: user_theme.appearance,
75 styles: ThemeStyles {
76 system: SystemColors::default(),
77 colors: theme_colors,
78 status: status_colors,
79 player: match user_theme.appearance {
80 Appearance::Light => PlayerColors::light(),
81 Appearance::Dark => PlayerColors::dark(),
82 },
83 syntax: Arc::new(syntax_colors),
84 },
85 }
86 }));
87 }
88
89 pub fn list_names(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
90 self.themes.keys().cloned()
91 }
92
93 pub fn list(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
94 self.themes.values().map(|theme| theme.name.clone())
95 }
96
97 pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
98 self.themes
99 .get(name)
100 .ok_or_else(|| anyhow!("theme not found: {}", name))
101 .cloned()
102 }
103
104 pub fn load_user_themes(&mut self) {
105 #[cfg(not(feature = "importing-themes"))]
106 self.insert_user_theme_familes(crate::all_user_themes());
107 }
108}
109
110impl Default for ThemeRegistry {
111 fn default() -> Self {
112 let mut this = Self {
113 themes: HashMap::default(),
114 };
115
116 this.insert_theme_families([one_family()]);
117
118 this
119 }
120}