1use collections::{HashMap, IndexMap};
2use schemars::JsonSchema;
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_json::Value;
5use settings_macros::{MergeFrom, with_fallible_options};
6use std::{borrow::Cow, fmt::Display, sync::Arc};
7
8use crate::serialize_f32_with_two_decimal_places;
9
10/// OpenType font features as a map of feature tag to value.
11/// This is a content type that mirrors `gpui::FontFeatures` but without the Arc wrapper.
12/// Values can be specified as booleans (true=1, false=0) or integers.
13#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, MergeFrom)]
14#[serde(transparent)]
15pub struct FontFeaturesContent(pub IndexMap<String, u32>);
16
17impl FontFeaturesContent {
18 pub fn new() -> Self {
19 Self(IndexMap::default())
20 }
21}
22
23#[derive(Debug, serde::Deserialize)]
24#[serde(untagged)]
25enum FeatureValue {
26 Bool(bool),
27 Number(serde_json::Number),
28}
29
30fn is_valid_feature_tag(tag: &str) -> bool {
31 tag.len() == 4 && tag.chars().all(|c| c.is_ascii_alphanumeric())
32}
33
34impl<'de> Deserialize<'de> for FontFeaturesContent {
35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
36 where
37 D: Deserializer<'de>,
38 {
39 use serde::de::{MapAccess, Visitor};
40 use std::fmt;
41
42 struct FontFeaturesVisitor;
43
44 impl<'de> Visitor<'de> for FontFeaturesVisitor {
45 type Value = FontFeaturesContent;
46
47 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
48 formatter.write_str("a map of font features")
49 }
50
51 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
52 where
53 M: MapAccess<'de>,
54 {
55 let mut feature_map = IndexMap::default();
56
57 while let Some((key, value)) =
58 access.next_entry::<String, Option<FeatureValue>>()?
59 {
60 if !is_valid_feature_tag(&key) {
61 log::error!("Incorrect font feature tag: {}", key);
62 continue;
63 }
64 if let Some(value) = value {
65 match value {
66 FeatureValue::Bool(enable) => {
67 feature_map.insert(key, if enable { 1 } else { 0 });
68 }
69 FeatureValue::Number(value) => {
70 if value.is_u64() {
71 feature_map.insert(key, value.as_u64().unwrap() as u32);
72 } else {
73 log::error!(
74 "Incorrect font feature value {} for feature tag {}",
75 value,
76 key
77 );
78 continue;
79 }
80 }
81 }
82 }
83 }
84
85 Ok(FontFeaturesContent(feature_map))
86 }
87 }
88
89 deserializer.deserialize_map(FontFeaturesVisitor)
90 }
91}
92
93impl JsonSchema for FontFeaturesContent {
94 fn schema_name() -> Cow<'static, str> {
95 "FontFeaturesContent".into()
96 }
97
98 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
99 use schemars::json_schema;
100 json_schema!({
101 "type": "object",
102 "patternProperties": {
103 "[0-9a-zA-Z]{4}$": {
104 "type": ["boolean", "integer"],
105 "minimum": 0,
106 "multipleOf": 1
107 }
108 },
109 "additionalProperties": false
110 })
111 }
112}
113
114/// Settings for rendering text in UI and text buffers.
115
116#[with_fallible_options]
117#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema, MergeFrom)]
118pub struct ThemeSettingsContent {
119 /// The default font size for text in the UI.
120 pub ui_font_size: Option<FontSize>,
121 /// The name of a font to use for rendering in the UI.
122 pub ui_font_family: Option<FontFamilyName>,
123 /// The font fallbacks to use for rendering in the UI.
124 #[schemars(default = "default_font_fallbacks")]
125 #[schemars(extend("uniqueItems" = true))]
126 pub ui_font_fallbacks: Option<Vec<FontFamilyName>>,
127 /// The OpenType features to enable for text in the UI.
128 #[schemars(default = "default_font_features")]
129 pub ui_font_features: Option<FontFeaturesContent>,
130 /// The weight of the UI font in CSS units from 100 to 900.
131 #[schemars(default = "default_buffer_font_weight")]
132 pub ui_font_weight: Option<FontWeightContent>,
133 /// The name of a font to use for rendering in text buffers.
134 pub buffer_font_family: Option<FontFamilyName>,
135 /// The font fallbacks to use for rendering in text buffers.
136 #[schemars(extend("uniqueItems" = true))]
137 pub buffer_font_fallbacks: Option<Vec<FontFamilyName>>,
138 /// The default font size for rendering in text buffers.
139 pub buffer_font_size: Option<FontSize>,
140 /// The weight of the editor font in CSS units from 100 to 900.
141 #[schemars(default = "default_buffer_font_weight")]
142 pub buffer_font_weight: Option<FontWeightContent>,
143 /// The buffer's line height.
144 pub buffer_line_height: Option<BufferLineHeight>,
145 /// The OpenType features to enable for rendering in text buffers.
146 #[schemars(default = "default_font_features")]
147 pub buffer_font_features: Option<FontFeaturesContent>,
148 /// The font size for agent responses in the agent panel. Falls back to the UI font size if unset.
149 pub agent_ui_font_size: Option<FontSize>,
150 /// The font size for user messages in the agent panel.
151 pub agent_buffer_font_size: Option<FontSize>,
152 /// The name of the Zed theme to use.
153 pub theme: Option<ThemeSelection>,
154 /// The name of the icon theme to use.
155 pub icon_theme: Option<IconThemeSelection>,
156
157 /// UNSTABLE: Expect many elements to be broken.
158 ///
159 // Controls the density of the UI.
160 #[serde(rename = "unstable.ui_density")]
161 pub ui_density: Option<UiDensity>,
162
163 /// How much to fade out unused code.
164 #[schemars(range(min = 0.0, max = 0.9))]
165 pub unnecessary_code_fade: Option<CodeFade>,
166
167 /// EXPERIMENTAL: Overrides for the current theme.
168 ///
169 /// These values will override the ones on the current theme specified in `theme`.
170 #[serde(rename = "experimental.theme_overrides")]
171 pub experimental_theme_overrides: Option<ThemeStyleContent>,
172
173 /// Overrides per theme
174 ///
175 /// These values will override the ones on the specified theme
176 #[serde(default)]
177 pub theme_overrides: HashMap<String, ThemeStyleContent>,
178}
179
180/// A font size value in pixels, wrapping around `f32` for custom settings UI rendering.
181#[derive(
182 Clone,
183 Copy,
184 Debug,
185 Serialize,
186 Deserialize,
187 JsonSchema,
188 MergeFrom,
189 PartialEq,
190 PartialOrd,
191 derive_more::FromStr,
192)]
193#[serde(transparent)]
194pub struct FontSize(#[serde(serialize_with = "serialize_f32_with_two_decimal_places")] pub f32);
195
196impl Display for FontSize {
197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198 write!(f, "{:.2}", self.0)
199 }
200}
201
202impl From<f32> for FontSize {
203 fn from(value: f32) -> Self {
204 Self(value)
205 }
206}
207
208#[derive(
209 Clone,
210 Copy,
211 Debug,
212 Serialize,
213 Deserialize,
214 JsonSchema,
215 MergeFrom,
216 PartialEq,
217 PartialOrd,
218 derive_more::FromStr,
219)]
220#[serde(transparent)]
221pub struct CodeFade(#[serde(serialize_with = "serialize_f32_with_two_decimal_places")] pub f32);
222
223impl Display for CodeFade {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 write!(f, "{:.2}", self.0)
226 }
227}
228
229impl From<f32> for CodeFade {
230 fn from(x: f32) -> Self {
231 Self(x)
232 }
233}
234
235fn default_font_features() -> Option<FontFeaturesContent> {
236 Some(FontFeaturesContent::default())
237}
238
239fn default_font_fallbacks() -> Option<Vec<FontFamilyName>> {
240 Some(Vec::new())
241}
242
243fn default_buffer_font_weight() -> Option<FontWeightContent> {
244 Some(FontWeightContent::NORMAL)
245}
246
247/// Represents the selection of a theme, which can be either static or dynamic.
248#[derive(
249 Clone,
250 Debug,
251 Serialize,
252 Deserialize,
253 JsonSchema,
254 MergeFrom,
255 PartialEq,
256 Eq,
257 strum::EnumDiscriminants,
258)]
259#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
260#[serde(untagged)]
261pub enum ThemeSelection {
262 /// A static theme selection, represented by a single theme name.
263 Static(ThemeName),
264 /// A dynamic theme selection, which can change based the [ThemeMode].
265 Dynamic {
266 /// The mode used to determine which theme to use.
267 #[serde(default)]
268 mode: ThemeAppearanceMode,
269 /// The theme to use for light mode.
270 light: ThemeName,
271 /// The theme to use for dark mode.
272 dark: ThemeName,
273 },
274}
275
276pub const DEFAULT_LIGHT_THEME: &'static str = "One Light";
277pub const DEFAULT_DARK_THEME: &'static str = "One Dark";
278
279impl Default for ThemeSelection {
280 fn default() -> Self {
281 Self::Dynamic {
282 mode: ThemeAppearanceMode::default(),
283 light: ThemeName(DEFAULT_LIGHT_THEME.into()),
284 dark: ThemeName(DEFAULT_DARK_THEME.into()),
285 }
286 }
287}
288
289/// Represents the selection of an icon theme, which can be either static or dynamic.
290#[derive(
291 Clone,
292 Debug,
293 Serialize,
294 Deserialize,
295 JsonSchema,
296 MergeFrom,
297 PartialEq,
298 Eq,
299 strum::EnumDiscriminants,
300)]
301#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
302#[serde(untagged)]
303pub enum IconThemeSelection {
304 /// A static icon theme selection, represented by a single icon theme name.
305 Static(IconThemeName),
306 /// A dynamic icon theme selection, which can change based on the [`ThemeMode`].
307 Dynamic {
308 /// The mode used to determine which theme to use.
309 #[serde(default)]
310 mode: ThemeAppearanceMode,
311 /// The icon theme to use for light mode.
312 light: IconThemeName,
313 /// The icon theme to use for dark mode.
314 dark: IconThemeName,
315 },
316}
317
318/// The mode use to select a theme.
319///
320/// `Light` and `Dark` will select their respective themes.
321///
322/// `System` will select the theme based on the system's appearance.
323#[derive(
324 Debug,
325 PartialEq,
326 Eq,
327 Clone,
328 Copy,
329 Default,
330 Serialize,
331 Deserialize,
332 JsonSchema,
333 MergeFrom,
334 strum::VariantArray,
335 strum::VariantNames,
336)]
337#[serde(rename_all = "snake_case")]
338pub enum ThemeAppearanceMode {
339 /// Use the specified `light` theme.
340 Light,
341
342 /// Use the specified `dark` theme.
343 Dark,
344
345 /// Use the theme based on the system's appearance.
346 #[default]
347 System,
348}
349
350/// Specifies the density of the UI.
351/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
352#[derive(
353 Debug,
354 Default,
355 PartialEq,
356 Eq,
357 PartialOrd,
358 Ord,
359 Hash,
360 Clone,
361 Copy,
362 Serialize,
363 Deserialize,
364 JsonSchema,
365 MergeFrom,
366)]
367#[serde(rename_all = "snake_case")]
368pub enum UiDensity {
369 /// A denser UI with tighter spacing and smaller elements.
370 #[serde(alias = "compact")]
371 Compact,
372 #[default]
373 #[serde(alias = "default")]
374 /// The default UI density.
375 Default,
376 #[serde(alias = "comfortable")]
377 /// A looser UI with more spacing and larger elements.
378 Comfortable,
379}
380
381impl UiDensity {
382 /// The spacing ratio of a given density.
383 /// TODO: Standardize usage throughout the app or remove
384 pub fn spacing_ratio(self) -> f32 {
385 match self {
386 UiDensity::Compact => 0.75,
387 UiDensity::Default => 1.0,
388 UiDensity::Comfortable => 1.25,
389 }
390 }
391}
392
393/// Font family name.
394#[with_fallible_options]
395#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
396#[serde(transparent)]
397pub struct FontFamilyName(pub Arc<str>);
398
399impl AsRef<str> for FontFamilyName {
400 fn as_ref(&self) -> &str {
401 &self.0
402 }
403}
404
405impl From<String> for FontFamilyName {
406 fn from(value: String) -> Self {
407 Self(Arc::from(value))
408 }
409}
410
411impl From<FontFamilyName> for String {
412 fn from(value: FontFamilyName) -> Self {
413 value.0.to_string()
414 }
415}
416
417/// The buffer's line height.
418#[derive(
419 Clone,
420 Copy,
421 Debug,
422 Serialize,
423 Deserialize,
424 PartialEq,
425 JsonSchema,
426 MergeFrom,
427 Default,
428 strum::EnumDiscriminants,
429)]
430#[strum_discriminants(derive(strum::VariantArray, strum::VariantNames, strum::FromRepr))]
431#[serde(rename_all = "snake_case")]
432pub enum BufferLineHeight {
433 /// A less dense line height.
434 #[default]
435 Comfortable,
436 /// The default line height.
437 Standard,
438 /// A custom line height, where 1.0 is the font's height. Must be at least 1.0.
439 Custom(#[serde(deserialize_with = "deserialize_line_height")] f32),
440}
441
442fn deserialize_line_height<'de, D>(deserializer: D) -> Result<f32, D::Error>
443where
444 D: serde::Deserializer<'de>,
445{
446 let value = f32::deserialize(deserializer)?;
447 if value < 1.0 {
448 return Err(serde::de::Error::custom(
449 "buffer_line_height.custom must be at least 1.0",
450 ));
451 }
452
453 Ok(value)
454}
455
456/// The content of a serialized theme.
457#[with_fallible_options]
458#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
459#[serde(default)]
460pub struct ThemeStyleContent {
461 #[serde(rename = "background.appearance")]
462 pub window_background_appearance: Option<WindowBackgroundContent>,
463
464 #[serde(default)]
465 pub accents: Vec<AccentContent>,
466
467 #[serde(flatten, default)]
468 pub colors: ThemeColorsContent,
469
470 #[serde(flatten, default)]
471 pub status: StatusColorsContent,
472
473 #[serde(default)]
474 pub players: Vec<PlayerColorContent>,
475
476 /// The styles for syntax nodes.
477 #[serde(default)]
478 pub syntax: IndexMap<String, HighlightStyleContent>,
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
482pub struct AccentContent(pub Option<String>);
483
484#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
485pub struct PlayerColorContent {
486 pub cursor: Option<String>,
487 pub background: Option<String>,
488 pub selection: Option<String>,
489}
490
491/// Theme name.
492#[with_fallible_options]
493#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
494#[serde(transparent)]
495pub struct ThemeName(pub Arc<str>);
496
497/// Icon Theme Name
498#[with_fallible_options]
499#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)]
500#[serde(transparent)]
501pub struct IconThemeName(pub Arc<str>);
502
503#[with_fallible_options]
504#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
505#[serde(default)]
506pub struct ThemeColorsContent {
507 /// Border color. Used for most borders, is usually a high contrast color.
508 #[serde(rename = "border")]
509 pub border: Option<String>,
510
511 /// Border color. Used for deemphasized borders, like a visual divider between two sections
512 #[serde(rename = "border.variant")]
513 pub border_variant: Option<String>,
514
515 /// Border color. Used for focused elements, like keyboard focused list item.
516 #[serde(rename = "border.focused")]
517 pub border_focused: Option<String>,
518
519 /// Border color. Used for selected elements, like an active search filter or selected checkbox.
520 #[serde(rename = "border.selected")]
521 pub border_selected: Option<String>,
522
523 /// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
524 #[serde(rename = "border.transparent")]
525 pub border_transparent: Option<String>,
526
527 /// Border color. Used for disabled elements, like a disabled input or button.
528 #[serde(rename = "border.disabled")]
529 pub border_disabled: Option<String>,
530
531 /// Background color. Used for elevated surfaces, like a context menu, popup, or dialog.
532 #[serde(rename = "elevated_surface.background")]
533 pub elevated_surface_background: Option<String>,
534
535 /// Background Color. Used for grounded surfaces like a panel or tab.
536 #[serde(rename = "surface.background")]
537 pub surface_background: Option<String>,
538
539 /// Background Color. Used for the app background and blank panels or windows.
540 #[serde(rename = "background")]
541 pub background: Option<String>,
542
543 /// Background Color. Used for the background of an element that should have a different background than the surface it's on.
544 ///
545 /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
546 ///
547 /// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
548 #[serde(rename = "element.background")]
549 pub element_background: Option<String>,
550
551 /// Background Color. Used for the hover state of an element that should have a different background than the surface it's on.
552 ///
553 /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
554 #[serde(rename = "element.hover")]
555 pub element_hover: Option<String>,
556
557 /// Background Color. Used for the active state of an element that should have a different background than the surface it's on.
558 ///
559 /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
560 #[serde(rename = "element.active")]
561 pub element_active: Option<String>,
562
563 /// Background Color. Used for the selected state of an element that should have a different background than the surface it's on.
564 ///
565 /// Selected states are triggered by the element being selected (or "activated") by the user.
566 ///
567 /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
568 #[serde(rename = "element.selected")]
569 pub element_selected: Option<String>,
570
571 /// Background Color. Used for the disabled state of an element that should have a different background than the surface it's on.
572 ///
573 /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
574 #[serde(rename = "element.disabled")]
575 pub element_disabled: Option<String>,
576
577 /// Background Color. Used for the background of selections in a UI element.
578 #[serde(rename = "element.selection_background")]
579 pub element_selection_background: Option<String>,
580
581 /// Background Color. Used for the area that shows where a dragged element will be dropped.
582 #[serde(rename = "drop_target.background")]
583 pub drop_target_background: Option<String>,
584
585 /// Border Color. Used for the border that shows where a dragged element will be dropped.
586 #[serde(rename = "drop_target.border")]
587 pub drop_target_border: Option<String>,
588
589 /// Used for the background of a ghost element that should have the same background as the surface it's on.
590 ///
591 /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
592 ///
593 /// For an element that should have a different background than the surface it's on, use `element_background`.
594 #[serde(rename = "ghost_element.background")]
595 pub ghost_element_background: Option<String>,
596
597 /// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
598 ///
599 /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
600 #[serde(rename = "ghost_element.hover")]
601 pub ghost_element_hover: Option<String>,
602
603 /// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
604 ///
605 /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
606 #[serde(rename = "ghost_element.active")]
607 pub ghost_element_active: Option<String>,
608
609 /// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
610 ///
611 /// Selected states are triggered by the element being selected (or "activated") by the user.
612 ///
613 /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
614 #[serde(rename = "ghost_element.selected")]
615 pub ghost_element_selected: Option<String>,
616
617 /// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
618 ///
619 /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
620 #[serde(rename = "ghost_element.disabled")]
621 pub ghost_element_disabled: Option<String>,
622
623 /// Text Color. Default text color used for most text.
624 #[serde(rename = "text")]
625 pub text: Option<String>,
626
627 /// Text Color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
628 #[serde(rename = "text.muted")]
629 pub text_muted: Option<String>,
630
631 /// Text Color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
632 #[serde(rename = "text.placeholder")]
633 pub text_placeholder: Option<String>,
634
635 /// Text Color. Color used for text denoting disabled elements. Typically, the color is faded or grayed out to emphasize the disabled state.
636 #[serde(rename = "text.disabled")]
637 pub text_disabled: Option<String>,
638
639 /// Text Color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
640 #[serde(rename = "text.accent")]
641 pub text_accent: Option<String>,
642
643 /// Fill Color. Used for the default fill color of an icon.
644 #[serde(rename = "icon")]
645 pub icon: Option<String>,
646
647 /// Fill Color. Used for the muted or deemphasized fill color of an icon.
648 ///
649 /// This might be used to show an icon in an inactive pane, or to deemphasize a series of icons to give them less visual weight.
650 #[serde(rename = "icon.muted")]
651 pub icon_muted: Option<String>,
652
653 /// Fill Color. Used for the disabled fill color of an icon.
654 ///
655 /// Disabled states are shown when a user cannot interact with an element, like a icon button.
656 #[serde(rename = "icon.disabled")]
657 pub icon_disabled: Option<String>,
658
659 /// Fill Color. Used for the placeholder fill color of an icon.
660 ///
661 /// This might be used to show an icon in an input that disappears when the user enters text.
662 #[serde(rename = "icon.placeholder")]
663 pub icon_placeholder: Option<String>,
664
665 /// Fill Color. Used for the accent fill color of an icon.
666 ///
667 /// This might be used to show when a toggleable icon button is selected.
668 #[serde(rename = "icon.accent")]
669 pub icon_accent: Option<String>,
670
671 /// Color used to accent some of the debuggers elements
672 /// Only accent breakpoint & breakpoint related symbols right now
673 #[serde(rename = "debugger.accent")]
674 pub debugger_accent: Option<String>,
675
676 #[serde(rename = "status_bar.background")]
677 pub status_bar_background: Option<String>,
678
679 #[serde(rename = "title_bar.background")]
680 pub title_bar_background: Option<String>,
681
682 #[serde(rename = "title_bar.inactive_background")]
683 pub title_bar_inactive_background: Option<String>,
684
685 #[serde(rename = "toolbar.background")]
686 pub toolbar_background: Option<String>,
687
688 #[serde(rename = "tab_bar.background")]
689 pub tab_bar_background: Option<String>,
690
691 #[serde(rename = "tab.inactive_background")]
692 pub tab_inactive_background: Option<String>,
693
694 #[serde(rename = "tab.active_background")]
695 pub tab_active_background: Option<String>,
696
697 #[serde(rename = "search.match_background")]
698 pub search_match_background: Option<String>,
699
700 #[serde(rename = "search.active_match_background")]
701 pub search_active_match_background: Option<String>,
702
703 #[serde(rename = "panel.background")]
704 pub panel_background: Option<String>,
705
706 #[serde(rename = "panel.focused_border")]
707 pub panel_focused_border: Option<String>,
708
709 #[serde(rename = "panel.indent_guide")]
710 pub panel_indent_guide: Option<String>,
711
712 #[serde(rename = "panel.indent_guide_hover")]
713 pub panel_indent_guide_hover: Option<String>,
714
715 #[serde(rename = "panel.indent_guide_active")]
716 pub panel_indent_guide_active: Option<String>,
717
718 #[serde(rename = "panel.overlay_background")]
719 pub panel_overlay_background: Option<String>,
720
721 #[serde(rename = "panel.overlay_hover")]
722 pub panel_overlay_hover: Option<String>,
723
724 #[serde(rename = "pane.focused_border")]
725 pub pane_focused_border: Option<String>,
726
727 #[serde(rename = "pane_group.border")]
728 pub pane_group_border: Option<String>,
729
730 /// The deprecated version of `scrollbar.thumb.background`.
731 ///
732 /// Don't use this field.
733 #[serde(rename = "scrollbar_thumb.background", skip_serializing)]
734 #[schemars(skip)]
735 pub deprecated_scrollbar_thumb_background: Option<String>,
736
737 /// The color of the scrollbar thumb.
738 #[serde(rename = "scrollbar.thumb.background")]
739 pub scrollbar_thumb_background: Option<String>,
740
741 /// The color of the scrollbar thumb when hovered over.
742 #[serde(rename = "scrollbar.thumb.hover_background")]
743 pub scrollbar_thumb_hover_background: Option<String>,
744
745 /// The color of the scrollbar thumb whilst being actively dragged.
746 #[serde(rename = "scrollbar.thumb.active_background")]
747 pub scrollbar_thumb_active_background: Option<String>,
748
749 /// The border color of the scrollbar thumb.
750 #[serde(rename = "scrollbar.thumb.border")]
751 pub scrollbar_thumb_border: Option<String>,
752
753 /// The background color of the scrollbar track.
754 #[serde(rename = "scrollbar.track.background")]
755 pub scrollbar_track_background: Option<String>,
756
757 /// The border color of the scrollbar track.
758 #[serde(rename = "scrollbar.track.border")]
759 pub scrollbar_track_border: Option<String>,
760
761 /// The color of the minimap thumb.
762 #[serde(rename = "minimap.thumb.background")]
763 pub minimap_thumb_background: Option<String>,
764
765 /// The color of the minimap thumb when hovered over.
766 #[serde(rename = "minimap.thumb.hover_background")]
767 pub minimap_thumb_hover_background: Option<String>,
768
769 /// The color of the minimap thumb whilst being actively dragged.
770 #[serde(rename = "minimap.thumb.active_background")]
771 pub minimap_thumb_active_background: Option<String>,
772
773 /// The border color of the minimap thumb.
774 #[serde(rename = "minimap.thumb.border")]
775 pub minimap_thumb_border: Option<String>,
776
777 #[serde(rename = "editor.foreground")]
778 pub editor_foreground: Option<String>,
779
780 #[serde(rename = "editor.background")]
781 pub editor_background: Option<String>,
782
783 #[serde(rename = "editor.gutter.background")]
784 pub editor_gutter_background: Option<String>,
785
786 #[serde(rename = "editor.subheader.background")]
787 pub editor_subheader_background: Option<String>,
788
789 #[serde(rename = "editor.active_line.background")]
790 pub editor_active_line_background: Option<String>,
791
792 #[serde(rename = "editor.highlighted_line.background")]
793 pub editor_highlighted_line_background: Option<String>,
794
795 /// Background of active line of debugger
796 #[serde(rename = "editor.debugger_active_line.background")]
797 pub editor_debugger_active_line_background: Option<String>,
798
799 /// Text Color. Used for the text of the line number in the editor gutter.
800 #[serde(rename = "editor.line_number")]
801 pub editor_line_number: Option<String>,
802
803 /// Text Color. Used for the text of the line number in the editor gutter when the line is highlighted.
804 #[serde(rename = "editor.active_line_number")]
805 pub editor_active_line_number: Option<String>,
806
807 /// Text Color. Used for the text of the line number in the editor gutter when the line is hovered over.
808 #[serde(rename = "editor.hover_line_number")]
809 pub editor_hover_line_number: Option<String>,
810
811 /// Text Color. Used to mark invisible characters in the editor.
812 ///
813 /// Example: spaces, tabs, carriage returns, etc.
814 #[serde(rename = "editor.invisible")]
815 pub editor_invisible: Option<String>,
816
817 #[serde(rename = "editor.wrap_guide")]
818 pub editor_wrap_guide: Option<String>,
819
820 #[serde(rename = "editor.active_wrap_guide")]
821 pub editor_active_wrap_guide: Option<String>,
822
823 #[serde(rename = "editor.indent_guide")]
824 pub editor_indent_guide: Option<String>,
825
826 #[serde(rename = "editor.indent_guide_active")]
827 pub editor_indent_guide_active: Option<String>,
828
829 /// Read-access of a symbol, like reading a variable.
830 ///
831 /// A document highlight is a range inside a text document which deserves
832 /// special attention. Usually a document highlight is visualized by changing
833 /// the background color of its range.
834 #[serde(rename = "editor.document_highlight.read_background")]
835 pub editor_document_highlight_read_background: Option<String>,
836
837 /// Read-access of a symbol, like reading a variable.
838 ///
839 /// A document highlight is a range inside a text document which deserves
840 /// special attention. Usually a document highlight is visualized by changing
841 /// the background color of its range.
842 #[serde(rename = "editor.document_highlight.write_background")]
843 pub editor_document_highlight_write_background: Option<String>,
844
845 /// Highlighted brackets background color.
846 ///
847 /// Matching brackets in the cursor scope are highlighted with this background color.
848 #[serde(rename = "editor.document_highlight.bracket_background")]
849 pub editor_document_highlight_bracket_background: Option<String>,
850
851 /// Terminal background color.
852 #[serde(rename = "terminal.background")]
853 pub terminal_background: Option<String>,
854
855 /// Terminal foreground color.
856 #[serde(rename = "terminal.foreground")]
857 pub terminal_foreground: Option<String>,
858
859 /// Terminal ANSI background color.
860 #[serde(rename = "terminal.ansi.background")]
861 pub terminal_ansi_background: Option<String>,
862
863 /// Bright terminal foreground color.
864 #[serde(rename = "terminal.bright_foreground")]
865 pub terminal_bright_foreground: Option<String>,
866
867 /// Dim terminal foreground color.
868 #[serde(rename = "terminal.dim_foreground")]
869 pub terminal_dim_foreground: Option<String>,
870
871 /// Black ANSI terminal color.
872 #[serde(rename = "terminal.ansi.black")]
873 pub terminal_ansi_black: Option<String>,
874
875 /// Bright black ANSI terminal color.
876 #[serde(rename = "terminal.ansi.bright_black")]
877 pub terminal_ansi_bright_black: Option<String>,
878
879 /// Dim black ANSI terminal color.
880 #[serde(rename = "terminal.ansi.dim_black")]
881 pub terminal_ansi_dim_black: Option<String>,
882
883 /// Red ANSI terminal color.
884 #[serde(rename = "terminal.ansi.red")]
885 pub terminal_ansi_red: Option<String>,
886
887 /// Bright red ANSI terminal color.
888 #[serde(rename = "terminal.ansi.bright_red")]
889 pub terminal_ansi_bright_red: Option<String>,
890
891 /// Dim red ANSI terminal color.
892 #[serde(rename = "terminal.ansi.dim_red")]
893 pub terminal_ansi_dim_red: Option<String>,
894
895 /// Green ANSI terminal color.
896 #[serde(rename = "terminal.ansi.green")]
897 pub terminal_ansi_green: Option<String>,
898
899 /// Bright green ANSI terminal color.
900 #[serde(rename = "terminal.ansi.bright_green")]
901 pub terminal_ansi_bright_green: Option<String>,
902
903 /// Dim green ANSI terminal color.
904 #[serde(rename = "terminal.ansi.dim_green")]
905 pub terminal_ansi_dim_green: Option<String>,
906
907 /// Yellow ANSI terminal color.
908 #[serde(rename = "terminal.ansi.yellow")]
909 pub terminal_ansi_yellow: Option<String>,
910
911 /// Bright yellow ANSI terminal color.
912 #[serde(rename = "terminal.ansi.bright_yellow")]
913 pub terminal_ansi_bright_yellow: Option<String>,
914
915 /// Dim yellow ANSI terminal color.
916 #[serde(rename = "terminal.ansi.dim_yellow")]
917 pub terminal_ansi_dim_yellow: Option<String>,
918
919 /// Blue ANSI terminal color.
920 #[serde(rename = "terminal.ansi.blue")]
921 pub terminal_ansi_blue: Option<String>,
922
923 /// Bright blue ANSI terminal color.
924 #[serde(rename = "terminal.ansi.bright_blue")]
925 pub terminal_ansi_bright_blue: Option<String>,
926
927 /// Dim blue ANSI terminal color.
928 #[serde(rename = "terminal.ansi.dim_blue")]
929 pub terminal_ansi_dim_blue: Option<String>,
930
931 /// Magenta ANSI terminal color.
932 #[serde(rename = "terminal.ansi.magenta")]
933 pub terminal_ansi_magenta: Option<String>,
934
935 /// Bright magenta ANSI terminal color.
936 #[serde(rename = "terminal.ansi.bright_magenta")]
937 pub terminal_ansi_bright_magenta: Option<String>,
938
939 /// Dim magenta ANSI terminal color.
940 #[serde(rename = "terminal.ansi.dim_magenta")]
941 pub terminal_ansi_dim_magenta: Option<String>,
942
943 /// Cyan ANSI terminal color.
944 #[serde(rename = "terminal.ansi.cyan")]
945 pub terminal_ansi_cyan: Option<String>,
946
947 /// Bright cyan ANSI terminal color.
948 #[serde(rename = "terminal.ansi.bright_cyan")]
949 pub terminal_ansi_bright_cyan: Option<String>,
950
951 /// Dim cyan ANSI terminal color.
952 #[serde(rename = "terminal.ansi.dim_cyan")]
953 pub terminal_ansi_dim_cyan: Option<String>,
954
955 /// White ANSI terminal color.
956 #[serde(rename = "terminal.ansi.white")]
957 pub terminal_ansi_white: Option<String>,
958
959 /// Bright white ANSI terminal color.
960 #[serde(rename = "terminal.ansi.bright_white")]
961 pub terminal_ansi_bright_white: Option<String>,
962
963 /// Dim white ANSI terminal color.
964 #[serde(rename = "terminal.ansi.dim_white")]
965 pub terminal_ansi_dim_white: Option<String>,
966
967 #[serde(rename = "link_text.hover")]
968 pub link_text_hover: Option<String>,
969
970 /// Added version control color.
971 #[serde(rename = "version_control.added")]
972 pub version_control_added: Option<String>,
973
974 /// Deleted version control color.
975 #[serde(rename = "version_control.deleted")]
976 pub version_control_deleted: Option<String>,
977
978 /// Modified version control color.
979 #[serde(rename = "version_control.modified")]
980 pub version_control_modified: Option<String>,
981
982 /// Renamed version control color.
983 #[serde(rename = "version_control.renamed")]
984 pub version_control_renamed: Option<String>,
985
986 /// Conflict version control color.
987 #[serde(rename = "version_control.conflict")]
988 pub version_control_conflict: Option<String>,
989
990 /// Ignored version control color.
991 #[serde(rename = "version_control.ignored")]
992 pub version_control_ignored: Option<String>,
993
994 /// Color for added words in word diffs.
995 #[serde(rename = "version_control.word_added")]
996 pub version_control_word_added: Option<String>,
997
998 /// Color for deleted words in word diffs.
999 #[serde(rename = "version_control.word_deleted")]
1000 pub version_control_word_deleted: Option<String>,
1001
1002 /// Background color for row highlights of "ours" regions in merge conflicts.
1003 #[serde(rename = "version_control.conflict_marker.ours")]
1004 pub version_control_conflict_marker_ours: Option<String>,
1005
1006 /// Background color for row highlights of "theirs" regions in merge conflicts.
1007 #[serde(rename = "version_control.conflict_marker.theirs")]
1008 pub version_control_conflict_marker_theirs: Option<String>,
1009
1010 /// Deprecated in favor of `version_control_conflict_marker_ours`.
1011 #[deprecated]
1012 pub version_control_conflict_ours_background: Option<String>,
1013
1014 /// Deprecated in favor of `version_control_conflict_marker_theirs`.
1015 #[deprecated]
1016 pub version_control_conflict_theirs_background: Option<String>,
1017
1018 /// Background color for Vim Normal mode indicator.
1019 #[serde(rename = "vim.normal.background")]
1020 pub vim_normal_background: Option<String>,
1021 /// Background color for Vim Insert mode indicator.
1022 #[serde(rename = "vim.insert.background")]
1023 pub vim_insert_background: Option<String>,
1024 /// Background color for Vim Replace mode indicator.
1025 #[serde(rename = "vim.replace.background")]
1026 pub vim_replace_background: Option<String>,
1027 /// Background color for Vim Visual mode indicator.
1028 #[serde(rename = "vim.visual.background")]
1029 pub vim_visual_background: Option<String>,
1030 /// Background color for Vim Visual Line mode indicator.
1031 #[serde(rename = "vim.visual_line.background")]
1032 pub vim_visual_line_background: Option<String>,
1033 /// Background color for Vim Visual Block mode indicator.
1034 #[serde(rename = "vim.visual_block.background")]
1035 pub vim_visual_block_background: Option<String>,
1036 /// Background color for Vim Helix Normal mode indicator.
1037 #[serde(rename = "vim.helix_normal.background")]
1038 pub vim_helix_normal_background: Option<String>,
1039 /// Background color for Vim Helix Select mode indicator.
1040 #[serde(rename = "vim.helix_select.background")]
1041 pub vim_helix_select_background: Option<String>,
1042 /// Background color for Vim Normal mode indicator.
1043 #[serde(rename = "vim.normal.foreground")]
1044 pub vim_normal_foreground: Option<String>,
1045 /// Foreground color for Vim Insert mode indicator.
1046 #[serde(rename = "vim.insert.foreground")]
1047 pub vim_insert_foreground: Option<String>,
1048 /// Foreground color for Vim Replace mode indicator.
1049 #[serde(rename = "vim.replace.foreground")]
1050 pub vim_replace_foreground: Option<String>,
1051 /// Foreground color for Vim Visual mode indicator.
1052 #[serde(rename = "vim.visual.foreground")]
1053 pub vim_visual_foreground: Option<String>,
1054 /// Foreground color for Vim Visual Line mode indicator.
1055 #[serde(rename = "vim.visual_line.foreground")]
1056 pub vim_visual_line_foreground: Option<String>,
1057 /// Foreground color for Vim Visual Block mode indicator.
1058 #[serde(rename = "vim.visual_block.foreground")]
1059 pub vim_visual_block_foreground: Option<String>,
1060 /// Foreground color for Vim Helix Normal mode indicator.
1061 #[serde(rename = "vim.helix_normal.foreground")]
1062 pub vim_helix_normal_foreground: Option<String>,
1063 /// Foreground color for Vim Helix Select mode indicator.
1064 #[serde(rename = "vim.helix_select.foreground")]
1065 pub vim_helix_select_foreground: Option<String>,
1066}
1067
1068#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
1069#[serde(default)]
1070pub struct HighlightStyleContent {
1071 pub color: Option<String>,
1072
1073 #[serde(
1074 skip_serializing_if = "Option::is_none",
1075 deserialize_with = "treat_error_as_none"
1076 )]
1077 pub background_color: Option<String>,
1078
1079 #[serde(
1080 skip_serializing_if = "Option::is_none",
1081 deserialize_with = "treat_error_as_none"
1082 )]
1083 pub font_style: Option<FontStyleContent>,
1084
1085 #[serde(
1086 skip_serializing_if = "Option::is_none",
1087 deserialize_with = "treat_error_as_none"
1088 )]
1089 pub font_weight: Option<FontWeightContent>,
1090}
1091
1092impl HighlightStyleContent {
1093 pub fn is_empty(&self) -> bool {
1094 self.color.is_none()
1095 && self.background_color.is_none()
1096 && self.font_style.is_none()
1097 && self.font_weight.is_none()
1098 }
1099}
1100
1101fn treat_error_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
1102where
1103 T: Deserialize<'de>,
1104 D: Deserializer<'de>,
1105{
1106 let value: Value = Deserialize::deserialize(deserializer)?;
1107 Ok(T::deserialize(value).ok())
1108}
1109
1110#[with_fallible_options]
1111#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
1112#[serde(default)]
1113pub struct StatusColorsContent {
1114 /// Indicates some kind of conflict, like a file changed on disk while it was open, or
1115 /// merge conflicts in a Git repository.
1116 #[serde(rename = "conflict")]
1117 pub conflict: Option<String>,
1118
1119 #[serde(rename = "conflict.background")]
1120 pub conflict_background: Option<String>,
1121
1122 #[serde(rename = "conflict.border")]
1123 pub conflict_border: Option<String>,
1124
1125 /// Indicates something new, like a new file added to a Git repository.
1126 #[serde(rename = "created")]
1127 pub created: Option<String>,
1128
1129 #[serde(rename = "created.background")]
1130 pub created_background: Option<String>,
1131
1132 #[serde(rename = "created.border")]
1133 pub created_border: Option<String>,
1134
1135 /// Indicates that something no longer exists, like a deleted file.
1136 #[serde(rename = "deleted")]
1137 pub deleted: Option<String>,
1138
1139 #[serde(rename = "deleted.background")]
1140 pub deleted_background: Option<String>,
1141
1142 #[serde(rename = "deleted.border")]
1143 pub deleted_border: Option<String>,
1144
1145 /// Indicates a system error, a failed operation or a diagnostic error.
1146 #[serde(rename = "error")]
1147 pub error: Option<String>,
1148
1149 #[serde(rename = "error.background")]
1150 pub error_background: Option<String>,
1151
1152 #[serde(rename = "error.border")]
1153 pub error_border: Option<String>,
1154
1155 /// Represents a hidden status, such as a file being hidden in a file tree.
1156 #[serde(rename = "hidden")]
1157 pub hidden: Option<String>,
1158
1159 #[serde(rename = "hidden.background")]
1160 pub hidden_background: Option<String>,
1161
1162 #[serde(rename = "hidden.border")]
1163 pub hidden_border: Option<String>,
1164
1165 /// Indicates a hint or some kind of additional information.
1166 #[serde(rename = "hint")]
1167 pub hint: Option<String>,
1168
1169 #[serde(rename = "hint.background")]
1170 pub hint_background: Option<String>,
1171
1172 #[serde(rename = "hint.border")]
1173 pub hint_border: Option<String>,
1174
1175 /// Indicates that something is deliberately ignored, such as a file or operation ignored by Git.
1176 #[serde(rename = "ignored")]
1177 pub ignored: Option<String>,
1178
1179 #[serde(rename = "ignored.background")]
1180 pub ignored_background: Option<String>,
1181
1182 #[serde(rename = "ignored.border")]
1183 pub ignored_border: Option<String>,
1184
1185 /// Represents informational status updates or messages.
1186 #[serde(rename = "info")]
1187 pub info: Option<String>,
1188
1189 #[serde(rename = "info.background")]
1190 pub info_background: Option<String>,
1191
1192 #[serde(rename = "info.border")]
1193 pub info_border: Option<String>,
1194
1195 /// Indicates a changed or altered status, like a file that has been edited.
1196 #[serde(rename = "modified")]
1197 pub modified: Option<String>,
1198
1199 #[serde(rename = "modified.background")]
1200 pub modified_background: Option<String>,
1201
1202 #[serde(rename = "modified.border")]
1203 pub modified_border: Option<String>,
1204
1205 /// Indicates something that is predicted, like automatic code completion, or generated code.
1206 #[serde(rename = "predictive")]
1207 pub predictive: Option<String>,
1208
1209 #[serde(rename = "predictive.background")]
1210 pub predictive_background: Option<String>,
1211
1212 #[serde(rename = "predictive.border")]
1213 pub predictive_border: Option<String>,
1214
1215 /// Represents a renamed status, such as a file that has been renamed.
1216 #[serde(rename = "renamed")]
1217 pub renamed: Option<String>,
1218
1219 #[serde(rename = "renamed.background")]
1220 pub renamed_background: Option<String>,
1221
1222 #[serde(rename = "renamed.border")]
1223 pub renamed_border: Option<String>,
1224
1225 /// Indicates a successful operation or task completion.
1226 #[serde(rename = "success")]
1227 pub success: Option<String>,
1228
1229 #[serde(rename = "success.background")]
1230 pub success_background: Option<String>,
1231
1232 #[serde(rename = "success.border")]
1233 pub success_border: Option<String>,
1234
1235 /// Indicates some kind of unreachable status, like a block of code that can never be reached.
1236 #[serde(rename = "unreachable")]
1237 pub unreachable: Option<String>,
1238
1239 #[serde(rename = "unreachable.background")]
1240 pub unreachable_background: Option<String>,
1241
1242 #[serde(rename = "unreachable.border")]
1243 pub unreachable_border: Option<String>,
1244
1245 /// Represents a warning status, like an operation that is about to fail.
1246 #[serde(rename = "warning")]
1247 pub warning: Option<String>,
1248
1249 #[serde(rename = "warning.background")]
1250 pub warning_background: Option<String>,
1251
1252 #[serde(rename = "warning.border")]
1253 pub warning_border: Option<String>,
1254}
1255
1256/// The background appearance of the window.
1257#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema, MergeFrom)]
1258#[serde(rename_all = "snake_case")]
1259pub enum WindowBackgroundContent {
1260 Opaque,
1261 Transparent,
1262 Blurred,
1263}
1264
1265#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq)]
1266#[serde(rename_all = "snake_case")]
1267pub enum FontStyleContent {
1268 Normal,
1269 Italic,
1270 Oblique,
1271}
1272
1273#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize, MergeFrom)]
1274#[serde(transparent)]
1275pub struct FontWeightContent(pub f32);
1276
1277impl Default for FontWeightContent {
1278 fn default() -> Self {
1279 Self::NORMAL
1280 }
1281}
1282
1283impl FontWeightContent {
1284 pub const THIN: FontWeightContent = FontWeightContent(100.0);
1285 pub const EXTRA_LIGHT: FontWeightContent = FontWeightContent(200.0);
1286 pub const LIGHT: FontWeightContent = FontWeightContent(300.0);
1287 pub const NORMAL: FontWeightContent = FontWeightContent(400.0);
1288 pub const MEDIUM: FontWeightContent = FontWeightContent(500.0);
1289 pub const SEMIBOLD: FontWeightContent = FontWeightContent(600.0);
1290 pub const BOLD: FontWeightContent = FontWeightContent(700.0);
1291 pub const EXTRA_BOLD: FontWeightContent = FontWeightContent(800.0);
1292 pub const BLACK: FontWeightContent = FontWeightContent(900.0);
1293}
1294
1295impl schemars::JsonSchema for FontWeightContent {
1296 fn schema_name() -> std::borrow::Cow<'static, str> {
1297 "FontWeightContent".into()
1298 }
1299
1300 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
1301 use schemars::json_schema;
1302 json_schema!({
1303 "type": "number",
1304 "minimum": Self::THIN.0,
1305 "maximum": Self::BLACK.0,
1306 "default": Self::NORMAL.0,
1307 "description": "Font weight value between 100 (thin) and 900 (black)"
1308 })
1309 }
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314 use super::*;
1315 use serde_json::json;
1316
1317 #[test]
1318 fn test_buffer_line_height_deserialize_valid() {
1319 assert_eq!(
1320 serde_json::from_value::<BufferLineHeight>(json!("comfortable")).unwrap(),
1321 BufferLineHeight::Comfortable
1322 );
1323 assert_eq!(
1324 serde_json::from_value::<BufferLineHeight>(json!("standard")).unwrap(),
1325 BufferLineHeight::Standard
1326 );
1327 assert_eq!(
1328 serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.0})).unwrap(),
1329 BufferLineHeight::Custom(1.0)
1330 );
1331 assert_eq!(
1332 serde_json::from_value::<BufferLineHeight>(json!({"custom": 1.5})).unwrap(),
1333 BufferLineHeight::Custom(1.5)
1334 );
1335 }
1336
1337 #[test]
1338 fn test_buffer_line_height_deserialize_invalid() {
1339 assert!(
1340 serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.99}))
1341 .err()
1342 .unwrap()
1343 .to_string()
1344 .contains("buffer_line_height.custom must be at least 1.0")
1345 );
1346 assert!(
1347 serde_json::from_value::<BufferLineHeight>(json!({"custom": 0.0}))
1348 .err()
1349 .unwrap()
1350 .to_string()
1351 .contains("buffer_line_height.custom must be at least 1.0")
1352 );
1353 assert!(
1354 serde_json::from_value::<BufferLineHeight>(json!({"custom": -1.0}))
1355 .err()
1356 .unwrap()
1357 .to_string()
1358 .contains("buffer_line_height.custom must be at least 1.0")
1359 );
1360 }
1361
1362 #[test]
1363 fn test_buffer_font_weight_schema_has_default() {
1364 use schemars::schema_for;
1365
1366 let schema = schema_for!(ThemeSettingsContent);
1367 let schema_value = serde_json::to_value(&schema).unwrap();
1368
1369 let properties = &schema_value["properties"];
1370 let buffer_font_weight = &properties["buffer_font_weight"];
1371
1372 assert!(
1373 buffer_font_weight.get("default").is_some(),
1374 "buffer_font_weight should have a default value in the schema"
1375 );
1376
1377 let default_value = &buffer_font_weight["default"];
1378 assert_eq!(
1379 default_value.as_f64(),
1380 Some(FontWeightContent::NORMAL.0 as f64),
1381 "buffer_font_weight default should be 400.0 (FontWeightContent::NORMAL)"
1382 );
1383
1384 let defs = &schema_value["$defs"];
1385 let font_weight_def = &defs["FontWeightContent"];
1386
1387 assert_eq!(
1388 font_weight_def["minimum"].as_f64(),
1389 Some(FontWeightContent::THIN.0 as f64),
1390 "FontWeightContent should have minimum of 100.0"
1391 );
1392 assert_eq!(
1393 font_weight_def["maximum"].as_f64(),
1394 Some(FontWeightContent::BLACK.0 as f64),
1395 "FontWeightContent should have maximum of 900.0"
1396 );
1397 assert_eq!(
1398 font_weight_def["default"].as_f64(),
1399 Some(FontWeightContent::NORMAL.0 as f64),
1400 "FontWeightContent should have default of 400.0"
1401 );
1402 }
1403}