Load JSON themes (#6893)
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
This commit is contained in:
parent
f7fc4ffbe5
commit
5f1dcb76fe
7 changed files with 133 additions and 48 deletions
|
@ -69,7 +69,7 @@ fn main() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.set_global(store);
|
cx.set_global(store);
|
||||||
|
|
||||||
theme::init(theme::LoadThemes::All, cx);
|
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
|
||||||
|
|
||||||
let selector = story_selector;
|
let selector = story_selector;
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,13 @@ use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::{HighlightStyle, SharedString};
|
use gpui::{AssetSource, HighlightStyle, SharedString};
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme, ThemeColors,
|
try_parse_color, Appearance, AppearanceContent, PlayerColor, PlayerColors, StatusColors,
|
||||||
ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeContent, ThemeFamily, ThemeFamilyContent,
|
||||||
|
ThemeStyles,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -17,10 +18,27 @@ pub struct ThemeMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThemeRegistry {
|
pub struct ThemeRegistry {
|
||||||
|
assets: Box<dyn AssetSource>,
|
||||||
themes: HashMap<SharedString, Arc<Theme>>,
|
themes: HashMap<SharedString, Arc<Theme>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThemeRegistry {
|
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>) {
|
fn insert_theme_families(&mut 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);
|
||||||
|
@ -34,48 +52,78 @@ impl ThemeRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[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() {
|
for family in families.into_iter() {
|
||||||
self.insert_user_themes(family.themes);
|
self.insert_user_themes(family.themes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[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| {
|
self.insert_themes(themes.into_iter().map(|user_theme| {
|
||||||
let mut theme_colors = match user_theme.appearance {
|
let mut theme_colors = match user_theme.appearance {
|
||||||
Appearance::Light => ThemeColors::light(),
|
AppearanceContent::Light => ThemeColors::light(),
|
||||||
Appearance::Dark => ThemeColors::dark(),
|
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 {
|
let mut status_colors = match user_theme.appearance {
|
||||||
Appearance::Light => StatusColors::light(),
|
AppearanceContent::Light => StatusColors::light(),
|
||||||
Appearance::Dark => StatusColors::dark(),
|
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 {
|
let mut player_colors = match user_theme.appearance {
|
||||||
Appearance::Light => PlayerColors::light(),
|
AppearanceContent::Light => PlayerColors::light(),
|
||||||
Appearance::Dark => PlayerColors::dark(),
|
AppearanceContent::Dark => PlayerColors::dark(),
|
||||||
};
|
};
|
||||||
if let Some(player_colors_from_theme) = user_theme.styles.player {
|
if !user_theme.style.players.is_empty() {
|
||||||
player_colors = player_colors_from_theme;
|
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 {
|
let mut syntax_colors = match user_theme.appearance {
|
||||||
Appearance::Light => SyntaxTheme::light(),
|
AppearanceContent::Light => SyntaxTheme::light(),
|
||||||
Appearance::Dark => SyntaxTheme::dark(),
|
AppearanceContent::Dark => SyntaxTheme::dark(),
|
||||||
};
|
};
|
||||||
if let Some(user_syntax) = user_theme.styles.syntax {
|
if !user_theme.style.syntax.is_empty() {
|
||||||
syntax_colors.highlights = user_syntax
|
syntax_colors.highlights = user_theme
|
||||||
.highlights
|
.style
|
||||||
|
.syntax
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(syntax_token, highlight)| {
|
.map(|(syntax_token, highlight)| {
|
||||||
(
|
(
|
||||||
syntax_token.clone(),
|
syntax_token.clone(),
|
||||||
HighlightStyle {
|
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_style: highlight.font_style.map(Into::into),
|
||||||
font_weight: highlight.font_weight.map(Into::into),
|
font_weight: highlight.font_weight.map(Into::into),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -88,7 +136,10 @@ impl ThemeRegistry {
|
||||||
Theme {
|
Theme {
|
||||||
id: uuid::Uuid::new_v4().to_string(),
|
id: uuid::Uuid::new_v4().to_string(),
|
||||||
name: user_theme.name.into(),
|
name: user_theme.name.into(),
|
||||||
appearance: user_theme.appearance,
|
appearance: match user_theme.appearance {
|
||||||
|
AppearanceContent::Light => Appearance::Light,
|
||||||
|
AppearanceContent::Dark => Appearance::Dark,
|
||||||
|
},
|
||||||
styles: ThemeStyles {
|
styles: ThemeStyles {
|
||||||
system: SystemColors::default(),
|
system: SystemColors::default(),
|
||||||
colors: theme_colors,
|
colors: theme_colors,
|
||||||
|
@ -124,24 +175,28 @@ impl ThemeRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_user_themes(&mut self) {
|
pub fn load_user_themes(&mut self) {
|
||||||
#[cfg(not(feature = "importing-themes"))]
|
let theme_paths = self
|
||||||
self.insert_user_theme_families(crate::all_user_themes());
|
.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 {
|
impl Default for ThemeRegistry {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut registry = Self {
|
Self::new(Box::new(()))
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{HighlightStyle, Hsla};
|
use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use palette::FromColor;
|
use palette::FromColor;
|
||||||
use schemars::gen::SchemaGenerator;
|
use schemars::gen::SchemaGenerator;
|
||||||
|
@ -11,7 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::{StatusColorsRefinement, ThemeColorsRefinement};
|
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 = gpui::Rgba::try_from(color)?;
|
||||||
let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
|
let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
|
||||||
let hsla = palette::Hsla::from_color(rgba);
|
let hsla = palette::Hsla::from_color(rgba);
|
||||||
|
@ -1171,6 +1171,16 @@ pub enum FontStyleContent {
|
||||||
Oblique,
|
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)]
|
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum FontWeightContent {
|
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)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct HighlightStyleContent {
|
pub struct HighlightStyleContent {
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub use styles::*;
|
||||||
pub use themes::*;
|
pub use themes::*;
|
||||||
pub use user_theme::*;
|
pub use user_theme::*;
|
||||||
|
|
||||||
use gpui::{AppContext, Hsla, SharedString};
|
use gpui::{AppContext, AssetSource, Hsla, SharedString};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
|
||||||
|
@ -51,7 +51,6 @@ impl Appearance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum LoadThemes {
|
pub enum LoadThemes {
|
||||||
/// Only load the base theme.
|
/// Only load the base theme.
|
||||||
///
|
///
|
||||||
|
@ -59,15 +58,20 @@ pub enum LoadThemes {
|
||||||
JustBase,
|
JustBase,
|
||||||
|
|
||||||
/// Load all of the built-in themes.
|
/// Load all of the built-in themes.
|
||||||
All,
|
All(Box<dyn AssetSource>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
|
pub fn init(themes_to_load: LoadThemes, cx: &mut AppContext) {
|
||||||
cx.set_global(ThemeRegistry::default());
|
let (assets, load_user_themes) = match themes_to_load {
|
||||||
match themes_to_load {
|
LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false),
|
||||||
LoadThemes::JustBase => (),
|
LoadThemes::All(assets) => (assets, true),
|
||||||
LoadThemes::All => cx.global_mut::<ThemeRegistry>().load_user_themes(),
|
};
|
||||||
|
cx.set_global(ThemeRegistry::new(assets));
|
||||||
|
|
||||||
|
if load_user_themes {
|
||||||
|
cx.global_mut::<ThemeRegistry>().load_user_themes();
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeSettings::register(cx);
|
ThemeSettings::register(cx);
|
||||||
|
|
||||||
let mut prev_buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
let mut prev_buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
|
||||||
|
|
|
@ -149,7 +149,7 @@ fn main() {
|
||||||
cx.set_global(client.clone());
|
cx.set_global(client.clone());
|
||||||
|
|
||||||
zed::init(cx);
|
zed::init(cx);
|
||||||
theme::init(theme::LoadThemes::All, cx);
|
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
|
||||||
project::Project::init(&client, cx);
|
project::Project::init(&client, cx);
|
||||||
client::init(&client, cx);
|
client::init(&client, cx);
|
||||||
command_palette::init(cx);
|
command_palette::init(cx);
|
||||||
|
|
|
@ -9,7 +9,7 @@ OUTPUT_FILE=$(pwd)/assets/licenses.md
|
||||||
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
|
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
|
||||||
|
|
||||||
echo "Generating theme licenses"
|
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
|
echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue