Add experimental.theme_overrides to settings file (#6791)

This PR adds **experimental** support for overriding theme values in the
current theme.

Be advised that both the existence of this setting and the structure of
the theme itself are subject to change.

But this is a first step towards allowing Zed users to customize or
bring their own themes.

### How it works

There is a new `experimental.theme_overrides` setting in
`settings.json`.

This accepts an object containing overrides for values in the theme. All
values are optional, and will be overlaid on top of whatever theme you
currently have set by the `theme` field.

There is JSON schema support to show which values are supported.

### Example

Here's an example of it in action:


https://github.com/zed-industries/zed/assets/1486634/173b94b1-4d88-4333-b980-8fed937e6f6d

Release Notes:

- Added `experimental.theme_overrides` to `settings.json` to allow for
customizing the current theme.
  - This setting is experimental and subject to change.
This commit is contained in:
Marshall Bowers 2024-01-26 13:42:58 -05:00 committed by GitHub
parent c9c9a6b11b
commit f5f7be1500
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 1182 additions and 3 deletions

2
Cargo.lock generated
View File

@ -6524,6 +6524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
dependencies = [
"dyn-clone",
"indexmap 1.9.3",
"schemars_derive",
"serde",
"serde_json",
@ -7856,6 +7857,7 @@ dependencies = [
"gpui",
"indexmap 1.9.3",
"itertools 0.11.0",
"palette",
"parking_lot 0.11.2",
"refineable",
"schemars",

View File

@ -24,10 +24,11 @@ doctest = false
anyhow.workspace = true
fs = { path = "../fs" }
gpui = { path = "../gpui" }
indexmap = "1.6.2"
indexmap = { version = "1.6.2", features = ["serde"] }
palette = { version = "0.7.3", default-features = false, features = ["std"] }
parking_lot.workspace = true
refineable.workspace = true
schemars.workspace = true
schemars = { workspace = true, features = ["indexmap"] }
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true

1133
crates/theme/src/schema.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,10 @@
use crate::one_themes::one_dark;
use crate::{Theme, ThemeRegistry};
use crate::{SyntaxTheme, Theme, ThemeContent, ThemeRegistry};
use anyhow::Result;
use gpui::{
px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels, Subscription, ViewContext,
};
use refineable::Refineable;
use schemars::{
gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject},
@ -26,6 +27,7 @@ pub struct ThemeSettings {
pub buffer_font_size: Pixels,
pub buffer_line_height: BufferLineHeight,
pub active_theme: Arc<Theme>,
pub theme_overrides: Option<ThemeContent>,
}
#[derive(Default)]
@ -49,6 +51,12 @@ pub struct ThemeSettingsContent {
pub buffer_font_features: Option<FontFeatures>,
#[serde(default)]
pub theme: Option<String>,
/// EXPERIMENTAL: Overrides for the current theme.
///
/// These values will override the ones on the current theme specified in `theme`.
#[serde(rename = "experimental.theme_overrides", default)]
pub theme_overrides: Option<ThemeContent>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
@ -80,6 +88,33 @@ impl ThemeSettings {
pub fn line_height(&self) -> f32 {
f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
}
/// Applies the theme overrides, if there are any, to the current theme.
pub fn apply_theme_overrides(&mut self) {
if let Some(theme_overrides) = &self.theme_overrides {
let mut base_theme = (*self.active_theme).clone();
base_theme
.styles
.colors
.refine(&theme_overrides.theme_colors_refinement());
base_theme
.styles
.status
.refine(&theme_overrides.status_colors_refinement());
base_theme.styles.syntax = Arc::new(SyntaxTheme {
highlights: {
let mut highlights = base_theme.styles.syntax.highlights.clone();
// Overrides come second in the highlight list so that they take precedence
// over the ones in the base theme.
highlights.extend(theme_overrides.syntax_overrides());
highlights
},
});
self.active_theme = Arc::new(base_theme);
}
}
}
pub fn observe_buffer_font_size_adjustment<V: 'static>(
@ -151,6 +186,7 @@ impl settings::Settings for ThemeSettings {
.get(defaults.theme.as_ref().unwrap())
.or(themes.get(&one_dark().name))
.unwrap(),
theme_overrides: None,
};
for value in user_values.into_iter().copied().cloned() {
@ -174,6 +210,9 @@ impl settings::Settings for ThemeSettings {
}
}
this.theme_overrides = value.theme_overrides;
this.apply_theme_overrides();
merge(&mut this.ui_font_size, value.ui_font_size.map(Into::into));
merge(
&mut this.buffer_font_size,

View File

@ -12,6 +12,7 @@ mod one_themes;
pub mod prelude;
mod registry;
mod scale;
mod schema;
mod settings;
mod styles;
#[cfg(not(feature = "importing-themes"))]
@ -25,6 +26,7 @@ pub use default_colors::*;
pub use default_theme::*;
pub use registry::*;
pub use scale::*;
pub use schema::*;
pub use settings::*;
pub use styles::*;
#[cfg(not(feature = "importing-themes"))]
@ -99,6 +101,7 @@ pub struct ThemeFamily {
impl ThemeFamily {}
#[derive(Clone)]
pub struct Theme {
pub id: String,
pub name: SharedString,

View File

@ -159,6 +159,7 @@ impl ThemeSelectorDelegate {
cx.update_global(|store: &mut SettingsStore, cx| {
let mut theme_settings = store.get::<ThemeSettings>(None).clone();
theme_settings.active_theme = theme;
theme_settings.apply_theme_overrides();
store.override_global(theme_settings);
cx.refresh();
});