theme: Don't log errors for missing themes until extensions have loaded (#25098)

This PR makes it so we don't log errors for missing themes or icon
themes until after the extensions have been loaded.

Currently, if you are using a theme that is defined in an extension it
is common to see one or more "theme not found" errors in the logs. This
is the result of us having to initialize the theme before the extensions
have actually finished loading.

This means that a theme that _may_ exist once extensions load is
considered non-existent before they have loaded.

To that end, we now wait until the extensions have loaded before we
start logging errors if we can't find the theme or icon theme.

Closes https://github.com/zed-industries/zed/issues/24539.

Release Notes:

- Reduced the number of "theme not found" and "icon theme not found"
errors in the logs for themes provided by extensions.
This commit is contained in:
Marshall Bowers 2025-02-18 12:47:25 -05:00 committed by GitHub
parent 1e255e41cc
commit c10ac31866
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 45 additions and 7 deletions

View File

@ -96,6 +96,8 @@ impl ExtensionHostProxy {
} }
pub trait ExtensionThemeProxy: Send + Sync + 'static { pub trait ExtensionThemeProxy: Send + Sync + 'static {
fn set_extensions_loaded(&self);
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>; fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>>;
fn remove_user_themes(&self, themes: Vec<SharedString>); fn remove_user_themes(&self, themes: Vec<SharedString>);
@ -123,6 +125,14 @@ pub trait ExtensionThemeProxy: Send + Sync + 'static {
} }
impl ExtensionThemeProxy for ExtensionHostProxy { impl ExtensionThemeProxy for ExtensionHostProxy {
fn set_extensions_loaded(&self) {
let Some(proxy) = self.theme_proxy.read().clone() else {
return;
};
proxy.set_extensions_loaded()
}
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> { fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
let Some(proxy) = self.theme_proxy.read().clone() else { let Some(proxy) = self.theme_proxy.read().clone() else {
return Task::ready(Ok(Vec::new())); return Task::ready(Ok(Vec::new()));

View File

@ -1290,6 +1290,7 @@ impl ExtensionStore {
} }
this.wasm_extensions.extend(wasm_extensions); this.wasm_extensions.extend(wasm_extensions);
this.proxy.set_extensions_loaded();
this.proxy.reload_current_theme(cx); this.proxy.reload_current_theme(cx);
this.proxy.reload_current_icon_theme(cx); this.proxy.reload_current_icon_theme(cx);
}) })

View File

@ -50,6 +50,8 @@ impl Global for GlobalThemeRegistry {}
struct ThemeRegistryState { struct ThemeRegistryState {
themes: HashMap<SharedString, Arc<Theme>>, themes: HashMap<SharedString, Arc<Theme>>,
icon_themes: HashMap<SharedString, Arc<IconTheme>>, icon_themes: HashMap<SharedString, Arc<IconTheme>>,
/// Whether the extensions have been loaded yet.
extensions_loaded: bool,
} }
/// The registry for themes. /// The registry for themes.
@ -82,6 +84,7 @@ impl ThemeRegistry {
state: RwLock::new(ThemeRegistryState { state: RwLock::new(ThemeRegistryState {
themes: HashMap::default(), themes: HashMap::default(),
icon_themes: HashMap::default(), icon_themes: HashMap::default(),
extensions_loaded: false,
}), }),
assets, assets,
}; };
@ -100,6 +103,16 @@ impl ThemeRegistry {
registry registry
} }
/// Returns whether the extensions have been loaded.
pub fn extensions_loaded(&self) -> bool {
self.state.read().extensions_loaded
}
/// Sets the flag indicating that the extensions have loaded.
pub fn set_extensions_loaded(&self) {
self.state.write().extensions_loaded = true;
}
fn insert_theme_families(&self, families: impl IntoIterator<Item = ThemeFamily>) { fn insert_theme_families(&self, families: impl IntoIterator<Item = ThemeFamily>) {
for family in families.into_iter() { for family in families.into_iter() {
self.insert_themes(family.themes); self.insert_themes(family.themes);

View File

@ -157,7 +157,11 @@ impl ThemeSettings {
// If the selected theme doesn't exist, fall back to a default theme // If the selected theme doesn't exist, fall back to a default theme
// based on the system appearance. // based on the system appearance.
let theme_registry = ThemeRegistry::global(cx); let theme_registry = ThemeRegistry::global(cx);
if theme_registry.get(theme_name).ok().is_none() { if let Err(err @ ThemeNotFoundError(_)) = theme_registry.get(theme_name) {
if theme_registry.extensions_loaded() {
log::error!("{err}");
}
theme_name = Self::default_theme(*system_appearance); theme_name = Self::default_theme(*system_appearance);
}; };
@ -180,11 +184,13 @@ impl ThemeSettings {
// If the selected icon theme doesn't exist, fall back to the default theme. // If the selected icon theme doesn't exist, fall back to the default theme.
let theme_registry = ThemeRegistry::global(cx); let theme_registry = ThemeRegistry::global(cx);
if theme_registry if let Err(err @ IconThemeNotFoundError(_)) =
.get_icon_theme(icon_theme_name) theme_registry.get_icon_theme(icon_theme_name)
.ok()
.is_none()
{ {
if theme_registry.extensions_loaded() {
log::error!("{err}");
}
icon_theme_name = DEFAULT_ICON_THEME_NAME; icon_theme_name = DEFAULT_ICON_THEME_NAME;
}; };
@ -848,10 +854,12 @@ impl settings::Settings for ThemeSettings {
this.active_theme = theme; this.active_theme = theme;
} }
Err(err @ ThemeNotFoundError(_)) => { Err(err @ ThemeNotFoundError(_)) => {
if themes.extensions_loaded() {
log::error!("{err}"); log::error!("{err}");
} }
} }
} }
}
this.theme_overrides.clone_from(&value.theme_overrides); this.theme_overrides.clone_from(&value.theme_overrides);
this.apply_theme_overrides(); this.apply_theme_overrides();
@ -866,10 +874,12 @@ impl settings::Settings for ThemeSettings {
this.active_icon_theme = icon_theme; this.active_icon_theme = icon_theme;
} }
Err(err @ IconThemeNotFoundError(_)) => { Err(err @ IconThemeNotFoundError(_)) => {
if themes.extensions_loaded() {
log::error!("{err}"); log::error!("{err}");
} }
} }
} }
}
merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into)); merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into));
this.ui_font_size = this.ui_font_size.clamp(px(6.), px(100.)); this.ui_font_size = this.ui_font_size.clamp(px(6.), px(100.));

View File

@ -24,6 +24,10 @@ struct ThemeRegistryProxy {
} }
impl ExtensionThemeProxy for ThemeRegistryProxy { impl ExtensionThemeProxy for ThemeRegistryProxy {
fn set_extensions_loaded(&self) {
self.theme_registry.set_extensions_loaded();
}
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> { fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
self.executor.spawn(async move { self.executor.spawn(async move {
let themes = theme::read_user_theme(&theme_path, fs).await?; let themes = theme::read_user_theme(&theme_path, fs).await?;