Detailed changes
@@ -17571,9 +17571,8 @@ dependencies = [
"anyhow",
"collections",
"derive_more",
- "fs",
- "futures 0.3.31",
"gpui",
+ "gpui_util",
"log",
"palette",
"parking_lot",
@@ -17585,7 +17584,6 @@ dependencies = [
"settings",
"strum 0.27.2",
"thiserror 2.0.17",
- "util",
"uuid",
]
@@ -18771,6 +18769,7 @@ dependencies = [
"documented",
"gpui",
"gpui_macros",
+ "gpui_util",
"icons",
"itertools 0.14.0",
"menu",
@@ -18782,7 +18781,6 @@ dependencies = [
"strum 0.27.2",
"theme",
"ui_macros",
- "util",
"windows 0.61.3",
]
@@ -9950,7 +9950,7 @@ impl Editor {
})
.when(!is_platform_style_mac, |parent| {
parent.child(
- Key::new(util::capitalize(keystroke.key()), Some(Color::Default))
+ Key::new(ui::utils::capitalize(keystroke.key()), Some(Color::Default))
.size(Some(IconSize::XSmall.rems().into())),
)
})
@@ -9978,7 +9978,7 @@ impl Editor {
)))
.into_any()
} else {
- Key::new(util::capitalize(keystroke.key()), Some(color))
+ Key::new(ui::utils::capitalize(keystroke.key()), Some(color))
.size(Some(IconSize::XSmall.rems().into()))
.into_any_element()
}
@@ -413,7 +413,7 @@ async fn test_themes(
) -> Result<()> {
for relative_theme_path in &manifest.themes {
let theme_path = extension_path.join(relative_theme_path);
- let theme_family = theme::read_user_theme(&theme_path, fs.clone()).await?;
+ let theme_family = theme::deserialize_user_theme(&fs.load_bytes(&theme_path).await?)?;
log::info!("loaded theme family {}", theme_family.name);
for theme in &theme_family.themes {
@@ -10,7 +10,7 @@ workspace = true
[features]
default = []
-test-support = ["gpui/test-support", "fs/test-support", "settings/test-support"]
+test-support = ["gpui/test-support", "settings/test-support"]
[lib]
path = "src/theme.rs"
@@ -20,9 +20,8 @@ doctest = false
anyhow.workspace = true
collections.workspace = true
derive_more.workspace = true
-fs.workspace = true
-futures.workspace = true
gpui.workspace = true
+gpui_util.workspace = true
log.workspace = true
palette = { workspace = true, default-features = false, features = ["std"] }
parking_lot.workspace = true
@@ -34,10 +33,8 @@ serde_json_lenient.workspace = true
settings.workspace = true
strum.workspace = true
thiserror.workspace = true
-util.workspace = true
uuid.workspace = true
[dev-dependencies]
-fs = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }
@@ -4,17 +4,15 @@ use std::{fmt::Debug, path::Path};
use anyhow::{Context as _, Result};
use collections::HashMap;
use derive_more::{Deref, DerefMut};
-use fs::Fs;
-use futures::StreamExt;
use gpui::{App, AssetSource, Global, SharedString};
+use gpui_util::ResultExt;
use parking_lot::RwLock;
use thiserror::Error;
-use util::ResultExt;
use crate::{
Appearance, AppearanceContent, ChevronIcons, DEFAULT_ICON_THEME_NAME, DirectoryIcons,
- IconDefinition, IconTheme, Theme, ThemeFamily, ThemeFamilyContent, default_icon_theme,
- read_icon_theme, read_user_theme, refine_theme_family,
+ IconDefinition, IconTheme, IconThemeFamilyContent, Theme, ThemeFamily, ThemeFamilyContent,
+ default_icon_theme, deserialize_user_theme, refine_theme_family,
};
/// The metadata for a theme.
@@ -208,29 +206,9 @@ impl ThemeRegistry {
}
}
- /// 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<dyn Fs>) -> Result<()> {
- let mut theme_paths = fs
- .read_dir(themes_path)
- .await
- .with_context(|| format!("reading themes from {themes_path:?}"))?;
-
- while let Some(theme_path) = theme_paths.next().await {
- let Some(theme_path) = theme_path.log_err() else {
- continue;
- };
-
- self.load_user_theme(&theme_path, fs.clone())
- .await
- .log_err();
- }
-
- Ok(())
- }
-
- /// 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<dyn Fs>) -> Result<()> {
- let theme = read_user_theme(theme_path, fs).await?;
+ /// Loads the user theme from the specified data and adds it to the registry.
+ pub fn load_user_theme(&self, bytes: &[u8]) -> Result<()> {
+ let theme = deserialize_user_theme(bytes)?;
self.insert_user_theme_families([theme]);
@@ -273,18 +251,15 @@ impl ThemeRegistry {
.retain(|name, _| !icon_themes_to_remove.contains(name))
}
- /// Loads the icon theme from the specified path and adds it to the registry.
+ /// Loads the icon theme from the icon theme family and adds it to the registry.
///
/// The `icons_root_dir` parameter indicates the root directory from which
/// the relative paths to icons in the theme should be resolved against.
- pub async fn load_icon_theme(
+ pub fn load_icon_theme(
&self,
- icon_theme_path: &Path,
+ icon_theme_family: IconThemeFamilyContent,
icons_root_dir: &Path,
- fs: Arc<dyn Fs>,
) -> Result<()> {
- let icon_theme_family = read_icon_theme(icon_theme_path, fs).await?;
-
let resolve_icon_path = |path: SharedString| {
icons_root_dir
.join(path.as_ref())
@@ -19,7 +19,6 @@ mod schema;
mod settings;
mod styles;
-use std::path::Path;
use std::sync::Arc;
use ::settings::DEFAULT_DARK_THEME;
@@ -28,7 +27,6 @@ use ::settings::Settings;
use ::settings::SettingsStore;
use anyhow::Result;
use fallback_themes::apply_status_color_defaults;
-use fs::Fs;
use gpui::BorrowAppContext;
use gpui::Global;
use gpui::{
@@ -405,10 +403,9 @@ impl Theme {
}
}
-/// Asynchronously reads the user theme from the specified path.
-pub async fn read_user_theme(theme_path: &Path, fs: Arc<dyn Fs>) -> Result<ThemeFamilyContent> {
- let bytes = fs.load_bytes(theme_path).await?;
- let theme_family: ThemeFamilyContent = serde_json_lenient::from_slice(&bytes)?;
+/// Deserializes a user theme from the given bytes.
+pub fn deserialize_user_theme(bytes: &[u8]) -> Result<ThemeFamilyContent> {
+ let theme_family: ThemeFamilyContent = serde_json_lenient::from_slice(bytes)?;
for theme in &theme_family.themes {
if theme
@@ -427,13 +424,9 @@ pub async fn read_user_theme(theme_path: &Path, fs: Arc<dyn Fs>) -> Result<Theme
Ok(theme_family)
}
-/// Asynchronously reads the icon theme from the specified path.
-pub async fn read_icon_theme(
- icon_theme_path: &Path,
- fs: Arc<dyn Fs>,
-) -> Result<IconThemeFamilyContent> {
- let bytes = fs.load_bytes(icon_theme_path).await?;
- let icon_theme_family: IconThemeFamilyContent = serde_json_lenient::from_slice(&bytes)?;
+/// Deserializes a icon theme from the given bytes.
+pub fn deserialize_icon_theme(bytes: &[u8]) -> Result<IconThemeFamilyContent> {
+ let icon_theme_family: IconThemeFamilyContent = serde_json_lenient::from_slice(bytes)?;
Ok(icon_theme_family)
}
@@ -5,7 +5,7 @@ use anyhow::Result;
use extension::{ExtensionHostProxy, ExtensionThemeProxy};
use fs::Fs;
use gpui::{App, BackgroundExecutor, SharedString, Task};
-use theme::{GlobalTheme, ThemeRegistry};
+use theme::{GlobalTheme, ThemeRegistry, deserialize_icon_theme};
pub fn init(
extension_host_proxy: Arc<ExtensionHostProxy>,
@@ -30,7 +30,7 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
fn list_theme_names(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<Vec<String>>> {
self.executor.spawn(async move {
- let themes = theme::read_user_theme(&theme_path, fs).await?;
+ let themes = theme::deserialize_user_theme(&fs.load_bytes(&theme_path).await?)?;
Ok(themes.themes.into_iter().map(|theme| theme.name).collect())
})
}
@@ -41,8 +41,9 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
fn load_user_theme(&self, theme_path: PathBuf, fs: Arc<dyn Fs>) -> Task<Result<()>> {
let theme_registry = self.theme_registry.clone();
- self.executor
- .spawn(async move { theme_registry.load_user_theme(&theme_path, fs).await })
+ self.executor.spawn(async move {
+ theme_registry.load_user_theme(&fs.load_bytes(&theme_path).await?)
+ })
}
fn reload_current_theme(&self, cx: &mut App) {
@@ -55,7 +56,8 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
fs: Arc<dyn Fs>,
) -> Task<Result<Vec<String>>> {
self.executor.spawn(async move {
- let icon_theme_family = theme::read_icon_theme(&icon_theme_path, fs).await?;
+ let icon_theme_family =
+ theme::deserialize_icon_theme(&fs.load_bytes(&icon_theme_path).await?)?;
Ok(icon_theme_family
.themes
.into_iter()
@@ -76,9 +78,9 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
) -> Task<Result<()>> {
let theme_registry = self.theme_registry.clone();
self.executor.spawn(async move {
- theme_registry
- .load_icon_theme(&icon_theme_path, &icons_root_dir, fs)
- .await
+ let icon_theme_family =
+ deserialize_icon_theme(&fs.load_bytes(&icon_theme_path).await?)?;
+ theme_registry.load_icon_theme(icon_theme_family, &icons_root_dir)
})
}
@@ -29,7 +29,7 @@ story = { workspace = true, optional = true }
strum.workspace = true
theme.workspace = true
ui_macros.workspace = true
-util.workspace = true
+gpui_util.workspace = true
[target.'cfg(windows)'.dependencies]
windows.workspace = true
@@ -1,6 +1,7 @@
use std::rc::Rc;
use crate::PlatformStyle;
+use crate::utils::capitalize;
use crate::{Icon, IconName, IconSize, h_flex, prelude::*};
use gpui::{
Action, AnyElement, App, FocusHandle, Global, IntoElement, KeybindingKeystroke, Keystroke,
@@ -142,7 +143,7 @@ fn render_key(
match key_icon {
Some(icon) => KeyIcon::new(icon, color).size(size).into_any_element(),
None => {
- let key = util::capitalize(key);
+ let key = capitalize(key);
Key::new(&key, color).size(size).into_any_element()
}
}
@@ -546,7 +547,7 @@ fn keystroke_text(
let key = match key {
"pageup" => "PageUp",
"pagedown" => "PageDown",
- key => &util::capitalize(key),
+ key => &capitalize(key),
};
text.push_str(key);
}
@@ -15,10 +15,10 @@ use gpui::{
UniformListScrollHandle, Window, ease_in_out, prelude::FluentBuilder as _, px, quad, relative,
size,
};
+use gpui_util::ResultExt;
use settings::SettingsStore;
use smallvec::SmallVec;
use theme::ActiveTheme as _;
-use util::ResultExt;
use std::ops::Range;
@@ -34,3 +34,25 @@ pub fn reveal_in_file_manager_label(is_remote: bool) -> &'static str {
"Reveal in File Manager"
}
}
+
+/// Capitalizes the first character of a string.
+///
+/// This function takes a string slice as input and returns a new `String` with the first character
+/// capitalized.
+///
+/// # Examples
+///
+/// ```
+/// use ui::utils::capitalize;
+///
+/// assert_eq!(capitalize("hello"), "Hello");
+/// assert_eq!(capitalize("WORLD"), "WORLD");
+/// assert_eq!(capitalize(""), "");
+/// ```
+pub fn capitalize(str: &str) -> String {
+ let mut chars = str.chars();
+ match chars.next() {
+ None => String::new(),
+ Some(first_char) => first_char.to_uppercase().collect::<String>() + chars.as_str(),
+ }
+}
@@ -686,28 +686,6 @@ impl PartialOrd for NumericPrefixWithSuffix<'_> {
}
}
-/// Capitalizes the first character of a string.
-///
-/// This function takes a string slice as input and returns a new `String` with the first character
-/// capitalized.
-///
-/// # Examples
-///
-/// ```
-/// use util::capitalize;
-///
-/// assert_eq!(capitalize("hello"), "Hello");
-/// assert_eq!(capitalize("WORLD"), "WORLD");
-/// assert_eq!(capitalize(""), "");
-/// ```
-pub fn capitalize(str: &str) -> String {
- let mut chars = str.chars();
- match chars.next() {
- None => String::new(),
- Some(first_char) => first_char.to_uppercase().collect::<String>() + chars.as_str(),
- }
-}
-
fn emoji_regex() -> &'static Regex {
static EMOJI_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new("(\\p{Emoji}|\u{200D})").unwrap());
@@ -1781,7 +1781,23 @@ fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut App) {
})?;
}
}
- theme_registry.load_user_themes(themes_dir, fs).await?;
+
+ let mut theme_paths = fs
+ .read_dir(themes_dir)
+ .await
+ .with_context(|| format!("reading themes from {themes_dir:?}"))?;
+
+ while let Some(theme_path) = theme_paths.next().await {
+ let Some(theme_path) = theme_path.log_err() else {
+ continue;
+ };
+ let Some(bytes) = fs.load_bytes(&theme_path).await.log_err() else {
+ continue;
+ };
+
+ theme_registry.load_user_theme(&bytes).log_err();
+ }
+
cx.update(GlobalTheme::reload_theme);
anyhow::Ok(())
}
@@ -1801,11 +1817,8 @@ fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut App) {
for event in paths {
if fs.metadata(&event.path).await.ok().flatten().is_some() {
let theme_registry = cx.update(|cx| ThemeRegistry::global(cx));
- if theme_registry
- .load_user_theme(&event.path, fs.clone())
- .await
- .log_err()
- .is_some()
+ if let Some(bytes) = fs.load_bytes(&event.path).await.log_err()
+ && theme_registry.load_user_theme(&bytes).log_err().is_some()
{
cx.update(GlobalTheme::reload_theme);
}
@@ -77,7 +77,10 @@ use std::{
sync::atomic::{self, AtomicBool},
};
use terminal_view::terminal_panel::{self, TerminalPanel};
-use theme::{ActiveTheme, GlobalTheme, SystemAppearance, ThemeRegistry, ThemeSettings};
+use theme::{
+ ActiveTheme, GlobalTheme, SystemAppearance, ThemeRegistry, ThemeSettings,
+ deserialize_icon_theme,
+};
use ui::{PopoverMenuHandle, prelude::*};
use util::markdown::MarkdownString;
use util::rel_path::RelPath;
@@ -2221,24 +2224,23 @@ pub(crate) fn eager_load_active_theme_and_icon_theme(fs: Arc<dyn Fs>, cx: &mut A
let reload_tasks = &reload_tasks;
let fs = fs.clone();
- scope.spawn(async {
+ scope.spawn(async move {
match load_target {
LoadTarget::Theme(theme_path) => {
- if theme_registry
- .load_user_theme(&theme_path, fs)
- .await
- .log_err()
- .is_some()
+ if let Some(bytes) = fs.load_bytes(&theme_path).await.log_err()
+ && theme_registry.load_user_theme(&bytes).log_err().is_some()
{
reload_tasks.lock().push(ReloadTarget::Theme);
}
}
LoadTarget::IconTheme((icon_theme_path, icons_root_path)) => {
- if theme_registry
- .load_icon_theme(&icon_theme_path, &icons_root_path, fs)
- .await
- .log_err()
- .is_some()
+ if let Some(bytes) = fs.load_bytes(&icon_theme_path).await.log_err()
+ && let Some(icon_theme_family) =
+ deserialize_icon_theme(&bytes).log_err()
+ && theme_registry
+ .load_icon_theme(icon_theme_family, &icons_root_path)
+ .log_err()
+ .is_some()
{
reload_tasks.lock().push(ReloadTarget::IconTheme);
}