diff --git a/Cargo.lock b/Cargo.lock index 6129d42f31f2d12a37a7d4a226846f6b0067ef4e..836584de8a6e00e55de9441563e218b32c87ce40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12098,7 +12098,6 @@ name = "theme" version = "0.1.0" dependencies = [ "anyhow", - "async-trait", "collections", "derive_more", "fs", diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index e53f8eeb75697795516c652ae24874876078d130..b27a486b4bd37b831fd39ad513d0ac7ffac5d139 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -112,7 +112,7 @@ pub struct ExtensionStore { outstanding_operations: BTreeMap, ExtensionOperation>, index_path: PathBuf, language_registry: Arc, - theme_registry: Arc, + theme_registry: Arc, slash_command_registry: Arc, indexed_docs_registry: Arc, snippet_registry: Arc, @@ -177,7 +177,7 @@ pub fn init( client: Arc, node_runtime: NodeRuntime, language_registry: Arc, - theme_registry: Arc, + theme_registry: Arc, cx: &mut AppContext, ) { ExtensionSettings::register(cx); @@ -228,7 +228,7 @@ impl ExtensionStore { telemetry: Option>, node_runtime: NodeRuntime, language_registry: Arc, - theme_registry: Arc, + theme_registry: Arc, slash_command_registry: Arc, indexed_docs_registry: Arc, snippet_registry: Arc, diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index 194625883c3733a5341a9e03b6bfae54ff743808..bd571703dba7f63848defc042662b2f62a4c0f1a 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/crates/extension_host/src/extension_store_test.rs @@ -27,7 +27,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use theme::{RealThemeRegistry, ThemeRegistry}; +use theme::ThemeRegistry; use util::test::temp_tree; #[cfg(test)] @@ -260,7 +260,7 @@ async fn test_extension_store(cx: &mut TestAppContext) { }; let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); - let theme_registry = Arc::new(RealThemeRegistry::new(Box::new(()))); + let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); let slash_command_registry = SlashCommandRegistry::new(); let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor())); let snippet_registry = Arc::new(SnippetRegistry::new()); @@ -486,7 +486,7 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { let project = Project::test(fs.clone(), [project_dir.as_path()], cx).await; let language_registry = project.read_with(cx, |project, _cx| project.languages().clone()); - let theme_registry = Arc::new(RealThemeRegistry::new(Box::new(()))); + let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); let slash_command_registry = SlashCommandRegistry::new(); let indexed_docs_registry = Arc::new(IndexedDocsRegistry::new(cx.executor())); let snippet_registry = Arc::new(SnippetRegistry::new()); diff --git a/crates/settings_ui/src/appearance_settings_controls.rs b/crates/settings_ui/src/appearance_settings_controls.rs index 70e8e78cb4956316675fd0de9270f548e58b0293..39bfda081671b29eca1a5257289df76b2210fdb2 100644 --- a/crates/settings_ui/src/appearance_settings_controls.rs +++ b/crates/settings_ui/src/appearance_settings_controls.rs @@ -83,7 +83,7 @@ impl RenderOnce for ThemeControl { "theme", value.clone(), ContextMenu::build(cx, |mut menu, cx| { - let theme_registry = ::global(cx); + let theme_registry = ThemeRegistry::global(cx); for theme in theme_registry.list_names() { menu = menu.custom_entry( diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs index d751a5ed03b5c0a16705304b47bcfae6b132dca5..9fe61a70c4d818a1176c0c3491f4deec519bd29d 100644 --- a/crates/storybook/src/storybook.rs +++ b/crates/storybook/src/storybook.rs @@ -76,7 +76,7 @@ fn main() { let selector = story_selector; - let theme_registry = ::global(cx); + let theme_registry = ThemeRegistry::global(cx); let mut theme_settings = ThemeSettings::get_global(cx).clone(); theme_settings.active_theme = theme_registry.get(&theme_name).unwrap(); ThemeSettings::override_global(theme_settings, cx); diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index b4fe3967931f0459e6e0134ebca2dd3dbd0654b4..c3e3a197cbf9116aadc22e7e131a65dbc7893d29 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -18,7 +18,6 @@ doctest = false [dependencies] anyhow.workspace = true -async-trait.workspace = true collections.workspace = true derive_more.workspace = true fs.workspace = true diff --git a/crates/theme/src/registry.rs b/crates/theme/src/registry.rs index a78a22b08fbdb4e651904c5bfba3e593ca696671..a511fb9da35789b951e643922176d2166b774774 100644 --- a/crates/theme/src/registry.rs +++ b/crates/theme/src/registry.rs @@ -1,8 +1,7 @@ use std::sync::Arc; use std::{fmt::Debug, path::Path}; -use anyhow::{anyhow, bail, Context, Result}; -use async_trait::async_trait; +use anyhow::{anyhow, Context, Result}; use collections::HashMap; use derive_more::{Deref, DerefMut}; use fs::Fs; @@ -30,34 +29,22 @@ pub struct ThemeMeta { /// inserting the [`ThemeRegistry`] into the context as a global. /// /// This should not be exposed outside of this module. -#[derive(Deref, DerefMut)] -struct GlobalThemeRegistry(Arc); +#[derive(Default, Deref, DerefMut)] +struct GlobalThemeRegistry(Arc); impl Global for GlobalThemeRegistry {} -/// A registry for themes. -#[async_trait] -pub trait ThemeRegistry: Send + Sync + 'static { - /// Returns the names of all themes in the registry. - fn list_names(&self) -> Vec; - - /// Returns the metadata of all themes in the registry. - fn list(&self) -> Vec; - - /// Returns the theme with the given name. - fn get(&self, name: &str) -> Result>; - - /// Loads the user theme from the specified path and adds it to the registry. - async fn load_user_theme(&self, theme_path: &Path, fs: Arc) -> Result<()>; - - /// Loads the user themes from the specified directory and adds them to the registry. - async fn load_user_themes(&self, themes_path: &Path, fs: Arc) -> Result<()>; +struct ThemeRegistryState { + themes: HashMap>, +} - /// Removes the themes with the given names from the registry. - fn remove_user_themes(&self, themes_to_remove: &[SharedString]); +/// The registry for themes. +pub struct ThemeRegistry { + state: RwLock, + assets: Box, } -impl dyn ThemeRegistry { +impl ThemeRegistry { /// Returns the global [`ThemeRegistry`]. pub fn global(cx: &AppContext) -> Arc { cx.global::().0.clone() @@ -67,37 +54,18 @@ impl dyn ThemeRegistry { /// /// Inserts a default [`ThemeRegistry`] if one does not yet exist. pub fn default_global(cx: &mut AppContext) -> Arc { - if let Some(registry) = cx.try_global::() { - return registry.0.clone(); - } - - let registry = Arc::new(RealThemeRegistry::default()); - cx.set_global(GlobalThemeRegistry(registry.clone())); - - registry + cx.default_global::().0.clone() } -} - -struct RealThemeRegistryState { - themes: HashMap>, -} -/// The registry for themes. -pub struct RealThemeRegistry { - state: RwLock, - assets: Box, -} - -impl RealThemeRegistry { /// Sets the global [`ThemeRegistry`]. - pub(crate) fn set_global(self: Arc, cx: &mut AppContext) { - cx.set_global(GlobalThemeRegistry(self)); + pub(crate) fn set_global(assets: Box, cx: &mut AppContext) { + cx.set_global(GlobalThemeRegistry(Arc::new(ThemeRegistry::new(assets)))); } /// Creates a new [`ThemeRegistry`] with the given [`AssetSource`]. pub fn new(assets: Box) -> Self { let registry = Self { - state: RwLock::new(RealThemeRegistryState { + state: RwLock::new(ThemeRegistryState { themes: HashMap::default(), }), assets, @@ -132,52 +100,28 @@ impl RealThemeRegistry { } } + /// Removes the themes with the given names from the registry. + pub fn remove_user_themes(&self, themes_to_remove: &[SharedString]) { + self.state + .write() + .themes + .retain(|name, _| !themes_to_remove.contains(name)) + } + /// Removes all themes from the registry. pub fn clear(&self) { self.state.write().themes.clear(); } - /// Loads the themes bundled with the Zed binary and adds them to the registry. - pub fn load_bundled_themes(&self) { - let theme_paths = self - .assets - .list("themes/") - .expect("failed to list theme assets") - .into_iter() - .filter(|path| path.ends_with(".json")); - - for path in theme_paths { - let Some(theme) = self.assets.load(&path).log_err().flatten() else { - continue; - }; - - let Some(theme_family) = serde_json::from_slice(&theme) - .with_context(|| format!("failed to parse theme at path \"{path}\"")) - .log_err() - else { - continue; - }; - - self.insert_user_theme_families([theme_family]); - } - } -} - -impl Default for RealThemeRegistry { - fn default() -> Self { - Self::new(Box::new(())) - } -} - -#[async_trait] -impl ThemeRegistry for RealThemeRegistry { - fn list_names(&self) -> Vec { + /// Returns the names of all themes in the registry. + pub fn list_names(&self) -> Vec { let mut names = self.state.read().themes.keys().cloned().collect::>(); names.sort(); names } - fn list(&self) -> Vec { + /// Returns the metadata of all themes in the registry. + pub fn list(&self) -> Vec { self.state .read() .themes @@ -189,7 +133,8 @@ impl ThemeRegistry for RealThemeRegistry { .collect() } - fn get(&self, name: &str) -> Result> { + /// Returns the theme with the given name. + pub fn get(&self, name: &str) -> Result> { self.state .read() .themes @@ -198,15 +143,33 @@ impl ThemeRegistry for RealThemeRegistry { .cloned() } - async fn load_user_theme(&self, theme_path: &Path, fs: Arc) -> Result<()> { - let theme = read_user_theme(theme_path, fs).await?; + /// Loads the themes bundled with the Zed binary and adds them to the registry. + pub fn load_bundled_themes(&self) { + let theme_paths = self + .assets + .list("themes/") + .expect("failed to list theme assets") + .into_iter() + .filter(|path| path.ends_with(".json")); - self.insert_user_theme_families([theme]); + for path in theme_paths { + let Some(theme) = self.assets.load(&path).log_err().flatten() else { + continue; + }; - Ok(()) + let Some(theme_family) = serde_json::from_slice(&theme) + .with_context(|| format!("failed to parse theme at path \"{path}\"")) + .log_err() + else { + continue; + }; + + self.insert_user_theme_families([theme_family]); + } } - async fn load_user_themes(&self, themes_path: &Path, fs: Arc) -> Result<()> { + /// Loads the user themes from the specified directory and adds them to the registry. + pub async fn load_user_themes(&self, themes_path: &Path, fs: Arc) -> Result<()> { let mut theme_paths = fs .read_dir(themes_path) .await @@ -225,38 +188,18 @@ impl ThemeRegistry for RealThemeRegistry { Ok(()) } - fn remove_user_themes(&self, themes_to_remove: &[SharedString]) { - self.state - .write() - .themes - .retain(|name, _| !themes_to_remove.contains(name)) - } -} - -/// A theme registry that doesn't have any behavior. -pub struct VoidThemeRegistry; - -#[async_trait] -impl ThemeRegistry for VoidThemeRegistry { - fn list_names(&self) -> Vec { - Vec::new() - } - - fn list(&self) -> Vec { - Vec::new() - } + /// Loads the user theme from the specified path and adds it to the registry. + pub async fn load_user_theme(&self, theme_path: &Path, fs: Arc) -> Result<()> { + let theme = read_user_theme(theme_path, fs).await?; - fn get(&self, name: &str) -> Result> { - bail!("cannot retrieve theme {name:?} from a void theme registry") - } + self.insert_user_theme_families([theme]); - async fn load_user_theme(&self, _theme_path: &Path, _fs: Arc) -> Result<()> { Ok(()) } +} - async fn load_user_themes(&self, _themes_path: &Path, _fs: Arc) -> Result<()> { - Ok(()) +impl Default for ThemeRegistry { + fn default() -> Self { + Self::new(Box::new(())) } - - fn remove_user_themes(&self, _themes_to_remove: &[SharedString]) {} } diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 81e1958029421f5d89811f773fd69be75918e469..cebfea84bc4e0d70cc8cf6c00294b2c0aa0cc29e 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -150,7 +150,7 @@ impl ThemeSettings { // If the selected theme doesn't exist, fall back to a default theme // based on the system appearance. - let theme_registry = ::global(cx); + let theme_registry = ThemeRegistry::global(cx); if theme_registry.get(theme_name).ok().is_none() { theme_name = Self::default_theme(*system_appearance); }; @@ -446,7 +446,7 @@ impl ThemeSettings { /// Returns a `Some` containing the new theme if it was successful. /// Returns `None` otherwise. pub fn switch_theme(&mut self, theme: &str, cx: &mut AppContext) -> Option> { - let themes = ::default_global(cx); + let themes = ThemeRegistry::default_global(cx); let mut new_theme = None; @@ -598,7 +598,7 @@ impl settings::Settings for ThemeSettings { type FileContent = ThemeSettingsContent; fn load(sources: SettingsSources, cx: &mut AppContext) -> Result { - let themes = ::default_global(cx); + let themes = ThemeRegistry::default_global(cx); let system_appearance = SystemAppearance::default_global(cx); let defaults = sources.default; @@ -710,7 +710,7 @@ impl settings::Settings for ThemeSettings { cx: &AppContext, ) -> schemars::schema::RootSchema { let mut root_schema = generator.root_schema_for::(); - let theme_names = ::global(cx) + let theme_names = ThemeRegistry::global(cx) .list_names() .into_iter() .map(|theme_name| Value::String(theme_name.to_string())) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 103ad684860c95c1aaba1f60fc3773184cf126d9..e0d4fb42440e2bb3260e4871507e517ed680def6 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -88,11 +88,10 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) { LoadThemes::JustBase => (Box::new(()) as Box, false), LoadThemes::All(assets) => (assets, true), }; - let registry = Arc::new(RealThemeRegistry::new(assets)); - registry.clone().set_global(cx); + ThemeRegistry::set_global(assets, cx); if load_user_themes { - registry.load_bundled_themes(); + ThemeRegistry::global(cx).load_bundled_themes(); } ThemeSettings::register(cx); diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 781a930607b856d572e095bc60b703bc8a389c9c..d0763c2793b9681f46fc167f660792adbfa6993e 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -95,7 +95,7 @@ impl ThemeSelectorDelegate { ) -> Self { let original_theme = cx.theme().clone(); - let registry = ::global(cx); + let registry = ThemeRegistry::global(cx); let mut themes = registry .list() .into_iter() @@ -140,7 +140,7 @@ impl ThemeSelectorDelegate { fn show_selected_theme(&mut self, cx: &mut ViewContext>) { if let Some(mat) = self.matches.get(self.selected_index) { - let registry = ::global(cx); + let registry = ThemeRegistry::global(cx); match registry.get(&mat.string) { Ok(theme) => { Self::set_theme(theme, cx); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 863f9664303c08b0914f02666c3818ccf79afc07..464b7ac706e20d2e5609354defb0463bd7b84b48 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -407,7 +407,7 @@ fn main() { app_state.client.clone(), app_state.node_runtime.clone(), app_state.languages.clone(), - ::global(cx), + ThemeRegistry::global(cx), cx, ); recent_projects::init(cx); @@ -1160,9 +1160,8 @@ fn load_user_themes_in_background(fs: Arc, cx: &mut AppContext) { cx.spawn({ let fs = fs.clone(); |cx| async move { - if let Some(theme_registry) = cx - .update(|cx| ::global(cx).clone()) - .log_err() + if let Some(theme_registry) = + cx.update(|cx| ThemeRegistry::global(cx).clone()).log_err() { let themes_dir = paths::themes_dir().as_ref(); match fs @@ -1201,9 +1200,8 @@ fn watch_themes(fs: Arc, cx: &mut AppContext) { while let Some(paths) = events.next().await { for event in paths { if fs.metadata(&event.path).await.ok().flatten().is_some() { - if let Some(theme_registry) = cx - .update(|cx| ::global(cx).clone()) - .log_err() + if let Some(theme_registry) = + cx.update(|cx| ThemeRegistry::global(cx).clone()).log_err() { if let Some(()) = theme_registry .load_user_theme(&event.path, fs.clone()) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 175497ffd79ffb917d9e943307770e0d401f76aa..427e3ab9f63aca989c10801258d1e96f0615530b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1139,7 +1139,7 @@ mod tests { path::{Path, PathBuf}, time::Duration, }; - use theme::{RealThemeRegistry, ThemeRegistry, ThemeSettings}; + use theme::{ThemeRegistry, ThemeSettings}; use workspace::{ item::{Item, ItemHandle}, open_new, open_paths, pane, NewFile, OpenVisible, SaveIntent, SplitDirection, @@ -3419,7 +3419,7 @@ mod tests { .unwrap(), ]) .unwrap(); - let themes = RealThemeRegistry::default(); + let themes = ThemeRegistry::default(); settings::init(cx); theme::init(theme::LoadThemes::JustBase, cx);