diff --git a/Cargo.lock b/Cargo.lock index 6129d42f31..836584de8a 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 e53f8eeb75..b27a486b4b 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 194625883c..bd571703db 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 70e8e78cb4..39bfda0816 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 d751a5ed03..9fe61a70c4 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 b4fe396793..c3e3a197cb 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 a78a22b08f..a511fb9da3 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<()>; - - /// Removes the themes with the given names from the registry. - fn remove_user_themes(&self, themes_to_remove: &[SharedString]); +struct ThemeRegistryState { + themes: HashMap>, } -impl dyn ThemeRegistry { +/// The registry for themes. +pub struct ThemeRegistry { + state: RwLock, + assets: Box, +} + +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,11 +100,49 @@ 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(); } + /// 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 + } + + /// Returns the metadata of all themes in the registry. + pub fn list(&self) -> Vec { + self.state + .read() + .themes + .values() + .map(|theme| ThemeMeta { + name: theme.name.clone(), + appearance: theme.appearance(), + }) + .collect() + } + + /// Returns the theme with the given name. + pub fn get(&self, name: &str) -> Result> { + self.state + .read() + .themes + .get(name) + .ok_or_else(|| anyhow!("theme not found: {}", name)) + .cloned() + } + /// Loads the themes bundled with the Zed binary and adds them to the registry. pub fn load_bundled_themes(&self) { let theme_paths = self @@ -161,52 +167,9 @@ impl RealThemeRegistry { 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 { - let mut names = self.state.read().themes.keys().cloned().collect::>(); - names.sort(); - names - } - - fn list(&self) -> Vec { - self.state - .read() - .themes - .values() - .map(|theme| ThemeMeta { - name: theme.name.clone(), - appearance: theme.appearance(), - }) - .collect() - } - - fn get(&self, name: &str) -> Result> { - self.state - .read() - .themes - .get(name) - .ok_or_else(|| anyhow!("theme not found: {}", name)) - .cloned() - } - - async fn load_user_theme(&self, theme_path: &Path, fs: Arc) -> Result<()> { - let theme = read_user_theme(theme_path, fs).await?; - - self.insert_user_theme_families([theme]); - - Ok(()) - } - - 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)) + /// 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?; + + self.insert_user_theme_families([theme]); + + Ok(()) } } -/// 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() +impl Default for ThemeRegistry { + fn default() -> Self { + Self::new(Box::new(())) } - - fn list(&self) -> Vec { - Vec::new() - } - - fn get(&self, name: &str) -> Result> { - bail!("cannot retrieve theme {name:?} from a void theme registry") - } - - 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(()) - } - - fn remove_user_themes(&self, _themes_to_remove: &[SharedString]) {} } diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 81e1958029..cebfea84bc 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 103ad68486..e0d4fb4244 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 781a930607..d0763c2793 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 863f966430..464b7ac706 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 175497ffd7..427e3ab9f6 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);