diff --git a/crates/extension_host/src/extension_host.rs b/crates/extension_host/src/extension_host.rs index 45fe5dc8ce375e..6bc6b0468b94fa 100644 --- a/crates/extension_host/src/extension_host.rs +++ b/crates/extension_host/src/extension_host.rs @@ -442,6 +442,18 @@ impl ExtensionStore { .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name)) } + /// Returns the path to the theme file within an extension, if there is an + /// extension that provides the theme. + pub fn path_to_extension_theme(&self, theme_name: &str) -> Option { + let entry = self.extension_index.themes.get(theme_name)?; + + Some( + self.extensions_dir() + .join(entry.extension.as_ref()) + .join(&entry.path), + ) + } + /// Returns the names of icon themes provided by extensions. pub fn extension_icon_themes<'a>( &'a self, @@ -459,6 +471,23 @@ impl ExtensionStore { }) } + /// Returns the path to the icon theme file within an extension, if there is + /// an extension that provides the icon theme. + pub fn path_to_extension_icon_theme( + &self, + icon_theme_name: &str, + ) -> Option<(PathBuf, PathBuf)> { + let entry = self.extension_index.icon_themes.get(icon_theme_name)?; + + let icon_theme_path = self + .extensions_dir() + .join(entry.extension.as_ref()) + .join(&entry.path); + let icons_root_path = self.extensions_dir().join(entry.extension.as_ref()); + + Some((icon_theme_path, icons_root_path)) + } + pub fn fetch_extensions( &self, search: Option<&str>, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 6ec65acb40188a..b56aceafe1ea54 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -15,6 +15,7 @@ use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE}; use editor::Editor; use env_logger::Builder; use extension::ExtensionHostProxy; +use extension_host::ExtensionStore; use fs::{Fs, RealFs}; use futures::{future, StreamExt}; use git::GitHostingProviderRegistry; @@ -44,7 +45,10 @@ use std::{ process, sync::Arc, }; -use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings}; +use theme::{ + ActiveTheme, IconThemeNotFoundError, SystemAppearance, ThemeNotFoundError, ThemeRegistry, + ThemeSettings, +}; use time::UtcOffset; use util::{maybe, ResultExt, TryFutureExt}; use uuid::Uuid; @@ -515,10 +519,10 @@ fn main() { zeta::init(cx); cx.observe_global::({ + let fs = fs.clone(); let languages = app_state.languages.clone(); let http = app_state.client.http_client(); let client = app_state.client.clone(); - move |cx| { for &mut window in cx.windows().iter_mut() { let background_appearance = cx.theme().window_background_appearance(); @@ -528,6 +532,9 @@ fn main() { }) .ok(); } + + eager_load_active_theme_and_icon_theme(fs.clone(), cx); + languages.set_theme(cx.theme().clone()); let new_host = &client::ClientSettings::get_global(cx).server_url; if &http.base_url() != new_host { @@ -1062,6 +1069,66 @@ fn load_embedded_fonts(cx: &App) { .unwrap(); } +/// Eagerly loads the active theme and icon theme based on the selections in the +/// theme settings. +/// +/// This fast path exists to load these themes as soon as possible so the user +/// doesn't see the default themes while waiting on extensions to load. +fn eager_load_active_theme_and_icon_theme(fs: Arc, cx: &App) { + let extension_store = ExtensionStore::global(cx); + let theme_registry = ThemeRegistry::global(cx); + let theme_settings = ThemeSettings::get_global(cx); + let appearance = cx.window_appearance().into(); + + if let Some(theme_selection) = theme_settings.theme_selection.as_ref() { + let theme_name = theme_selection.theme(appearance); + if matches!(theme_registry.get(theme_name), Err(ThemeNotFoundError(_))) { + if let Some(theme_path) = extension_store.read(cx).path_to_extension_theme(theme_name) { + cx.spawn({ + let theme_registry = theme_registry.clone(); + let fs = fs.clone(); + |cx| async move { + theme_registry.load_user_theme(&theme_path, fs).await?; + + cx.update(|cx| { + ThemeSettings::reload_current_theme(cx); + }) + } + }) + .detach_and_log_err(cx); + } + } + } + + if let Some(icon_theme_selection) = theme_settings.icon_theme_selection.as_ref() { + let icon_theme_name = icon_theme_selection.icon_theme(appearance); + if matches!( + theme_registry.get_icon_theme(icon_theme_name), + Err(IconThemeNotFoundError(_)) + ) { + if let Some((icon_theme_path, icons_root_path)) = extension_store + .read(cx) + .path_to_extension_icon_theme(icon_theme_name) + { + cx.spawn({ + let theme_registry = theme_registry.clone(); + let fs = fs.clone(); + |cx| async move { + theme_registry + .load_icon_theme(&icon_theme_path, &icons_root_path, fs) + .await?; + + cx.update(|cx| { + ThemeSettings::reload_current_icon_theme(cx); + }) + } + }) + .detach_and_log_err(cx); + } + } + } +} + /// Spawns a background task to load the user themes from the themes directory. fn load_user_themes_in_background(fs: Arc, cx: &mut App) { cx.spawn({