crates/theme/src/themes/LICENSES → assets/themes/LICENSES 🔗
Marshall Bowers created
This PR changes the theme loading to use the JSON themes bundled with
the binary rather then the Rust theme definitions.
### Performance
I profiled this using `cargo run --release` to see what the speed
differences would be now that we're deserializing JSON:
**Before:** `ThemeRegistry::load_user_themes` took 16.656666ms
**After:** `ThemeRegistry::load_user_themes` took 18.784875ms
It's slightly slower, but not by much. There is probably some work we
could do here to bring down the theme loading time in general.
Release Notes:
- N/A
assets/themes/LICENSES | 0
crates/storybook/src/storybook.rs | 2
crates/theme/src/registry.rs | 127 +++++++++++++++++++++++---------
crates/theme/src/schema.rs | 30 +++++++
crates/theme/src/theme.rs | 18 ++-
crates/zed/src/main.rs | 2
script/generate-licenses | 2
7 files changed, 133 insertions(+), 48 deletions(-)
@@ -69,7 +69,7 @@ fn main() {
.unwrap();
cx.set_global(store);
- theme::init(theme::LoadThemes::All, cx);
+ theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
let selector = story_selector;
@@ -2,12 +2,13 @@ use std::collections::HashMap;
use std::sync::Arc;
use anyhow::{anyhow, Result};
-use gpui::{HighlightStyle, SharedString};
+use gpui::{AssetSource, HighlightStyle, SharedString};
use refineable::Refineable;
use crate::{
- Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme, ThemeColors,
- ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
+ try_parse_color, Appearance, AppearanceContent, PlayerColor, PlayerColors, StatusColors,
+ SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeContent, ThemeFamily, ThemeFamilyContent,
+ ThemeStyles,
};
#[derive(Debug, Clone)]
@@ -17,10 +18,27 @@ pub struct ThemeMeta {
}
pub struct ThemeRegistry {
+ assets: Box<dyn AssetSource>,
themes: HashMap<SharedString, Arc<Theme>>,
}
impl ThemeRegistry {
+ pub fn new(assets: Box<dyn AssetSource>) -> Self {
+ let mut registry = Self {
+ assets,
+ themes: HashMap::new(),
+ };
+
+ // We're loading our new versions of the One themes by default, as
+ // we need them to be loaded for tests.
+ //
+ // These themes will get overwritten when `load_user_themes` is called
+ // when Zed starts, so the One variants used will be the ones ported from Zed1.
+ registry.insert_theme_families([crate::one_themes::one_family()]);
+
+ registry
+ }
+
fn insert_theme_families(&mut self, families: impl IntoIterator<Item = ThemeFamily>) {
for family in families.into_iter() {
self.insert_themes(family.themes);
@@ -34,48 +52,78 @@ impl ThemeRegistry {
}
#[allow(unused)]
- fn insert_user_theme_families(&mut self, families: impl IntoIterator<Item = UserThemeFamily>) {
+ fn insert_user_theme_families(
+ &mut self,
+ families: impl IntoIterator<Item = ThemeFamilyContent>,
+ ) {
for family in families.into_iter() {
self.insert_user_themes(family.themes);
}
}
#[allow(unused)]
- fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
+ fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = ThemeContent>) {
self.insert_themes(themes.into_iter().map(|user_theme| {
let mut theme_colors = match user_theme.appearance {
- Appearance::Light => ThemeColors::light(),
- Appearance::Dark => ThemeColors::dark(),
+ AppearanceContent::Light => ThemeColors::light(),
+ AppearanceContent::Dark => ThemeColors::dark(),
};
- theme_colors.refine(&user_theme.styles.colors);
+ theme_colors.refine(&user_theme.style.theme_colors_refinement());
let mut status_colors = match user_theme.appearance {
- Appearance::Light => StatusColors::light(),
- Appearance::Dark => StatusColors::dark(),
+ AppearanceContent::Light => StatusColors::light(),
+ AppearanceContent::Dark => StatusColors::dark(),
};
- status_colors.refine(&user_theme.styles.status);
+ status_colors.refine(&user_theme.style.status_colors_refinement());
let mut player_colors = match user_theme.appearance {
- Appearance::Light => PlayerColors::light(),
- Appearance::Dark => PlayerColors::dark(),
+ AppearanceContent::Light => PlayerColors::light(),
+ AppearanceContent::Dark => PlayerColors::dark(),
};
- if let Some(player_colors_from_theme) = user_theme.styles.player {
- player_colors = player_colors_from_theme;
+ if !user_theme.style.players.is_empty() {
+ player_colors = PlayerColors(
+ user_theme
+ .style
+ .players
+ .into_iter()
+ .map(|player| PlayerColor {
+ cursor: player
+ .cursor
+ .as_ref()
+ .and_then(|color| try_parse_color(&color).ok())
+ .unwrap_or_default(),
+ background: player
+ .background
+ .as_ref()
+ .and_then(|color| try_parse_color(&color).ok())
+ .unwrap_or_default(),
+ selection: player
+ .selection
+ .as_ref()
+ .and_then(|color| try_parse_color(&color).ok())
+ .unwrap_or_default(),
+ })
+ .collect(),
+ );
}
let mut syntax_colors = match user_theme.appearance {
- Appearance::Light => SyntaxTheme::light(),
- Appearance::Dark => SyntaxTheme::dark(),
+ AppearanceContent::Light => SyntaxTheme::light(),
+ AppearanceContent::Dark => SyntaxTheme::dark(),
};
- if let Some(user_syntax) = user_theme.styles.syntax {
- syntax_colors.highlights = user_syntax
- .highlights
+ if !user_theme.style.syntax.is_empty() {
+ syntax_colors.highlights = user_theme
+ .style
+ .syntax
.iter()
.map(|(syntax_token, highlight)| {
(
syntax_token.clone(),
HighlightStyle {
- color: highlight.color,
+ color: highlight
+ .color
+ .as_ref()
+ .and_then(|color| try_parse_color(&color).ok()),
font_style: highlight.font_style.map(Into::into),
font_weight: highlight.font_weight.map(Into::into),
..Default::default()
@@ -88,7 +136,10 @@ impl ThemeRegistry {
Theme {
id: uuid::Uuid::new_v4().to_string(),
name: user_theme.name.into(),
- appearance: user_theme.appearance,
+ appearance: match user_theme.appearance {
+ AppearanceContent::Light => Appearance::Light,
+ AppearanceContent::Dark => Appearance::Dark,
+ },
styles: ThemeStyles {
system: SystemColors::default(),
colors: theme_colors,
@@ -124,24 +175,28 @@ impl ThemeRegistry {
}
pub fn load_user_themes(&mut self) {
- #[cfg(not(feature = "importing-themes"))]
- self.insert_user_theme_families(crate::all_user_themes());
+ let theme_paths = self
+ .assets
+ .list("themes/")
+ .unwrap()
+ .into_iter()
+ .filter(|path| path.ends_with(".json"));
+
+ for path in theme_paths {
+ let theme = self
+ .assets
+ .load(&path)
+ .expect(&format!("Failed to load theme '{path}'"));
+
+ let theme_family: ThemeFamilyContent = serde_json::from_slice(&theme).unwrap();
+
+ self.insert_user_theme_families([theme_family]);
+ }
}
}
impl Default for ThemeRegistry {
fn default() -> Self {
- let mut registry = Self {
- themes: HashMap::default(),
- };
-
- // We're loading our new versions of the One themes by default, as
- // we need them to be loaded for tests.
- //
- // These themes will get overwritten when `load_user_themes` is called
- // when Zed starts, so the One variants used will be the ones ported from Zed1.
- registry.insert_theme_families([crate::one_themes::one_family()]);
-
- registry
+ Self::new(Box::new(()))
}
}
@@ -1,5 +1,5 @@
use anyhow::Result;
-use gpui::{HighlightStyle, Hsla};
+use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
use indexmap::IndexMap;
use palette::FromColor;
use schemars::gen::SchemaGenerator;
@@ -11,7 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::{StatusColorsRefinement, ThemeColorsRefinement};
-fn try_parse_color(color: &str) -> Result<Hsla> {
+pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
let rgba = gpui::Rgba::try_from(color)?;
let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
let hsla = palette::Hsla::from_color(rgba);
@@ -1171,6 +1171,16 @@ pub enum FontStyleContent {
Oblique,
}
+impl From<FontStyleContent> for FontStyle {
+ fn from(value: FontStyleContent) -> Self {
+ match value {
+ FontStyleContent::Normal => FontStyle::Normal,
+ FontStyleContent::Italic => FontStyle::Italic,
+ FontStyleContent::Oblique => FontStyle::Oblique,
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(u16)]
pub enum FontWeightContent {
@@ -1211,6 +1221,22 @@ impl JsonSchema for FontWeightContent {
}
}
+impl From<FontWeightContent> for FontWeight {
+ fn from(value: FontWeightContent) -> Self {
+ match value {
+ FontWeightContent::Thin => FontWeight::THIN,
+ FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT,
+ FontWeightContent::Light => FontWeight::LIGHT,
+ FontWeightContent::Normal => FontWeight::NORMAL,
+ FontWeightContent::Medium => FontWeight::MEDIUM,
+ FontWeightContent::Semibold => FontWeight::SEMIBOLD,
+ FontWeightContent::Bold => FontWeight::BOLD,
+ FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD,
+ FontWeightContent::Black => FontWeight::BLACK,
+ }
+ }
+}
+
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct HighlightStyleContent {
@@ -33,7 +33,7 @@ pub use styles::*;
pub use themes::*;
pub use user_theme::*;
-use gpui::{AppContext, Hsla, SharedString};
+use gpui::{AppContext, AssetSource, Hsla, SharedString};
use serde::Deserialize;
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
@@ -51,7 +51,6 @@ impl Appearance {
}
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LoadThemes {
/// Only load the base theme.
///
@@ -59,15 +58,20 @@ pub enum LoadThemes {
JustBase,
/// Load all of the built-in themes.
- All,
+ All(Box<dyn AssetSource>),
}
pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
- cx.set_global(ThemeRegistry::default());
- match themes_to_load {
- LoadThemes::JustBase => (),
- LoadThemes::All => cx.global_mut::<ThemeRegistry>().load_user_themes(),
+ let (assets, load_user_themes) = match themes_to_load {
+ LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false),
+ LoadThemes::All(assets) => (assets, true),
+ };
+ cx.set_global(ThemeRegistry::new(assets));
+
+ if load_user_themes {
+ cx.global_mut::<ThemeRegistry>().load_user_themes();
}
+
ThemeSettings::register(cx);
let mut prev_buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
@@ -149,7 +149,7 @@ fn main() {
cx.set_global(client.clone());
zed::init(cx);
- theme::init(theme::LoadThemes::All, cx);
+ theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
project::Project::init(&client, cx);
client::init(&client, cx);
command_palette::init(cx);
@@ -9,7 +9,7 @@ OUTPUT_FILE=$(pwd)/assets/licenses.md
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
echo "Generating theme licenses"
-cat crates/theme/src/themes/LICENSES >> $OUTPUT_FILE
+cat assets/themes/LICENSES >> $OUTPUT_FILE
echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE