Detailed changes
@@ -4,6 +4,7 @@ use crate::{
self as settings,
settings_content::{self, BaseKeymapContent, SettingsContent},
};
+use gpui::App;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, VsCodeSettings};
@@ -138,11 +139,11 @@ pub struct BaseKeymapSetting {
}
impl Settings for BaseKeymap {
- fn from_file(s: &crate::settings_content::SettingsContent) -> Option<Self> {
+ fn from_file(s: &crate::settings_content::SettingsContent, _cx: &mut App) -> Option<Self> {
s.base_keymap.map(Into::into)
}
- fn refine(&mut self, s: &settings_content::SettingsContent) {
+ fn refine(&mut self, s: &settings_content::SettingsContent, _cx: &mut App) {
if let Some(base_keymap) = s.base_keymap {
*self = base_keymap.into();
};
@@ -18,6 +18,9 @@ pub struct SettingsContent {
#[serde(flatten)]
pub project: ProjectSettingsContent,
+ #[serde(flatten)]
+ pub theme: ThemeSettingsContent,
+
pub base_keymap: Option<BaseKeymapContent>,
pub auto_update: Option<bool>,
@@ -1,5 +1,5 @@
use collections::{HashMap, IndexMap};
-use gpui::{FontFallbacks, FontFeatures};
+use gpui::{FontFallbacks, FontFeatures, FontStyle, FontWeight};
use schemars::{JsonSchema, JsonSchema_repr, json_schema};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
@@ -999,6 +999,16 @@ pub enum WindowBackgroundContent {
Blurred,
}
+impl Into<gpui::WindowBackgroundAppearance> for WindowBackgroundContent {
+ fn into(self) -> gpui::WindowBackgroundAppearance {
+ match self {
+ WindowBackgroundContent::Opaque => gpui::WindowBackgroundAppearance::Opaque,
+ WindowBackgroundContent::Transparent => gpui::WindowBackgroundAppearance::Transparent,
+ WindowBackgroundContent::Blurred => gpui::WindowBackgroundAppearance::Blurred,
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FontStyleContent {
@@ -1007,6 +1017,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, JsonSchema_repr, PartialEq)]
#[repr(u16)]
pub enum FontWeightContent {
@@ -1020,3 +1040,70 @@ pub enum FontWeightContent {
ExtraBold = 800,
Black = 900,
}
+
+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,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_json::json;
+
+ #[test]
+ fn test_buffer_line_height_deserialize_valid() {
+ assert_eq!(
+ serde_json::from_value::<BufferLineHeight>(json!("comfortable")).unwrap(),
+ BufferLineHeight::Comfortable
+ );
+ assert_eq!(
+ serde_json::from_value::<BufferLineHeight>(json!("standard")).unwrap(),
+ BufferLineHeight::Standard
+ );
+ assert_eq!(
+ serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.0})).unwrap(),
+ BufferLineHeight::Custom(1.0)
+ );
+ assert_eq!(
+ serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.5})).unwrap(),
+ BufferLineHeight::Custom(1.5)
+ );
+ }
+
+ #[test]
+ fn test_buffer_line_height_deserialize_invalid() {
+ assert!(
+ serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.99}))
+ .err()
+ .unwrap()
+ .to_string()
+ .contains("buffer_line_height.custom must be at least 1.0")
+ );
+ assert!(
+ serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.0}))
+ .err()
+ .unwrap()
+ .to_string()
+ .contains("buffer_line_height.custom must be at least 1.0")
+ );
+ assert!(
+ serde_json::from_value::<BufferLineHeight>(json!({"custom": -1.0}))
+ .err()
+ .unwrap()
+ .to_string()
+ .contains("buffer_line_height.custom must be at least 1.0")
+ );
+ }
+}
@@ -21,16 +21,13 @@ use std::{
str::{self, FromStr},
sync::Arc,
};
-use util::{
- ResultExt as _, merge_non_null_json_value_into,
-};
+use util::{ResultExt as _, merge_non_null_json_value_into};
pub type EditorconfigProperties = ec4rs::Properties;
use crate::{
- ActiveSettingsProfileName, SettingsJsonSchemaParams, SettingsUiEntry,
- VsCodeSettings, WorktreeId, parse_json_with_comments,
- replace_value_in_json_text,
+ ActiveSettingsProfileName, SettingsJsonSchemaParams, SettingsUiEntry, VsCodeSettings,
+ WorktreeId, parse_json_with_comments, replace_value_in_json_text,
settings_content::{
ExtensionsSettingsContent, ProjectSettingsContent, ServerSettingsContent, SettingsContent,
UserSettingsContent,
@@ -60,9 +57,9 @@ pub trait Settings: 'static + Send + Sync + Sized {
/// user settings match the current version of the settings.
const PRESERVED_KEYS: Option<&'static [&'static str]> = None;
- fn from_file(content: &SettingsContent) -> Option<Self>;
+ fn from_file(content: &SettingsContent, cx: &mut App) -> Option<Self>;
- fn refine(&mut self, content: &SettingsContent);
+ fn refine(&mut self, content: &SettingsContent, cx: &mut App);
fn missing_default() -> anyhow::Error {
anyhow::anyhow!("missing default for: {}", std::any::type_name::<Self>())
@@ -252,8 +249,8 @@ struct SettingValue<T> {
trait AnySettingValue: 'static + Send + Sync {
fn setting_type_name(&self) -> &'static str;
- fn from_file(&self, s: &SettingsContent) -> Option<Box<dyn Any>>;
- fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent]);
+ fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option<Box<dyn Any>>;
+ fn refine(&self, value: &mut dyn Any, s: &[&SettingsContent], cx: &mut App);
fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>;
@@ -347,9 +344,9 @@ impl SettingsStore {
refinements.push(server_settings)
}
// todo!() unwrap...
- let mut value = T::from_file(&self.default_settings).unwrap();
+ let mut value = T::from_file(&self.default_settings, cx).unwrap();
for refinement in refinements {
- value.refine(refinement)
+ value.refine(refinement, cx)
}
setting_value.set_global_value(Box::new(value));
@@ -1114,8 +1111,8 @@ impl SettingsStore {
for setting_value in self.setting_values.values_mut() {
// If the global settings file changed, reload the global value for the field.
if changed_local_path.is_none() {
- let mut value = setting_value.from_file(&self.default_settings).unwrap();
- setting_value.refine(value.as_mut(), &refinements);
+ let mut value = setting_value.from_file(&self.default_settings, cx).unwrap();
+ setting_value.refine(value.as_mut(), &refinements, cx);
setting_value.set_global_value(value);
}
@@ -1137,7 +1134,7 @@ impl SettingsStore {
// NOTE: this kind of condition existing in the old code too,
// but is there a problem when a setting is removed from a file?
- if setting_value.from_file(local_settings).is_some() {
+ if setting_value.from_file(local_settings, cx).is_some() {
paths_stack.push(Some((*root_id, directory_path.as_ref())));
project_settings_stack.push(local_settings);
@@ -1150,9 +1147,9 @@ impl SettingsStore {
continue;
}
- let mut value = setting_value.from_file(&self.default_settings).unwrap();
- setting_value.refine(value.as_mut(), &refinements);
- setting_value.refine(value.as_mut(), &project_settings_stack);
+ let mut value = setting_value.from_file(&self.default_settings, cx).unwrap();
+ setting_value.refine(value.as_mut(), &refinements, cx);
+ setting_value.refine(value.as_mut(), &project_settings_stack, cx);
setting_value.set_local_value(*root_id, directory_path.clone(), value);
}
}
@@ -1235,16 +1232,16 @@ impl Debug for SettingsStore {
}
impl<T: Settings> AnySettingValue for SettingValue<T> {
- fn from_file(&self, s: &SettingsContent) -> Option<Box<dyn Any>> {
+ fn from_file(&self, s: &SettingsContent, cx: &mut App) -> Option<Box<dyn Any>> {
(type_name::<T>(), TypeId::of::<T>());
- T::from_file(s).map(|result| Box::new(result) as _)
+ T::from_file(s, cx).map(|result| Box::new(result) as _)
}
- fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent]) {
+ fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent], cx: &mut App) {
(type_name::<T>(), TypeId::of::<T>());
let value = value.downcast_mut::<T>().unwrap();
for refinement in refinements {
- value.refine(refinement)
+ value.refine(refinement, cx)
}
}
@@ -1338,7 +1335,7 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
#[cfg(test)]
mod tests {
use crate::{
- TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource,
+ TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, default_settings,
settings_content::LanguageSettingsContent, test_settings,
};
@@ -1348,7 +1345,7 @@ mod tests {
use serde::Deserialize;
use settings_ui_macros::{SettingsKey, SettingsUi};
use unindent::Unindent;
- use util::Refine;
+ use util::MergeFrom;
#[derive(Debug, PartialEq)]
struct AutoUpdateSetting {
@@ -1356,13 +1353,13 @@ mod tests {
}
impl Settings for AutoUpdateSetting {
- fn from_file(content: &SettingsContent) -> Option<Self> {
+ fn from_file(content: &SettingsContent, _: &mut App) -> Option<Self> {
content
.auto_update
.map(|auto_update| AutoUpdateSetting { auto_update })
}
- fn refine(&mut self, content: &SettingsContent) {
+ fn refine(&mut self, content: &SettingsContent, _: &mut App) {
if let Some(auto_update) = content.auto_update {
self.auto_update = auto_update;
}
@@ -1377,18 +1374,18 @@ mod tests {
}
impl Settings for TitleBarSettings {
- fn from_file(content: &SettingsContent) -> Option<Self> {
+ fn from_file(content: &SettingsContent, _: &mut App) -> Option<Self> {
let content = content.title_bar?;
Some(TitleBarSettings {
show: content.show?,
})
}
- fn refine(&mut self, content: &SettingsContent) {
+ fn refine(&mut self, content: &SettingsContent, _: &mut App) {
let Some(content) = content.title_bar else {
return;
};
- self.show.refine(&content.show)
+ self.show.merge_from(&content.show)
}
fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {}
@@ -11,21 +11,6 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::{StatusColorsRefinement, ThemeColorsRefinement};
-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);
-
- let hsla = gpui::hsla(
- hsla.hue.into_positive_degrees() / 360.,
- hsla.saturation,
- hsla.lightness,
- hsla.alpha,
- );
-
- Ok(hsla)
-}
-
fn ensure_non_opaque(color: Hsla) -> Hsla {
const MAXIMUM_OPACITY: f32 = 0.7;
if color.a <= MAXIMUM_OPACITY {
@@ -81,7 +66,7 @@ pub struct ThemeFamilyContent {
pub struct ThemeContent {
pub name: String,
pub appearance: AppearanceContent,
- pub style: ThemeStyleContent,
+ pub style: settings::ThemeStyleContent,
}
/// The content of a serialized theme.
@@ -95,909 +80,214 @@ pub struct ThemeStyleContent {
pub accents: Vec<AccentContent>,
#[serde(flatten, default)]
- pub colors: ThemeColorsContent,
+ pub colors: settings::ThemeColorsContent,
#[serde(flatten, default)]
- pub status: StatusColorsContent,
+ pub status: settings::StatusColorsContent,
#[serde(default)]
pub players: Vec<PlayerColorContent>,
/// The styles for syntax nodes.
#[serde(default)]
- pub syntax: IndexMap<String, HighlightStyleContent>,
+ pub syntax: IndexMap<String, settings::HighlightStyleContent>,
}
-impl ThemeStyleContent {
- /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeContent`].
- #[inline(always)]
- pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement {
- self.colors
- .theme_colors_refinement(&self.status_colors_refinement())
- }
-
- /// Returns a [`StatusColorsRefinement`] based on the colors in the [`ThemeContent`].
- #[inline(always)]
- pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
- self.status.status_colors_refinement()
- }
-
- /// Returns the syntax style overrides in the [`ThemeContent`].
- pub fn syntax_overrides(&self) -> Vec<(String, HighlightStyle)> {
- self.syntax
- .iter()
- .map(|(key, style)| {
- (
- key.clone(),
- HighlightStyle {
- color: style
- .color
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- background_color: style
- .background_color
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- font_style: style.font_style.map(FontStyle::from),
- font_weight: style.font_weight.map(FontWeight::from),
- ..Default::default()
- },
- )
- })
- .collect()
- }
+/// Returns the syntax style overrides in the [`ThemeContent`].
+pub fn syntax_overrides(this: &settings::ThemeStyleContent) -> Vec<(String, HighlightStyle)> {
+ this.syntax
+ .iter()
+ .map(|(key, style)| {
+ (
+ key.clone(),
+ HighlightStyle {
+ color: style
+ .color
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ background_color: style
+ .background_color
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ font_style: style.font_style.map(FontStyle::from),
+ font_weight: style.font_weight.map(FontWeight::from),
+ ..Default::default()
+ },
+ )
+ })
+ .collect()
}
-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);
-
- let hsla = gpui::hsla(
- hsla.hue.into_positive_degrees() / 360.,
- hsla.saturation,
- hsla.lightness,
- hsla.alpha,
- );
-
- Ok(hsla)
-}
-
-impl ThemeColorsContent {
- /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeColorsContent`].
- pub fn theme_colors_refinement(
- &self,
- status_colors: &StatusColorsRefinement,
- ) -> ThemeColorsRefinement {
- let border = self
- .border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- let editor_document_highlight_read_background = self
- .editor_document_highlight_read_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- let scrollbar_thumb_background = self
- .scrollbar_thumb_background
+pub fn status_colors_refinement(colors: &settings::StatusColorsContent) -> StatusColorsRefinement {
+ StatusColorsRefinement {
+ conflict: colors
+ .conflict
.as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or_else(|| {
- self.deprecated_scrollbar_thumb_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- });
- let scrollbar_thumb_hover_background = self
- .scrollbar_thumb_hover_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- let scrollbar_thumb_active_background = self
- .scrollbar_thumb_active_background
+ .and_then(|color| try_parse_color(color).ok()),
+ conflict_background: colors
+ .conflict_background
.as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(scrollbar_thumb_background);
- let scrollbar_thumb_border = self
- .scrollbar_thumb_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- let element_hover = self
- .element_hover
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- let panel_background = self
- .panel_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok());
- ThemeColorsRefinement {
- border,
- border_variant: self
- .border_variant
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- border_focused: self
- .border_focused
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- border_selected: self
- .border_selected
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- border_transparent: self
- .border_transparent
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- border_disabled: self
- .border_disabled
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- elevated_surface_background: self
- .elevated_surface_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- surface_background: self
- .surface_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- background: self
- .background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- element_background: self
- .element_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- element_hover,
- element_active: self
- .element_active
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- element_selected: self
- .element_selected
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- element_disabled: self
- .element_disabled
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- element_selection_background: self
- .element_selection_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- drop_target_background: self
- .drop_target_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- drop_target_border: self
- .drop_target_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ghost_element_background: self
- .ghost_element_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ghost_element_hover: self
- .ghost_element_hover
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ghost_element_active: self
- .ghost_element_active
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ghost_element_selected: self
- .ghost_element_selected
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ghost_element_disabled: self
- .ghost_element_disabled
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- text: self
- .text
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- text_muted: self
- .text_muted
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- text_placeholder: self
- .text_placeholder
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- text_disabled: self
- .text_disabled
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- text_accent: self
- .text_accent
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- icon: self
- .icon
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- icon_muted: self
- .icon_muted
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- icon_disabled: self
- .icon_disabled
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- icon_placeholder: self
- .icon_placeholder
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- icon_accent: self
- .icon_accent
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- debugger_accent: self
- .debugger_accent
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- status_bar_background: self
- .status_bar_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- title_bar_background: self
- .title_bar_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- title_bar_inactive_background: self
- .title_bar_inactive_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- toolbar_background: self
- .toolbar_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- tab_bar_background: self
- .tab_bar_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- tab_inactive_background: self
- .tab_inactive_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- tab_active_background: self
- .tab_active_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- search_match_background: self
- .search_match_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- panel_background,
- panel_focused_border: self
- .panel_focused_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- panel_indent_guide: self
- .panel_indent_guide
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- panel_indent_guide_hover: self
- .panel_indent_guide_hover
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- panel_indent_guide_active: self
- .panel_indent_guide_active
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- panel_overlay_background: self
- .panel_overlay_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(panel_background.map(ensure_opaque)),
- panel_overlay_hover: self
- .panel_overlay_hover
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(panel_background
- .zip(element_hover)
- .map(|(panel_bg, hover_bg)| panel_bg.blend(hover_bg))
- .map(ensure_opaque)),
- pane_focused_border: self
- .pane_focused_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- pane_group_border: self
- .pane_group_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(border),
- scrollbar_thumb_background,
- scrollbar_thumb_hover_background,
- scrollbar_thumb_active_background,
- scrollbar_thumb_border,
- scrollbar_track_background: self
- .scrollbar_track_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- scrollbar_track_border: self
- .scrollbar_track_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- minimap_thumb_background: self
- .minimap_thumb_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(scrollbar_thumb_background.map(ensure_non_opaque)),
- minimap_thumb_hover_background: self
- .minimap_thumb_hover_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(scrollbar_thumb_hover_background.map(ensure_non_opaque)),
- minimap_thumb_active_background: self
- .minimap_thumb_active_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(scrollbar_thumb_active_background.map(ensure_non_opaque)),
- minimap_thumb_border: self
- .minimap_thumb_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(scrollbar_thumb_border),
- editor_foreground: self
- .editor_foreground
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_background: self
- .editor_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_gutter_background: self
- .editor_gutter_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_subheader_background: self
- .editor_subheader_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_active_line_background: self
- .editor_active_line_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_highlighted_line_background: self
- .editor_highlighted_line_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_debugger_active_line_background: self
- .editor_debugger_active_line_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_line_number: self
- .editor_line_number
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_hover_line_number: self
- .editor_hover_line_number
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_active_line_number: self
- .editor_active_line_number
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_invisible: self
- .editor_invisible
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_wrap_guide: self
- .editor_wrap_guide
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_active_wrap_guide: self
- .editor_active_wrap_guide
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_indent_guide: self
- .editor_indent_guide
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_indent_guide_active: self
- .editor_indent_guide_active
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_document_highlight_read_background,
- editor_document_highlight_write_background: self
- .editor_document_highlight_write_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- editor_document_highlight_bracket_background: self
- .editor_document_highlight_bracket_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `editor.document_highlight.read_background`, for backwards compatibility.
- .or(editor_document_highlight_read_background),
- terminal_background: self
- .terminal_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_background: self
- .terminal_ansi_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_foreground: self
- .terminal_foreground
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_bright_foreground: self
- .terminal_bright_foreground
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_dim_foreground: self
- .terminal_dim_foreground
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_black: self
- .terminal_ansi_black
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_black: self
- .terminal_ansi_bright_black
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_black: self
- .terminal_ansi_dim_black
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_red: self
- .terminal_ansi_red
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_red: self
- .terminal_ansi_bright_red
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_red: self
- .terminal_ansi_dim_red
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_green: self
- .terminal_ansi_green
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_green: self
- .terminal_ansi_bright_green
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_green: self
- .terminal_ansi_dim_green
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_yellow: self
- .terminal_ansi_yellow
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_yellow: self
- .terminal_ansi_bright_yellow
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_yellow: self
- .terminal_ansi_dim_yellow
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_blue: self
- .terminal_ansi_blue
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_blue: self
- .terminal_ansi_bright_blue
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_blue: self
- .terminal_ansi_dim_blue
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_magenta: self
- .terminal_ansi_magenta
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_magenta: self
- .terminal_ansi_bright_magenta
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_magenta: self
- .terminal_ansi_dim_magenta
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_cyan: self
- .terminal_ansi_cyan
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_cyan: self
- .terminal_ansi_bright_cyan
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_cyan: self
- .terminal_ansi_dim_cyan
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_white: self
- .terminal_ansi_white
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_bright_white: self
- .terminal_ansi_bright_white
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- terminal_ansi_dim_white: self
- .terminal_ansi_dim_white
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- link_text_hover: self
- .link_text_hover
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- version_control_added: self
- .version_control_added
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `created`, for backwards compatibility.
- .or(status_colors.created),
- version_control_deleted: self
- .version_control_deleted
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `deleted`, for backwards compatibility.
- .or(status_colors.deleted),
- version_control_modified: self
- .version_control_modified
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `modified`, for backwards compatibility.
- .or(status_colors.modified),
- version_control_renamed: self
- .version_control_renamed
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `modified`, for backwards compatibility.
- .or(status_colors.modified),
- version_control_conflict: self
- .version_control_conflict
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `ignored`, for backwards compatibility.
- .or(status_colors.ignored),
- version_control_ignored: self
- .version_control_ignored
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- // Fall back to `conflict`, for backwards compatibility.
- .or(status_colors.ignored),
- #[allow(deprecated)]
- version_control_conflict_marker_ours: self
- .version_control_conflict_marker_ours
- .as_ref()
- .or(self.version_control_conflict_ours_background.as_ref())
- .and_then(|color| try_parse_color(color).ok()),
- #[allow(deprecated)]
- version_control_conflict_marker_theirs: self
- .version_control_conflict_marker_theirs
- .as_ref()
- .or(self.version_control_conflict_theirs_background.as_ref())
- .and_then(|color| try_parse_color(color).ok()),
- }
- }
-}
-
-#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
-#[serde(default)]
-pub struct StatusColorsContent {
- /// Indicates some kind of conflict, like a file changed on disk while it was open, or
- /// merge conflicts in a Git repository.
- #[serde(rename = "conflict")]
- pub conflict: Option<String>,
-
- #[serde(rename = "conflict.background")]
- pub conflict_background: Option<String>,
-
- #[serde(rename = "conflict.border")]
- pub conflict_border: Option<String>,
-
- /// Indicates something new, like a new file added to a Git repository.
- #[serde(rename = "created")]
- pub created: Option<String>,
-
- #[serde(rename = "created.background")]
- pub created_background: Option<String>,
-
- #[serde(rename = "created.border")]
- pub created_border: Option<String>,
-
- /// Indicates that something no longer exists, like a deleted file.
- #[serde(rename = "deleted")]
- pub deleted: Option<String>,
-
- #[serde(rename = "deleted.background")]
- pub deleted_background: Option<String>,
-
- #[serde(rename = "deleted.border")]
- pub deleted_border: Option<String>,
-
- /// Indicates a system error, a failed operation or a diagnostic error.
- #[serde(rename = "error")]
- pub error: Option<String>,
-
- #[serde(rename = "error.background")]
- pub error_background: Option<String>,
-
- #[serde(rename = "error.border")]
- pub error_border: Option<String>,
-
- /// Represents a hidden status, such as a file being hidden in a file tree.
- #[serde(rename = "hidden")]
- pub hidden: Option<String>,
-
- #[serde(rename = "hidden.background")]
- pub hidden_background: Option<String>,
-
- #[serde(rename = "hidden.border")]
- pub hidden_border: Option<String>,
-
- /// Indicates a hint or some kind of additional information.
- #[serde(rename = "hint")]
- pub hint: Option<String>,
-
- #[serde(rename = "hint.background")]
- pub hint_background: Option<String>,
-
- #[serde(rename = "hint.border")]
- pub hint_border: Option<String>,
-
- /// Indicates that something is deliberately ignored, such as a file or operation ignored by Git.
- #[serde(rename = "ignored")]
- pub ignored: Option<String>,
-
- #[serde(rename = "ignored.background")]
- pub ignored_background: Option<String>,
-
- #[serde(rename = "ignored.border")]
- pub ignored_border: Option<String>,
-
- /// Represents informational status updates or messages.
- #[serde(rename = "info")]
- pub info: Option<String>,
-
- #[serde(rename = "info.background")]
- pub info_background: Option<String>,
-
- #[serde(rename = "info.border")]
- pub info_border: Option<String>,
-
- /// Indicates a changed or altered status, like a file that has been edited.
- #[serde(rename = "modified")]
- pub modified: Option<String>,
-
- #[serde(rename = "modified.background")]
- pub modified_background: Option<String>,
-
- #[serde(rename = "modified.border")]
- pub modified_border: Option<String>,
-
- /// Indicates something that is predicted, like automatic code completion, or generated code.
- #[serde(rename = "predictive")]
- pub predictive: Option<String>,
-
- #[serde(rename = "predictive.background")]
- pub predictive_background: Option<String>,
-
- #[serde(rename = "predictive.border")]
- pub predictive_border: Option<String>,
-
- /// Represents a renamed status, such as a file that has been renamed.
- #[serde(rename = "renamed")]
- pub renamed: Option<String>,
-
- #[serde(rename = "renamed.background")]
- pub renamed_background: Option<String>,
-
- #[serde(rename = "renamed.border")]
- pub renamed_border: Option<String>,
-
- /// Indicates a successful operation or task completion.
- #[serde(rename = "success")]
- pub success: Option<String>,
-
- #[serde(rename = "success.background")]
- pub success_background: Option<String>,
-
- #[serde(rename = "success.border")]
- pub success_border: Option<String>,
-
- /// Indicates some kind of unreachable status, like a block of code that can never be reached.
- #[serde(rename = "unreachable")]
- pub unreachable: Option<String>,
-
- #[serde(rename = "unreachable.background")]
- pub unreachable_background: Option<String>,
-
- #[serde(rename = "unreachable.border")]
- pub unreachable_border: Option<String>,
-
- /// Represents a warning status, like an operation that is about to fail.
- #[serde(rename = "warning")]
- pub warning: Option<String>,
-
- #[serde(rename = "warning.background")]
- pub warning_background: Option<String>,
-
- #[serde(rename = "warning.border")]
- pub warning_border: Option<String>,
-}
-
-impl StatusColorsContent {
- /// Returns a [`StatusColorsRefinement`] based on the colors in the [`StatusColorsContent`].
- pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
- StatusColorsRefinement {
- conflict: self
- .conflict
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- conflict_background: self
- .conflict_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- conflict_border: self
- .conflict_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- created: self
- .created
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- created_background: self
- .created_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- created_border: self
- .created_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- deleted: self
- .deleted
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- deleted_background: self
- .deleted_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- deleted_border: self
- .deleted_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- error: self
- .error
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- error_background: self
- .error_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- error_border: self
- .error_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hidden: self
- .hidden
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hidden_background: self
- .hidden_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hidden_border: self
- .hidden_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hint: self
- .hint
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hint_background: self
- .hint_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- hint_border: self
- .hint_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ignored: self
- .ignored
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ignored_background: self
- .ignored_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- ignored_border: self
- .ignored_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- info: self
- .info
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- info_background: self
- .info_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- info_border: self
- .info_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- modified: self
- .modified
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- modified_background: self
- .modified_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- modified_border: self
- .modified_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- predictive: self
- .predictive
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- predictive_background: self
- .predictive_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- predictive_border: self
- .predictive_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- renamed: self
- .renamed
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- renamed_background: self
- .renamed_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- renamed_border: self
- .renamed_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- success: self
- .success
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- success_background: self
- .success_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- success_border: self
- .success_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- unreachable: self
- .unreachable
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- unreachable_background: self
- .unreachable_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- unreachable_border: self
- .unreachable_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- warning: self
- .warning
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- warning_background: self
- .warning_background
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- warning_border: self
- .warning_border
- .as_ref()
- .and_then(|color| try_parse_color(color).ok()),
- }
+ .and_then(|color| try_parse_color(color).ok()),
+ conflict_border: colors
+ .conflict_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ created: colors
+ .created
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ created_background: colors
+ .created_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ created_border: colors
+ .created_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ deleted: colors
+ .deleted
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ deleted_background: colors
+ .deleted_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ deleted_border: colors
+ .deleted_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ error: colors
+ .error
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ error_background: colors
+ .error_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ error_border: colors
+ .error_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hidden: colors
+ .hidden
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hidden_background: colors
+ .hidden_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hidden_border: colors
+ .hidden_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hint: colors
+ .hint
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hint_background: colors
+ .hint_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ hint_border: colors
+ .hint_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ ignored: colors
+ .ignored
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ ignored_background: colors
+ .ignored_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ ignored_border: colors
+ .ignored_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ info: colors
+ .info
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ info_background: colors
+ .info_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ info_border: colors
+ .info_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ modified: colors
+ .modified
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ modified_background: colors
+ .modified_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ modified_border: colors
+ .modified_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ predictive: colors
+ .predictive
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ predictive_background: colors
+ .predictive_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ predictive_border: colors
+ .predictive_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ renamed: colors
+ .renamed
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ renamed_background: colors
+ .renamed_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ renamed_border: colors
+ .renamed_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ success: colors
+ .success
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ success_background: colors
+ .success_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ success_border: colors
+ .success_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ unreachable: colors
+ .unreachable
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ unreachable_background: colors
+ .unreachable_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ unreachable_border: colors
+ .unreachable_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ warning: colors
+ .warning
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ warning_background: colors
+ .warning_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
+ warning_border: colors
+ .warning_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok()),
}
}
@@ -1,22 +1,25 @@
use crate::fallback_themes::zed_default_dark;
use crate::{
Appearance, DEFAULT_ICON_THEME_NAME, IconTheme, IconThemeNotFoundError, SyntaxTheme, Theme,
- ThemeNotFoundError, ThemeRegistry, ThemeStyleContent,
+ ThemeColorsRefinement, ThemeNotFoundError, ThemeRegistry, ThemeStyleContent,
+ status_colors_refinement, syntax_overrides, theme_colors_refinement,
};
-use anyhow::Result;
use collections::HashMap;
use derive_more::{Deref, DerefMut};
use gpui::{
- App, Context, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels,
- SharedString, Subscription, Window, px,
+ App, Context, Font, FontFallbacks, FontStyle, FontWeight, Global, Pixels, Subscription, Window,
+ px,
};
use refineable::Refineable;
use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
-use settings::{ParameterizedJsonSchema, Settings, SettingsKey, SettingsSources, SettingsUi};
+use settings::{
+ FontFamilyName, IconThemeName, ParameterizedJsonSchema, Settings, SettingsContent, ThemeMode,
+ ThemeName,
+};
use std::sync::Arc;
-use util::ResultExt as _;
use util::schemars::replace_subschema;
+use util::{MergeFrom, ResultExt as _};
const MIN_FONT_SIZE: Pixels = px(6.0);
const MAX_FONT_SIZE: Pixels = px(100.0);
@@ -119,16 +122,16 @@ pub struct ThemeSettings {
/// Manual overrides for the active theme.
///
/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
- pub experimental_theme_overrides: Option<ThemeStyleContent>,
+ pub experimental_theme_overrides: Option<settings::ThemeStyleContent>,
/// Manual overrides per theme
- pub theme_overrides: HashMap<String, ThemeStyleContent>,
+ pub theme_overrides: HashMap<String, settings::ThemeStyleContent>,
/// The current icon theme selection.
pub icon_theme_selection: Option<IconThemeSelection>,
/// The active icon theme.
pub active_icon_theme: Arc<IconTheme>,
/// The density of the UI.
/// Note: This setting is still experimental. See [this tracking issue](
- pub ui_density: UiDensity,
+ pub ui_density: settings::UiDensity,
/// The amount of fading applied to unnecessary code.
pub unnecessary_code_fade: f32,
}
@@ -277,24 +280,15 @@ pub enum ThemeSelection {
},
}
-// TODO: Rename ThemeMode -> ThemeAppearanceMode
-/// The mode use to select a theme.
-///
-/// `Light` and `Dark` will select their respective themes.
-///
-/// `System` will select the theme based on the system's appearance.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum ThemeMode {
- /// Use the specified `light` theme.
- Light,
-
- /// Use the specified `dark` theme.
- Dark,
-
- /// Use the theme based on the system's appearance.
- #[default]
- System,
+impl From<settings::ThemeSelection> for ThemeSelection {
+ fn from(selection: settings::ThemeSelection) -> Self {
+ match selection {
+ settings::ThemeSelection::Static(theme) => ThemeSelection::Static(theme),
+ settings::ThemeSelection::Dynamic { mode, light, dark } => {
+ ThemeSelection::Dynamic { mode, light, dark }
+ }
+ }
+ }
}
impl ThemeSelection {
@@ -323,15 +317,13 @@ impl ThemeSelection {
}
/// Represents the selection of an icon theme, which can be either static or dynamic.
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(untagged)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IconThemeSelection {
/// A static icon theme selection, represented by a single icon theme name.
Static(IconThemeName),
/// A dynamic icon theme selection, which can change based on the [`ThemeMode`].
Dynamic {
/// The mode used to determine which theme to use.
- #[serde(default)]
mode: ThemeMode,
/// The icon theme to use for light mode.
light: IconThemeName,
@@ -340,6 +332,17 @@ pub enum IconThemeSelection {
},
}
+impl From<settings::IconThemeSelection> for IconThemeSelection {
+ fn from(selection: settings::IconThemeSelection) -> Self {
+ match selection {
+ settings::IconThemeSelection::Static(theme) => IconThemeSelection::Static(theme),
+ settings::IconThemeSelection::Dynamic { mode, light, dark } => {
+ IconThemeSelection::Dynamic { mode, light, dark }
+ }
+ }
+ }
+}
+
impl IconThemeSelection {
/// Returns the icon theme name based on the given [`Appearance`].
pub fn icon_theme(&self, system_appearance: Appearance) -> &str {
@@ -365,189 +368,105 @@ impl IconThemeSelection {
}
}
-/// Settings for rendering text in UI and text buffers.
-#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)]
-#[settings_key(None)]
-pub struct ThemeSettingsContent {
- /// The default font size for text in the UI.
- #[serde(default)]
- pub ui_font_size: Option<f32>,
- /// The name of a font to use for rendering in the UI.
- #[serde(default)]
- pub ui_font_family: Option<FontFamilyName>,
- /// The font fallbacks to use for rendering in the UI.
- #[serde(default)]
- #[schemars(default = "default_font_fallbacks")]
- #[schemars(extend("uniqueItems" = true))]
- pub ui_font_fallbacks: Option<Vec<FontFamilyName>>,
- /// The OpenType features to enable for text in the UI.
- #[serde(default)]
- #[schemars(default = "default_font_features")]
- pub ui_font_features: Option<FontFeatures>,
- /// The weight of the UI font in CSS units from 100 to 900.
- #[serde(default)]
- pub ui_font_weight: Option<f32>,
- /// The name of a font to use for rendering in text buffers.
- #[serde(default)]
- pub buffer_font_family: Option<FontFamilyName>,
- /// The font fallbacks to use for rendering in text buffers.
- #[serde(default)]
- #[schemars(extend("uniqueItems" = true))]
- pub buffer_font_fallbacks: Option<Vec<FontFamilyName>>,
- /// The default font size for rendering in text buffers.
- #[serde(default)]
- pub buffer_font_size: Option<f32>,
- /// The weight of the editor font in CSS units from 100 to 900.
- #[serde(default)]
- pub buffer_font_weight: Option<f32>,
- /// The buffer's line height.
- #[serde(default)]
- pub buffer_line_height: Option<BufferLineHeight>,
- /// The OpenType features to enable for rendering in text buffers.
- #[serde(default)]
- #[schemars(default = "default_font_features")]
- pub buffer_font_features: Option<FontFeatures>,
- /// The font size for the agent panel. Falls back to the UI font size if unset.
- #[serde(default)]
- pub agent_font_size: Option<Option<f32>>,
- /// The name of the Zed theme to use.
- #[serde(default)]
- pub theme: Option<ThemeSelection>,
- /// The name of the icon theme to use.
- #[serde(default)]
- pub icon_theme: Option<IconThemeSelection>,
-
- /// UNSTABLE: Expect many elements to be broken.
- ///
- // Controls the density of the UI.
- #[serde(rename = "unstable.ui_density", default)]
- pub ui_density: Option<UiDensity>,
-
- /// How much to fade out unused code.
- #[serde(default)]
- pub unnecessary_code_fade: Option<f32>,
-
- /// 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 experimental_theme_overrides: Option<ThemeStyleContent>,
-
- /// Overrides per theme
- ///
- /// These values will override the ones on the specified theme
- #[serde(default)]
- pub theme_overrides: HashMap<String, ThemeStyleContent>,
-}
-
-fn default_font_features() -> Option<FontFeatures> {
- Some(FontFeatures::default())
-}
-
-fn default_font_fallbacks() -> Option<FontFallbacks> {
- Some(FontFallbacks::default())
-}
-
-impl ThemeSettingsContent {
- /// Sets the theme for the given appearance to the theme with the specified name.
- pub fn set_theme(&mut self, theme_name: impl Into<Arc<str>>, appearance: Appearance) {
- if let Some(selection) = self.theme.as_mut() {
- let theme_to_update = match selection {
- ThemeSelection::Static(theme) => theme,
- ThemeSelection::Dynamic { mode, light, dark } => match mode {
- ThemeMode::Light => light,
- ThemeMode::Dark => dark,
- ThemeMode::System => match appearance {
- Appearance::Light => light,
- Appearance::Dark => dark,
- },
- },
- };
-
- *theme_to_update = ThemeName(theme_name.into());
- } else {
- self.theme = Some(ThemeSelection::Static(ThemeName(theme_name.into())));
- }
- }
-
- /// Sets the icon theme for the given appearance to the icon theme with the specified name.
- pub fn set_icon_theme(&mut self, icon_theme_name: String, appearance: Appearance) {
- if let Some(selection) = self.icon_theme.as_mut() {
- let icon_theme_to_update = match selection {
- IconThemeSelection::Static(theme) => theme,
- IconThemeSelection::Dynamic { mode, light, dark } => match mode {
- ThemeMode::Light => light,
- ThemeMode::Dark => dark,
- ThemeMode::System => match appearance {
- Appearance::Light => light,
- Appearance::Dark => dark,
- },
- },
- };
-
- *icon_theme_to_update = IconThemeName(icon_theme_name.into());
- } else {
- self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
- icon_theme_name.into(),
- )));
- }
- }
-
- /// Sets the mode for the theme.
- pub fn set_mode(&mut self, mode: ThemeMode) {
- if let Some(selection) = self.theme.as_mut() {
- match selection {
- ThemeSelection::Static(theme) => {
- // If the theme was previously set to a single static theme,
- // we don't know whether it was a light or dark theme, so we
- // just use it for both.
- self.theme = Some(ThemeSelection::Dynamic {
- mode,
- light: theme.clone(),
- dark: theme.clone(),
- });
- }
- ThemeSelection::Dynamic {
- mode: mode_to_update,
- ..
- } => *mode_to_update = mode,
- }
- } else {
- self.theme = Some(ThemeSelection::Dynamic {
- mode,
- light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()),
- dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()),
- });
- }
-
- if let Some(selection) = self.icon_theme.as_mut() {
- match selection {
- IconThemeSelection::Static(icon_theme) => {
- // If the icon theme was previously set to a single static
- // theme, we don't know whether it was a light or dark
- // theme, so we just use it for both.
- self.icon_theme = Some(IconThemeSelection::Dynamic {
- mode,
- light: icon_theme.clone(),
- dark: icon_theme.clone(),
- });
- }
- IconThemeSelection::Dynamic {
- mode: mode_to_update,
- ..
- } => *mode_to_update = mode,
- }
- } else {
- self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
- DEFAULT_ICON_THEME_NAME.into(),
- )));
- }
- }
-}
+// impl ThemeSettingsContent {
+// /// Sets the theme for the given appearance to the theme with the specified name.
+// pub fn set_theme(&mut self, theme_name: impl Into<Arc<str>>, appearance: Appearance) {
+// if let Some(selection) = self.theme.as_mut() {
+// let theme_to_update = match selection {
+// ThemeSelection::Static(theme) => theme,
+// ThemeSelection::Dynamic { mode, light, dark } => match mode {
+// ThemeMode::Light => light,
+// ThemeMode::Dark => dark,
+// ThemeMode::System => match appearance {
+// Appearance::Light => light,
+// Appearance::Dark => dark,
+// },
+// },
+// };
+
+// *theme_to_update = ThemeName(theme_name.into());
+// } else {
+// self.theme = Some(ThemeSelection::Static(ThemeName(theme_name.into())));
+// }
+// }
+
+// /// Sets the icon theme for the given appearance to the icon theme with the specified name.
+// pub fn set_icon_theme(&mut self, icon_theme_name: String, appearance: Appearance) {
+// if let Some(selection) = self.icon_theme.as_mut() {
+// let icon_theme_to_update = match selection {
+// IconThemeSelection::Static(theme) => theme,
+// IconThemeSelection::Dynamic { mode, light, dark } => match mode {
+// ThemeMode::Light => light,
+// ThemeMode::Dark => dark,
+// ThemeMode::System => match appearance {
+// Appearance::Light => light,
+// Appearance::Dark => dark,
+// },
+// },
+// };
+
+// *icon_theme_to_update = IconThemeName(icon_theme_name.into());
+// } else {
+// self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
+// icon_theme_name.into(),
+// )));
+// }
+// }
+
+// /// Sets the mode for the theme.
+// pub fn set_mode(&mut self, mode: ThemeMode) {
+// if let Some(selection) = self.theme.as_mut() {
+// match selection {
+// ThemeSelection::Static(theme) => {
+// // If the theme was previously set to a single static theme,
+// // we don't know whether it was a light or dark theme, so we
+// // just use it for both.
+// self.theme = Some(ThemeSelection::Dynamic {
+// mode,
+// light: theme.clone(),
+// dark: theme.clone(),
+// });
+// }
+// ThemeSelection::Dynamic {
+// mode: mode_to_update,
+// ..
+// } => *mode_to_update = mode,
+// }
+// } else {
+// self.theme = Some(ThemeSelection::Dynamic {
+// mode,
+// light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()),
+// dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()),
+// });
+// }
+
+// if let Some(selection) = self.icon_theme.as_mut() {
+// match selection {
+// IconThemeSelection::Static(icon_theme) => {
+// // If the icon theme was previously set to a single static
+// // theme, we don't know whether it was a light or dark
+// // theme, so we just use it for both.
+// self.icon_theme = Some(IconThemeSelection::Dynamic {
+// mode,
+// light: icon_theme.clone(),
+// dark: icon_theme.clone(),
+// });
+// }
+// IconThemeSelection::Dynamic {
+// mode: mode_to_update,
+// ..
+// } => *mode_to_update = mode,
+// }
+// } else {
+// self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
+// DEFAULT_ICON_THEME_NAME.into(),
+// )));
+// }
+// }
+// }
/// The buffer's line height.
-#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
-#[serde(rename_all = "snake_case")]
+#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum BufferLineHeight {
/// A less dense line height.
#[default]
@@ -555,21 +474,19 @@ pub enum BufferLineHeight {
/// The default line height.
Standard,
/// A custom line height, where 1.0 is the font's height. Must be at least 1.0.
- Custom(#[serde(deserialize_with = "deserialize_line_height")] f32),
+ Custom(f32),
}
-fn deserialize_line_height<'de, D>(deserializer: D) -> Result<f32, D::Error>
-where
- D: serde::Deserializer<'de>,
-{
- let value = f32::deserialize(deserializer)?;
- if value < 1.0 {
- return Err(serde::de::Error::custom(
- "buffer_line_height.custom must be at least 1.0",
- ));
+impl From<settings::BufferLineHeight> for BufferLineHeight {
+ fn from(value: settings::BufferLineHeight) -> Self {
+ match value {
+ settings::BufferLineHeight::Comfortable => BufferLineHeight::Comfortable,
+ settings::BufferLineHeight::Standard => BufferLineHeight::Standard,
+ settings::BufferLineHeight::Custom(line_height) => {
+ BufferLineHeight::Custom(line_height)
+ }
+ }
}
-
- Ok(value)
}
impl BufferLineHeight {
@@ -681,24 +598,22 @@ impl ThemeSettings {
}
}
- fn modify_theme(base_theme: &mut Theme, theme_overrides: &ThemeStyleContent) {
+ fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) {
if let Some(window_background_appearance) = theme_overrides.window_background_appearance {
base_theme.styles.window_background_appearance = window_background_appearance.into();
}
+ let status_color_refinement = status_colors_refinement(&theme_overrides.status);
- base_theme
- .styles
- .colors
- .refine(&theme_overrides.theme_colors_refinement());
- base_theme
- .styles
- .status
- .refine(&theme_overrides.status_colors_refinement());
+ base_theme.styles.colors.refine(&theme_colors_refinement(
+ &theme_overrides.colors,
+ &status_color_refinement,
+ ));
+ base_theme.styles.status.refine(&status_color_refinement);
base_theme.styles.player.merge(&theme_overrides.players);
base_theme.styles.accents.merge(&theme_overrides.accents);
base_theme.styles.syntax = SyntaxTheme::merge(
base_theme.styles.syntax.clone(),
- theme_overrides.syntax_overrides(),
+ syntax_overrides(&theme_overrides),
);
}
@@ -818,292 +733,170 @@ fn clamp_font_weight(weight: f32) -> FontWeight {
FontWeight(weight.clamp(100., 950.))
}
+fn font_fallbacks_from_settings(
+ fallbacks: Option<Vec<settings::FontFamilyName>>,
+) -> Option<FontFallbacks> {
+ fallbacks.map(|fallbacks| {
+ FontFallbacks::from_fonts(
+ fallbacks
+ .into_iter()
+ .map(|font_family| font_family.0.to_string())
+ .collect(),
+ )
+ })
+}
+
impl settings::Settings for ThemeSettings {
- fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self> {
+ fn from_file(content: &settings::SettingsContent, cx: &mut App) -> Option<Self> {
+ let content = &content.theme;
let themes = ThemeRegistry::default_global(cx);
let system_appearance = SystemAppearance::default_global(cx);
-
- fn font_fallbacks_from_settings(
- fallbacks: Option<Vec<FontFamilyName>>,
- ) -> Option<FontFallbacks> {
- fallbacks.map(|fallbacks| {
- FontFallbacks::from_fonts(
- fallbacks
- .into_iter()
- .map(|font_family| font_family.0.to_string())
- .collect(),
- )
- })
- }
-
- let defaults = sources.default;
- let mut this = Self {
- ui_font_size: defaults.ui_font_size.unwrap().into(),
+ let theme_selection: ThemeSelection = content.theme.clone()?.into();
+ let icon_theme_selection: IconThemeSelection = content.icon_theme.clone()?.into();
+ let this = Self {
+ ui_font_size: content.ui_font_size?.into(),
ui_font: Font {
- family: defaults.ui_font_family.as_ref().unwrap().0.clone().into(),
- features: defaults.ui_font_features.clone().unwrap(),
- fallbacks: font_fallbacks_from_settings(defaults.ui_font_fallbacks.clone()),
- weight: defaults.ui_font_weight.map(FontWeight).unwrap(),
+ family: content.ui_font_family.as_ref()?.0.clone().into(),
+ features: content.ui_font_features.clone()?,
+ fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()),
+ weight: content.ui_font_weight.map(FontWeight)?,
style: Default::default(),
},
buffer_font: Font {
- family: defaults
- .buffer_font_family
- .as_ref()
- .unwrap()
- .0
- .clone()
- .into(),
- features: defaults.buffer_font_features.clone().unwrap(),
- fallbacks: font_fallbacks_from_settings(defaults.buffer_font_fallbacks.clone()),
- weight: defaults.buffer_font_weight.map(FontWeight).unwrap(),
+ family: content.buffer_font_family.as_ref()?.0.clone().into(),
+ features: content.buffer_font_features.clone()?,
+ fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()),
+ weight: content.buffer_font_weight.map(FontWeight)?,
style: FontStyle::default(),
},
- buffer_font_size: defaults.buffer_font_size.unwrap().into(),
- buffer_line_height: defaults.buffer_line_height.unwrap(),
- agent_font_size: defaults.agent_font_size.flatten().map(Into::into),
- theme_selection: defaults.theme.clone(),
+ buffer_font_size: content.buffer_font_size?.into(),
+ buffer_line_height: content.buffer_line_height?.into(),
+ agent_font_size: content.agent_font_size.flatten().map(Into::into),
active_theme: themes
- .get(defaults.theme.as_ref().unwrap().theme(*system_appearance))
+ .get(theme_selection.theme(*system_appearance))
.or(themes.get(&zed_default_dark().name))
.unwrap(),
+ theme_selection: Some(theme_selection),
experimental_theme_overrides: None,
theme_overrides: HashMap::default(),
- icon_theme_selection: defaults.icon_theme.clone(),
- active_icon_theme: defaults
- .icon_theme
- .as_ref()
- .and_then(|selection| {
- themes
- .get_icon_theme(selection.icon_theme(*system_appearance))
- .ok()
- })
- .unwrap_or_else(|| themes.get_icon_theme(DEFAULT_ICON_THEME_NAME).unwrap()),
- ui_density: defaults.ui_density.unwrap_or(UiDensity::Default),
- unnecessary_code_fade: defaults.unnecessary_code_fade.unwrap_or(0.0),
+ active_icon_theme: themes
+ .get_icon_theme(icon_theme_selection.icon_theme(*system_appearance))
+ .ok()?,
+ icon_theme_selection: Some(icon_theme_selection),
+ ui_density: content.ui_density?,
+ unnecessary_code_fade: content.unnecessary_code_fade?,
};
- for value in sources
- .user
- .into_iter()
- .chain(sources.release_channel)
- .chain(sources.operating_system)
- .chain(sources.profile)
- .chain(sources.server)
- {
- if let Some(value) = value.ui_density {
- this.ui_density = value;
- }
-
- if let Some(value) = value.buffer_font_family.clone() {
- this.buffer_font.family = value.0.into();
- }
- if let Some(value) = value.buffer_font_features.clone() {
- this.buffer_font.features = value;
- }
- if let Some(value) = value.buffer_font_fallbacks.clone() {
- this.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value));
- }
- if let Some(value) = value.buffer_font_weight {
- this.buffer_font.weight = clamp_font_weight(value);
- }
+ Some(this)
+ }
- if let Some(value) = value.ui_font_family.clone() {
- this.ui_font.family = value.0.into();
- }
- if let Some(value) = value.ui_font_features.clone() {
- this.ui_font.features = value;
- }
- if let Some(value) = value.ui_font_fallbacks.clone() {
- this.ui_font.fallbacks = font_fallbacks_from_settings(Some(value));
- }
- if let Some(value) = value.ui_font_weight {
- this.ui_font.weight = clamp_font_weight(value);
- }
+ fn refine(&mut self, content: &SettingsContent, cx: &mut App) {
+ let value = &content.theme;
- if let Some(value) = &value.theme {
- this.theme_selection = Some(value.clone());
+ let themes = ThemeRegistry::default_global(cx);
+ let system_appearance = SystemAppearance::default_global(cx);
- let theme_name = value.theme(*system_appearance);
+ self.ui_density.merge_from(&value.ui_density);
- match themes.get(theme_name) {
- Ok(theme) => {
- this.active_theme = theme;
- }
- Err(err @ ThemeNotFoundError(_)) => {
- if themes.extensions_loaded() {
- log::error!("{err}");
- }
- }
- }
- }
+ if let Some(value) = value.buffer_font_family.clone() {
+ self.buffer_font.family = value.0.into();
+ }
+ if let Some(value) = value.buffer_font_features.clone() {
+ self.buffer_font.features = value;
+ }
+ if let Some(value) = value.buffer_font_fallbacks.clone() {
+ self.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value));
+ }
+ if let Some(value) = value.buffer_font_weight {
+ self.buffer_font.weight = clamp_font_weight(value);
+ }
- this.experimental_theme_overrides
- .clone_from(&value.experimental_theme_overrides);
- this.theme_overrides.clone_from(&value.theme_overrides);
- this.apply_theme_overrides();
+ if let Some(value) = value.ui_font_family.clone() {
+ self.ui_font.family = value.0.into();
+ }
+ if let Some(value) = value.ui_font_features.clone() {
+ self.ui_font.features = value;
+ }
+ if let Some(value) = value.ui_font_fallbacks.clone() {
+ self.ui_font.fallbacks = font_fallbacks_from_settings(Some(value));
+ }
+ if let Some(value) = value.ui_font_weight {
+ self.ui_font.weight = clamp_font_weight(value);
+ }
- if let Some(value) = &value.icon_theme {
- this.icon_theme_selection = Some(value.clone());
+ if let Some(value) = &value.theme {
+ self.theme_selection = Some(value.clone().into());
- let icon_theme_name = value.icon_theme(*system_appearance);
+ let theme_name = self
+ .theme_selection
+ .as_ref()
+ .unwrap()
+ .theme(*system_appearance);
- match themes.get_icon_theme(icon_theme_name) {
- Ok(icon_theme) => {
- this.active_icon_theme = icon_theme;
- }
- Err(err @ IconThemeNotFoundError(_)) => {
- if themes.extensions_loaded() {
- log::error!("{err}");
- }
+ match themes.get(theme_name) {
+ Ok(theme) => {
+ self.active_theme = theme;
+ }
+ Err(err @ ThemeNotFoundError(_)) => {
+ if themes.extensions_loaded() {
+ log::error!("{err}");
}
}
}
-
- merge(
- &mut this.ui_font_size,
- value.ui_font_size.map(Into::into).map(clamp_font_size),
- );
- merge(
- &mut this.buffer_font_size,
- value.buffer_font_size.map(Into::into).map(clamp_font_size),
- );
- merge(
- &mut this.agent_font_size,
- value
- .agent_font_size
- .map(|value| value.map(Into::into).map(clamp_font_size)),
- );
-
- merge(&mut this.buffer_line_height, value.buffer_line_height);
-
- // Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely.
- merge(&mut this.unnecessary_code_fade, value.unnecessary_code_fade);
- this.unnecessary_code_fade = this.unnecessary_code_fade.clamp(0.0, 0.9);
}
- Ok(this)
- }
+ self.experimental_theme_overrides
+ .clone_from(&value.experimental_theme_overrides);
+ self.theme_overrides.clone_from(&value.theme_overrides);
- fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
- vscode.f32_setting("editor.fontWeight", &mut current.buffer_font_weight);
- vscode.f32_setting("editor.fontSize", &mut current.buffer_font_size);
- if let Some(font) = vscode.read_string("editor.font") {
- current.buffer_font_family = Some(FontFamilyName(font.into()));
- }
- // TODO: possibly map editor.fontLigatures to buffer_font_features?
- }
-}
+ self.apply_theme_overrides();
-/// Newtype for a theme name. Its `ParameterizedJsonSchema` lists the theme names known at runtime.
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(transparent)]
-pub struct ThemeName(pub Arc<str>);
-
-inventory::submit! {
- ParameterizedJsonSchema {
- add_and_get_ref: |generator, _params, cx| {
- replace_subschema::<ThemeName>(generator, || json_schema!({
- "type": "string",
- "enum": ThemeRegistry::global(cx).list_names(),
- }))
- }
- }
-}
+ if let Some(value) = &value.icon_theme {
+ self.icon_theme_selection = Some(value.clone().into());
-/// Newtype for a icon theme name. Its `ParameterizedJsonSchema` lists the icon theme names known at
-/// runtime.
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(transparent)]
-pub struct IconThemeName(pub Arc<str>);
-
-inventory::submit! {
- ParameterizedJsonSchema {
- add_and_get_ref: |generator, _params, cx| {
- replace_subschema::<IconThemeName>(generator, || json_schema!({
- "type": "string",
- "enum": ThemeRegistry::global(cx)
- .list_icon_themes()
- .into_iter()
- .map(|icon_theme| icon_theme.name)
- .collect::<Vec<SharedString>>(),
- }))
- }
- }
-}
+ let icon_theme_name = self
+ .icon_theme_selection
+ .as_ref()
+ .unwrap()
+ .icon_theme(*system_appearance);
-/// Newtype for font family name. Its `ParameterizedJsonSchema` lists the font families known at
-/// runtime.
-#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(transparent)]
-pub struct FontFamilyName(pub Arc<str>);
-
-inventory::submit! {
- ParameterizedJsonSchema {
- add_and_get_ref: |generator, params, _cx| {
- replace_subschema::<FontFamilyName>(generator, || {
- json_schema!({
- "type": "string",
- "enum": params.font_names,
- })
- })
+ match themes.get_icon_theme(icon_theme_name) {
+ Ok(icon_theme) => {
+ self.active_icon_theme = icon_theme;
+ }
+ Err(err @ IconThemeNotFoundError(_)) => {
+ if themes.extensions_loaded() {
+ log::error!("{err}");
+ }
+ }
+ }
}
- }
-}
-fn merge<T: Copy>(target: &mut T, value: Option<T>) {
- if let Some(value) = value {
- *target = value;
- }
-}
+ self.ui_font_size
+ .merge_from(&value.ui_font_size.map(Into::into).map(clamp_font_size));
+ self.buffer_font_size
+ .merge_from(&value.buffer_font_size.map(Into::into).map(clamp_font_size));
+ self.agent_font_size.merge_from(
+ &value
+ .agent_font_size
+ .map(|value| value.map(Into::into).map(clamp_font_size)),
+ );
-#[cfg(test)]
-mod tests {
- use super::*;
- use serde_json::json;
+ self.buffer_line_height
+ .merge_from(&value.buffer_line_height.map(Into::into));
- #[test]
- fn test_buffer_line_height_deserialize_valid() {
- assert_eq!(
- serde_json::from_value::<BufferLineHeight>(json!("comfortable")).unwrap(),
- BufferLineHeight::Comfortable
- );
- assert_eq!(
- serde_json::from_value::<BufferLineHeight>(json!("standard")).unwrap(),
- BufferLineHeight::Standard
- );
- assert_eq!(
- serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.0})).unwrap(),
- BufferLineHeight::Custom(1.0)
- );
- assert_eq!(
- serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.5})).unwrap(),
- BufferLineHeight::Custom(1.5)
- );
+ // Clamp the `unnecessary_code_fade` to ensure text can't disappear entirely.
+ self.unnecessary_code_fade
+ .merge_from(&value.unnecessary_code_fade);
+ self.unnecessary_code_fade = self.unnecessary_code_fade.clamp(0.0, 0.9);
}
- #[test]
- fn test_buffer_line_height_deserialize_invalid() {
- assert!(
- serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.99}))
- .err()
- .unwrap()
- .to_string()
- .contains("buffer_line_height.custom must be at least 1.0")
- );
- assert!(
- serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.0}))
- .err()
- .unwrap()
- .to_string()
- .contains("buffer_line_height.custom must be at least 1.0")
- );
- assert!(
- serde_json::from_value::<BufferLineHeight>(json!({"custom": -1.0}))
- .err()
- .unwrap()
- .to_string()
- .contains("buffer_line_height.custom must be at least 1.0")
- );
+ fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {
+ vscode.f32_setting("editor.fontWeight", &mut current.theme.buffer_font_weight);
+ vscode.f32_setting("editor.fontSize", &mut current.theme.buffer_font_size);
+ if let Some(font) = vscode.read_string("editor.font") {
+ current.theme.buffer_font_family = Some(FontFamilyName(font.into()));
+ }
+ // TODO: possibly map editor.fontLigatures to buffer_font_features?
}
}
@@ -66,7 +66,7 @@ impl AccentColors {
}
/// Merges the given accent colors into this [`AccentColors`] instance.
- pub fn merge(&mut self, accent_colors: &[AccentContent]) {
+ pub fn merge(&mut self, accent_colors: &[settings::AccentContent]) {
if accent_colors.is_empty() {
return;
}
@@ -152,7 +152,7 @@ impl PlayerColors {
}
/// Merges the given player colors into this [`PlayerColors`] instance.
- pub fn merge(&mut self, user_player_colors: &[PlayerColorContent]) {
+ pub fn merge(&mut self, user_player_colors: &[settings::PlayerColorContent]) {
if user_player_colors.is_empty() {
return;
}
@@ -178,7 +178,7 @@ impl ThemeFamily {
AppearanceContent::Light => StatusColors::light(),
AppearanceContent::Dark => StatusColors::dark(),
};
- let mut status_colors_refinement = theme.style.status_colors_refinement();
+ let mut status_colors_refinement = status_colors_refinement(&theme.style.status);
apply_status_color_defaults(&mut status_colors_refinement);
refined_status_colors.refine(&status_colors_refinement);
@@ -192,7 +192,8 @@ impl ThemeFamily {
AppearanceContent::Light => ThemeColors::light(),
AppearanceContent::Dark => ThemeColors::dark(),
};
- let mut theme_colors_refinement = theme.style.theme_colors_refinement();
+ let mut theme_colors_refinement =
+ theme_colors_refinement(&theme.style.colors, &status_colors_refinement);
apply_theme_color_defaults(&mut theme_colors_refinement, &refined_player_colors);
refined_theme_colors.refine(&theme_colors_refinement);
@@ -38,7 +38,7 @@ impl Settings for TitleBarSettings {
}
}
- fn refine(&mut self, s: &SettingsContent) {
+ fn refine(&mut self, s: &SettingsContent, _: &mut App) {
let Some(content) = s.title_bar else {
return
}
@@ -1388,12 +1388,12 @@ pub fn refine<T: Clone>(dest: &mut T, src: &Option<T>) {
}
}
-pub trait Refine: Sized + Clone {
- fn refine(&mut self, src: &Option<Self>);
+pub trait MergeFrom: Sized + Clone {
+ fn merge_from(&mut self, src: &Option<Self>);
}
-impl<T: Clone> Refine for T {
- fn refine(&mut self, src: &Option<Self>) {
+impl<T: Clone> MergeFrom for T {
+ fn merge_from(&mut self, src: &Option<Self>) {
if let Some(src) = src {
*self = src.clone();
}