1use anyhow::Result;
2use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
3use indexmap::IndexMap;
4use palette::FromColor;
5use schemars::gen::SchemaGenerator;
6use schemars::schema::{Schema, SchemaObject};
7use schemars::JsonSchema;
8use serde::{Deserialize, Deserializer, Serialize};
9use serde_json::Value;
10use serde_repr::{Deserialize_repr, Serialize_repr};
11
12use crate::{StatusColorsRefinement, ThemeColorsRefinement};
13
14pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
15 let rgba = gpui::Rgba::try_from(color)?;
16 let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
17 let hsla = palette::Hsla::from_color(rgba);
18
19 let hsla = gpui::hsla(
20 hsla.hue.into_positive_degrees() / 360.,
21 hsla.saturation,
22 hsla.lightness,
23 hsla.alpha,
24 );
25
26 Ok(hsla)
27}
28
29#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
30#[serde(rename_all = "snake_case")]
31pub enum AppearanceContent {
32 Light,
33 Dark,
34}
35
36/// The content of a serialized theme family.
37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
38pub struct ThemeFamilyContent {
39 pub name: String,
40 pub author: String,
41 pub themes: Vec<ThemeContent>,
42}
43
44/// The content of a serialized theme.
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
46pub struct ThemeContent {
47 pub name: String,
48 pub appearance: AppearanceContent,
49 pub style: ThemeStyleContent,
50}
51
52/// The content of a serialized theme.
53#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
54#[serde(default)]
55pub struct ThemeStyleContent {
56 #[serde(flatten, default)]
57 pub colors: ThemeColorsContent,
58
59 #[serde(flatten, default)]
60 pub status: StatusColorsContent,
61
62 #[serde(default)]
63 pub players: Vec<PlayerColorContent>,
64
65 /// The styles for syntax nodes.
66 #[serde(default)]
67 pub syntax: IndexMap<String, HighlightStyleContent>,
68}
69
70impl ThemeStyleContent {
71 /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeContent`].
72 #[inline(always)]
73 pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement {
74 self.colors.theme_colors_refinement()
75 }
76
77 /// Returns a [`StatusColorsRefinement`] based on the colors in the [`ThemeContent`].
78 #[inline(always)]
79 pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
80 self.status.status_colors_refinement()
81 }
82
83 /// Returns the syntax style overrides in the [`ThemeContent`].
84 pub fn syntax_overrides(&self) -> Vec<(String, HighlightStyle)> {
85 self.syntax
86 .iter()
87 .map(|(key, style)| {
88 (
89 key.clone(),
90 HighlightStyle {
91 color: style
92 .color
93 .as_ref()
94 .and_then(|color| try_parse_color(color).ok()),
95 font_style: style
96 .font_style
97 .map(|font_style| FontStyle::from(font_style)),
98 font_weight: style
99 .font_weight
100 .map(|font_weight| FontWeight::from(font_weight)),
101 ..Default::default()
102 },
103 )
104 })
105 .collect()
106 }
107}
108
109#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
110#[serde(default)]
111pub struct ThemeColorsContent {
112 /// Border color. Used for most borders, is usually a high contrast color.
113 #[serde(rename = "border")]
114 pub border: Option<String>,
115
116 /// Border color. Used for deemphasized borders, like a visual divider between two sections
117 #[serde(rename = "border.variant")]
118 pub border_variant: Option<String>,
119
120 /// Border color. Used for focused elements, like keyboard focused list item.
121 #[serde(rename = "border.focused")]
122 pub border_focused: Option<String>,
123
124 /// Border color. Used for selected elements, like an active search filter or selected checkbox.
125 #[serde(rename = "border.selected")]
126 pub border_selected: Option<String>,
127
128 /// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
129 #[serde(rename = "border.transparent")]
130 pub border_transparent: Option<String>,
131
132 /// Border color. Used for disabled elements, like a disabled input or button.
133 #[serde(rename = "border.disabled")]
134 pub border_disabled: Option<String>,
135
136 /// Border color. Used for elevated surfaces, like a context menu, popup, or dialog.
137 #[serde(rename = "elevated_surface.background")]
138 pub elevated_surface_background: Option<String>,
139
140 /// Background Color. Used for grounded surfaces like a panel or tab.
141 #[serde(rename = "surface.background")]
142 pub surface_background: Option<String>,
143
144 /// Background Color. Used for the app background and blank panels or windows.
145 #[serde(rename = "background")]
146 pub background: Option<String>,
147
148 /// Background Color. Used for the background of an element that should have a different background than the surface it's on.
149 ///
150 /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
151 ///
152 /// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
153 #[serde(rename = "element.background")]
154 pub element_background: Option<String>,
155
156 /// Background Color. Used for the hover state of an element that should have a different background than the surface it's on.
157 ///
158 /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
159 #[serde(rename = "element.hover")]
160 pub element_hover: Option<String>,
161
162 /// Background Color. Used for the active state of an element that should have a different background than the surface it's on.
163 ///
164 /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
165 #[serde(rename = "element.active")]
166 pub element_active: Option<String>,
167
168 /// Background Color. Used for the selected state of an element that should have a different background than the surface it's on.
169 ///
170 /// Selected states are triggered by the element being selected (or "activated") by the user.
171 ///
172 /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
173 #[serde(rename = "element.selected")]
174 pub element_selected: Option<String>,
175
176 /// Background Color. Used for the disabled state of an element that should have a different background than the surface it's on.
177 ///
178 /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
179 #[serde(rename = "element.disabled")]
180 pub element_disabled: Option<String>,
181
182 /// Background Color. Used for the area that shows where a dragged element will be dropped.
183 #[serde(rename = "drop_target.background")]
184 pub drop_target_background: Option<String>,
185
186 /// Used for the background of a ghost element that should have the same background as the surface it's on.
187 ///
188 /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
189 ///
190 /// For an element that should have a different background than the surface it's on, use `element_background`.
191 #[serde(rename = "ghost_element.background")]
192 pub ghost_element_background: Option<String>,
193
194 /// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
195 ///
196 /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
197 #[serde(rename = "ghost_element.hover")]
198 pub ghost_element_hover: Option<String>,
199
200 /// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
201 ///
202 /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
203 #[serde(rename = "ghost_element.active")]
204 pub ghost_element_active: Option<String>,
205
206 /// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
207 ///
208 /// Selected states are triggered by the element being selected (or "activated") by the user.
209 ///
210 /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
211 #[serde(rename = "ghost_element.selected")]
212 pub ghost_element_selected: Option<String>,
213
214 /// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
215 ///
216 /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
217 #[serde(rename = "ghost_element.disabled")]
218 pub ghost_element_disabled: Option<String>,
219
220 /// Text Color. Default text color used for most text.
221 #[serde(rename = "text")]
222 pub text: Option<String>,
223
224 /// Text Color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
225 #[serde(rename = "text.muted")]
226 pub text_muted: Option<String>,
227
228 /// Text Color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
229 #[serde(rename = "text.placeholder")]
230 pub text_placeholder: Option<String>,
231
232 /// Text Color. Color used for text denoting disabled elements. Typically, the color is faded or grayed out to emphasize the disabled state.
233 #[serde(rename = "text.disabled")]
234 pub text_disabled: Option<String>,
235
236 /// Text Color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
237 #[serde(rename = "text.accent")]
238 pub text_accent: Option<String>,
239
240 /// Fill Color. Used for the default fill color of an icon.
241 #[serde(rename = "icon")]
242 pub icon: Option<String>,
243
244 /// Fill Color. Used for the muted or deemphasized fill color of an icon.
245 ///
246 /// This might be used to show an icon in an inactive pane, or to demphasize a series of icons to give them less visual weight.
247 #[serde(rename = "icon.muted")]
248 pub icon_muted: Option<String>,
249
250 /// Fill Color. Used for the disabled fill color of an icon.
251 ///
252 /// Disabled states are shown when a user cannot interact with an element, like a icon button.
253 #[serde(rename = "icon.disabled")]
254 pub icon_disabled: Option<String>,
255
256 /// Fill Color. Used for the placeholder fill color of an icon.
257 ///
258 /// This might be used to show an icon in an input that disappears when the user enters text.
259 #[serde(rename = "icon.placeholder")]
260 pub icon_placeholder: Option<String>,
261
262 /// Fill Color. Used for the accent fill color of an icon.
263 ///
264 /// This might be used to show when a toggleable icon button is selected.
265 #[serde(rename = "icon.accent")]
266 pub icon_accent: Option<String>,
267
268 #[serde(rename = "status_bar.background")]
269 pub status_bar_background: Option<String>,
270
271 #[serde(rename = "title_bar.background")]
272 pub title_bar_background: Option<String>,
273
274 #[serde(rename = "toolbar.background")]
275 pub toolbar_background: Option<String>,
276
277 #[serde(rename = "tab_bar.background")]
278 pub tab_bar_background: Option<String>,
279
280 #[serde(rename = "tab.inactive_background")]
281 pub tab_inactive_background: Option<String>,
282
283 #[serde(rename = "tab.active_background")]
284 pub tab_active_background: Option<String>,
285
286 #[serde(rename = "search.match_background")]
287 pub search_match_background: Option<String>,
288
289 #[serde(rename = "panel.background")]
290 pub panel_background: Option<String>,
291
292 #[serde(rename = "panel.focused_border")]
293 pub panel_focused_border: Option<String>,
294
295 #[serde(rename = "pane.focused_border")]
296 pub pane_focused_border: Option<String>,
297
298 /// The color of the scrollbar thumb.
299 #[serde(
300 rename = "scrollbar.thumb.background",
301 alias = "scrollbar_thumb.background"
302 )]
303 pub scrollbar_thumb_background: Option<String>,
304
305 /// The color of the scrollbar thumb when hovered over.
306 #[serde(rename = "scrollbar.thumb.hover_background")]
307 pub scrollbar_thumb_hover_background: Option<String>,
308
309 /// The border color of the scrollbar thumb.
310 #[serde(rename = "scrollbar.thumb.border")]
311 pub scrollbar_thumb_border: Option<String>,
312
313 /// The background color of the scrollbar track.
314 #[serde(rename = "scrollbar.track.background")]
315 pub scrollbar_track_background: Option<String>,
316
317 /// The border color of the scrollbar track.
318 #[serde(rename = "scrollbar.track.border")]
319 pub scrollbar_track_border: Option<String>,
320
321 #[serde(rename = "editor.foreground")]
322 pub editor_foreground: Option<String>,
323
324 #[serde(rename = "editor.background")]
325 pub editor_background: Option<String>,
326
327 #[serde(rename = "editor.gutter.background")]
328 pub editor_gutter_background: Option<String>,
329
330 #[serde(rename = "editor.subheader.background")]
331 pub editor_subheader_background: Option<String>,
332
333 #[serde(rename = "editor.active_line.background")]
334 pub editor_active_line_background: Option<String>,
335
336 #[serde(rename = "editor.highlighted_line.background")]
337 pub editor_highlighted_line_background: Option<String>,
338
339 /// Text Color. Used for the text of the line number in the editor gutter.
340 #[serde(rename = "editor.line_number")]
341 pub editor_line_number: Option<String>,
342
343 /// Text Color. Used for the text of the line number in the editor gutter when the line is highlighted.
344 #[serde(rename = "editor.active_line_number")]
345 pub editor_active_line_number: Option<String>,
346
347 /// Text Color. Used to mark invisible characters in the editor.
348 ///
349 /// Example: spaces, tabs, carriage returns, etc.
350 #[serde(rename = "editor.invisible")]
351 pub editor_invisible: Option<String>,
352
353 #[serde(rename = "editor.wrap_guide")]
354 pub editor_wrap_guide: Option<String>,
355
356 #[serde(rename = "editor.active_wrap_guide")]
357 pub editor_active_wrap_guide: Option<String>,
358
359 /// Read-access of a symbol, like reading a variable.
360 ///
361 /// A document highlight is a range inside a text document which deserves
362 /// special attention. Usually a document highlight is visualized by changing
363 /// the background color of its range.
364 #[serde(rename = "editor.document_highlight.read_background")]
365 pub editor_document_highlight_read_background: Option<String>,
366
367 /// Read-access of a symbol, like reading a variable.
368 ///
369 /// A document highlight is a range inside a text document which deserves
370 /// special attention. Usually a document highlight is visualized by changing
371 /// the background color of its range.
372 #[serde(rename = "editor.document_highlight.write_background")]
373 pub editor_document_highlight_write_background: Option<String>,
374
375 /// Terminal background color.
376 #[serde(rename = "terminal.background")]
377 pub terminal_background: Option<String>,
378
379 /// Terminal foreground color.
380 #[serde(rename = "terminal.foreground")]
381 pub terminal_foreground: Option<String>,
382
383 /// Bright terminal foreground color.
384 #[serde(rename = "terminal.bright_foreground")]
385 pub terminal_bright_foreground: Option<String>,
386
387 /// Dim terminal foreground color.
388 #[serde(rename = "terminal.dim_foreground")]
389 pub terminal_dim_foreground: Option<String>,
390
391 /// Black ANSI terminal color.
392 #[serde(rename = "terminal.ansi.black")]
393 pub terminal_ansi_black: Option<String>,
394
395 /// Bright black ANSI terminal color.
396 #[serde(rename = "terminal.ansi.bright_black")]
397 pub terminal_ansi_bright_black: Option<String>,
398
399 /// Dim black ANSI terminal color.
400 #[serde(rename = "terminal.ansi.dim_black")]
401 pub terminal_ansi_dim_black: Option<String>,
402
403 /// Red ANSI terminal color.
404 #[serde(rename = "terminal.ansi.red")]
405 pub terminal_ansi_red: Option<String>,
406
407 /// Bright red ANSI terminal color.
408 #[serde(rename = "terminal.ansi.bright_red")]
409 pub terminal_ansi_bright_red: Option<String>,
410
411 /// Dim red ANSI terminal color.
412 #[serde(rename = "terminal.ansi.dim_red")]
413 pub terminal_ansi_dim_red: Option<String>,
414
415 /// Green ANSI terminal color.
416 #[serde(rename = "terminal.ansi.green")]
417 pub terminal_ansi_green: Option<String>,
418
419 /// Bright green ANSI terminal color.
420 #[serde(rename = "terminal.ansi.bright_green")]
421 pub terminal_ansi_bright_green: Option<String>,
422
423 /// Dim green ANSI terminal color.
424 #[serde(rename = "terminal.ansi.dim_green")]
425 pub terminal_ansi_dim_green: Option<String>,
426
427 /// Yellow ANSI terminal color.
428 #[serde(rename = "terminal.ansi.yellow")]
429 pub terminal_ansi_yellow: Option<String>,
430
431 /// Bright yellow ANSI terminal color.
432 #[serde(rename = "terminal.ansi.bright_yellow")]
433 pub terminal_ansi_bright_yellow: Option<String>,
434
435 /// Dim yellow ANSI terminal color.
436 #[serde(rename = "terminal.ansi.dim_yellow")]
437 pub terminal_ansi_dim_yellow: Option<String>,
438
439 /// Blue ANSI terminal color.
440 #[serde(rename = "terminal.ansi.blue")]
441 pub terminal_ansi_blue: Option<String>,
442
443 /// Bright blue ANSI terminal color.
444 #[serde(rename = "terminal.ansi.bright_blue")]
445 pub terminal_ansi_bright_blue: Option<String>,
446
447 /// Dim blue ANSI terminal color.
448 #[serde(rename = "terminal.ansi.dim_blue")]
449 pub terminal_ansi_dim_blue: Option<String>,
450
451 /// Magenta ANSI terminal color.
452 #[serde(rename = "terminal.ansi.magenta")]
453 pub terminal_ansi_magenta: Option<String>,
454
455 /// Bright magenta ANSI terminal color.
456 #[serde(rename = "terminal.ansi.bright_magenta")]
457 pub terminal_ansi_bright_magenta: Option<String>,
458
459 /// Dim magenta ANSI terminal color.
460 #[serde(rename = "terminal.ansi.dim_magenta")]
461 pub terminal_ansi_dim_magenta: Option<String>,
462
463 /// Cyan ANSI terminal color.
464 #[serde(rename = "terminal.ansi.cyan")]
465 pub terminal_ansi_cyan: Option<String>,
466
467 /// Bright cyan ANSI terminal color.
468 #[serde(rename = "terminal.ansi.bright_cyan")]
469 pub terminal_ansi_bright_cyan: Option<String>,
470
471 /// Dim cyan ANSI terminal color.
472 #[serde(rename = "terminal.ansi.dim_cyan")]
473 pub terminal_ansi_dim_cyan: Option<String>,
474
475 /// White ANSI terminal color.
476 #[serde(rename = "terminal.ansi.white")]
477 pub terminal_ansi_white: Option<String>,
478
479 /// Bright white ANSI terminal color.
480 #[serde(rename = "terminal.ansi.bright_white")]
481 pub terminal_ansi_bright_white: Option<String>,
482
483 /// Dim white ANSI terminal color.
484 #[serde(rename = "terminal.ansi.dim_white")]
485 pub terminal_ansi_dim_white: Option<String>,
486
487 #[serde(rename = "link_text.hover")]
488 pub link_text_hover: Option<String>,
489}
490
491impl ThemeColorsContent {
492 /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeColorsContent`].
493 pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement {
494 ThemeColorsRefinement {
495 border: self
496 .border
497 .as_ref()
498 .and_then(|color| try_parse_color(color).ok()),
499 border_variant: self
500 .border_variant
501 .as_ref()
502 .and_then(|color| try_parse_color(color).ok()),
503 border_focused: self
504 .border_focused
505 .as_ref()
506 .and_then(|color| try_parse_color(color).ok()),
507 border_selected: self
508 .border_selected
509 .as_ref()
510 .and_then(|color| try_parse_color(color).ok()),
511 border_transparent: self
512 .border_transparent
513 .as_ref()
514 .and_then(|color| try_parse_color(color).ok()),
515 border_disabled: self
516 .border_disabled
517 .as_ref()
518 .and_then(|color| try_parse_color(color).ok()),
519 elevated_surface_background: self
520 .elevated_surface_background
521 .as_ref()
522 .and_then(|color| try_parse_color(color).ok()),
523 surface_background: self
524 .surface_background
525 .as_ref()
526 .and_then(|color| try_parse_color(color).ok()),
527 background: self
528 .background
529 .as_ref()
530 .and_then(|color| try_parse_color(color).ok()),
531 element_background: self
532 .element_background
533 .as_ref()
534 .and_then(|color| try_parse_color(color).ok()),
535 element_hover: self
536 .element_hover
537 .as_ref()
538 .and_then(|color| try_parse_color(color).ok()),
539 element_active: self
540 .element_active
541 .as_ref()
542 .and_then(|color| try_parse_color(color).ok()),
543 element_selected: self
544 .element_selected
545 .as_ref()
546 .and_then(|color| try_parse_color(color).ok()),
547 element_disabled: self
548 .element_disabled
549 .as_ref()
550 .and_then(|color| try_parse_color(color).ok()),
551 drop_target_background: self
552 .drop_target_background
553 .as_ref()
554 .and_then(|color| try_parse_color(color).ok()),
555 ghost_element_background: self
556 .ghost_element_background
557 .as_ref()
558 .and_then(|color| try_parse_color(color).ok()),
559 ghost_element_hover: self
560 .ghost_element_hover
561 .as_ref()
562 .and_then(|color| try_parse_color(color).ok()),
563 ghost_element_active: self
564 .ghost_element_active
565 .as_ref()
566 .and_then(|color| try_parse_color(color).ok()),
567 ghost_element_selected: self
568 .ghost_element_selected
569 .as_ref()
570 .and_then(|color| try_parse_color(color).ok()),
571 ghost_element_disabled: self
572 .ghost_element_disabled
573 .as_ref()
574 .and_then(|color| try_parse_color(color).ok()),
575 text: self
576 .text
577 .as_ref()
578 .and_then(|color| try_parse_color(color).ok()),
579 text_muted: self
580 .text_muted
581 .as_ref()
582 .and_then(|color| try_parse_color(color).ok()),
583 text_placeholder: self
584 .text_placeholder
585 .as_ref()
586 .and_then(|color| try_parse_color(color).ok()),
587 text_disabled: self
588 .text_disabled
589 .as_ref()
590 .and_then(|color| try_parse_color(color).ok()),
591 text_accent: self
592 .text_accent
593 .as_ref()
594 .and_then(|color| try_parse_color(color).ok()),
595 icon: self
596 .icon
597 .as_ref()
598 .and_then(|color| try_parse_color(color).ok()),
599 icon_muted: self
600 .icon_muted
601 .as_ref()
602 .and_then(|color| try_parse_color(color).ok()),
603 icon_disabled: self
604 .icon_disabled
605 .as_ref()
606 .and_then(|color| try_parse_color(color).ok()),
607 icon_placeholder: self
608 .icon_placeholder
609 .as_ref()
610 .and_then(|color| try_parse_color(color).ok()),
611 icon_accent: self
612 .icon_accent
613 .as_ref()
614 .and_then(|color| try_parse_color(color).ok()),
615 status_bar_background: self
616 .status_bar_background
617 .as_ref()
618 .and_then(|color| try_parse_color(color).ok()),
619 title_bar_background: self
620 .title_bar_background
621 .as_ref()
622 .and_then(|color| try_parse_color(color).ok()),
623 toolbar_background: self
624 .toolbar_background
625 .as_ref()
626 .and_then(|color| try_parse_color(color).ok()),
627 tab_bar_background: self
628 .tab_bar_background
629 .as_ref()
630 .and_then(|color| try_parse_color(color).ok()),
631 tab_inactive_background: self
632 .tab_inactive_background
633 .as_ref()
634 .and_then(|color| try_parse_color(color).ok()),
635 tab_active_background: self
636 .tab_active_background
637 .as_ref()
638 .and_then(|color| try_parse_color(color).ok()),
639 search_match_background: self
640 .search_match_background
641 .as_ref()
642 .and_then(|color| try_parse_color(color).ok()),
643 panel_background: self
644 .panel_background
645 .as_ref()
646 .and_then(|color| try_parse_color(color).ok()),
647 panel_focused_border: self
648 .panel_focused_border
649 .as_ref()
650 .and_then(|color| try_parse_color(color).ok()),
651 pane_focused_border: self
652 .pane_focused_border
653 .as_ref()
654 .and_then(|color| try_parse_color(color).ok()),
655 scrollbar_thumb_background: self
656 .scrollbar_thumb_background
657 .as_ref()
658 .and_then(|color| try_parse_color(color).ok()),
659 scrollbar_thumb_hover_background: self
660 .scrollbar_thumb_hover_background
661 .as_ref()
662 .and_then(|color| try_parse_color(color).ok()),
663 scrollbar_thumb_border: self
664 .scrollbar_thumb_border
665 .as_ref()
666 .and_then(|color| try_parse_color(color).ok()),
667 scrollbar_track_background: self
668 .scrollbar_track_background
669 .as_ref()
670 .and_then(|color| try_parse_color(color).ok()),
671 scrollbar_track_border: self
672 .scrollbar_track_border
673 .as_ref()
674 .and_then(|color| try_parse_color(color).ok()),
675 editor_foreground: self
676 .editor_foreground
677 .as_ref()
678 .and_then(|color| try_parse_color(color).ok()),
679 editor_background: self
680 .editor_background
681 .as_ref()
682 .and_then(|color| try_parse_color(color).ok()),
683 editor_gutter_background: self
684 .editor_gutter_background
685 .as_ref()
686 .and_then(|color| try_parse_color(color).ok()),
687 editor_subheader_background: self
688 .editor_subheader_background
689 .as_ref()
690 .and_then(|color| try_parse_color(color).ok()),
691 editor_active_line_background: self
692 .editor_active_line_background
693 .as_ref()
694 .and_then(|color| try_parse_color(color).ok()),
695 editor_highlighted_line_background: self
696 .editor_highlighted_line_background
697 .as_ref()
698 .and_then(|color| try_parse_color(color).ok()),
699 editor_line_number: self
700 .editor_line_number
701 .as_ref()
702 .and_then(|color| try_parse_color(color).ok()),
703 editor_active_line_number: self
704 .editor_active_line_number
705 .as_ref()
706 .and_then(|color| try_parse_color(color).ok()),
707 editor_invisible: self
708 .editor_invisible
709 .as_ref()
710 .and_then(|color| try_parse_color(color).ok()),
711 editor_wrap_guide: self
712 .editor_wrap_guide
713 .as_ref()
714 .and_then(|color| try_parse_color(color).ok()),
715 editor_active_wrap_guide: self
716 .editor_active_wrap_guide
717 .as_ref()
718 .and_then(|color| try_parse_color(color).ok()),
719 editor_document_highlight_read_background: self
720 .editor_document_highlight_read_background
721 .as_ref()
722 .and_then(|color| try_parse_color(color).ok()),
723 editor_document_highlight_write_background: self
724 .editor_document_highlight_write_background
725 .as_ref()
726 .and_then(|color| try_parse_color(color).ok()),
727 terminal_background: self
728 .terminal_background
729 .as_ref()
730 .and_then(|color| try_parse_color(color).ok()),
731 terminal_foreground: self
732 .terminal_foreground
733 .as_ref()
734 .and_then(|color| try_parse_color(color).ok()),
735 terminal_bright_foreground: self
736 .terminal_bright_foreground
737 .as_ref()
738 .and_then(|color| try_parse_color(color).ok()),
739 terminal_dim_foreground: self
740 .terminal_dim_foreground
741 .as_ref()
742 .and_then(|color| try_parse_color(color).ok()),
743 terminal_ansi_black: self
744 .terminal_ansi_black
745 .as_ref()
746 .and_then(|color| try_parse_color(color).ok()),
747 terminal_ansi_bright_black: self
748 .terminal_ansi_bright_black
749 .as_ref()
750 .and_then(|color| try_parse_color(color).ok()),
751 terminal_ansi_dim_black: self
752 .terminal_ansi_dim_black
753 .as_ref()
754 .and_then(|color| try_parse_color(color).ok()),
755 terminal_ansi_red: self
756 .terminal_ansi_red
757 .as_ref()
758 .and_then(|color| try_parse_color(color).ok()),
759 terminal_ansi_bright_red: self
760 .terminal_ansi_bright_red
761 .as_ref()
762 .and_then(|color| try_parse_color(color).ok()),
763 terminal_ansi_dim_red: self
764 .terminal_ansi_dim_red
765 .as_ref()
766 .and_then(|color| try_parse_color(color).ok()),
767 terminal_ansi_green: self
768 .terminal_ansi_green
769 .as_ref()
770 .and_then(|color| try_parse_color(color).ok()),
771 terminal_ansi_bright_green: self
772 .terminal_ansi_bright_green
773 .as_ref()
774 .and_then(|color| try_parse_color(color).ok()),
775 terminal_ansi_dim_green: self
776 .terminal_ansi_dim_green
777 .as_ref()
778 .and_then(|color| try_parse_color(color).ok()),
779 terminal_ansi_yellow: self
780 .terminal_ansi_yellow
781 .as_ref()
782 .and_then(|color| try_parse_color(color).ok()),
783 terminal_ansi_bright_yellow: self
784 .terminal_ansi_bright_yellow
785 .as_ref()
786 .and_then(|color| try_parse_color(color).ok()),
787 terminal_ansi_dim_yellow: self
788 .terminal_ansi_dim_yellow
789 .as_ref()
790 .and_then(|color| try_parse_color(color).ok()),
791 terminal_ansi_blue: self
792 .terminal_ansi_blue
793 .as_ref()
794 .and_then(|color| try_parse_color(color).ok()),
795 terminal_ansi_bright_blue: self
796 .terminal_ansi_bright_blue
797 .as_ref()
798 .and_then(|color| try_parse_color(color).ok()),
799 terminal_ansi_dim_blue: self
800 .terminal_ansi_dim_blue
801 .as_ref()
802 .and_then(|color| try_parse_color(color).ok()),
803 terminal_ansi_magenta: self
804 .terminal_ansi_magenta
805 .as_ref()
806 .and_then(|color| try_parse_color(color).ok()),
807 terminal_ansi_bright_magenta: self
808 .terminal_ansi_bright_magenta
809 .as_ref()
810 .and_then(|color| try_parse_color(color).ok()),
811 terminal_ansi_dim_magenta: self
812 .terminal_ansi_dim_magenta
813 .as_ref()
814 .and_then(|color| try_parse_color(color).ok()),
815 terminal_ansi_cyan: self
816 .terminal_ansi_cyan
817 .as_ref()
818 .and_then(|color| try_parse_color(color).ok()),
819 terminal_ansi_bright_cyan: self
820 .terminal_ansi_bright_cyan
821 .as_ref()
822 .and_then(|color| try_parse_color(color).ok()),
823 terminal_ansi_dim_cyan: self
824 .terminal_ansi_dim_cyan
825 .as_ref()
826 .and_then(|color| try_parse_color(color).ok()),
827 terminal_ansi_white: self
828 .terminal_ansi_white
829 .as_ref()
830 .and_then(|color| try_parse_color(color).ok()),
831 terminal_ansi_bright_white: self
832 .terminal_ansi_bright_white
833 .as_ref()
834 .and_then(|color| try_parse_color(color).ok()),
835 terminal_ansi_dim_white: self
836 .terminal_ansi_dim_white
837 .as_ref()
838 .and_then(|color| try_parse_color(color).ok()),
839 link_text_hover: self
840 .link_text_hover
841 .as_ref()
842 .and_then(|color| try_parse_color(color).ok()),
843 }
844 }
845}
846
847#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
848#[serde(default)]
849pub struct StatusColorsContent {
850 /// Indicates some kind of conflict, like a file changed on disk while it was open, or
851 /// merge conflicts in a Git repository.
852 #[serde(rename = "conflict")]
853 pub conflict: Option<String>,
854
855 #[serde(rename = "conflict.background")]
856 pub conflict_background: Option<String>,
857
858 #[serde(rename = "conflict.border")]
859 pub conflict_border: Option<String>,
860
861 /// Indicates something new, like a new file added to a Git repository.
862 #[serde(rename = "created")]
863 pub created: Option<String>,
864
865 #[serde(rename = "created.background")]
866 pub created_background: Option<String>,
867
868 #[serde(rename = "created.border")]
869 pub created_border: Option<String>,
870
871 /// Indicates that something no longer exists, like a deleted file.
872 #[serde(rename = "deleted")]
873 pub deleted: Option<String>,
874
875 #[serde(rename = "deleted.background")]
876 pub deleted_background: Option<String>,
877
878 #[serde(rename = "deleted.border")]
879 pub deleted_border: Option<String>,
880
881 /// Indicates a system error, a failed operation or a diagnostic error.
882 #[serde(rename = "error")]
883 pub error: Option<String>,
884
885 #[serde(rename = "error.background")]
886 pub error_background: Option<String>,
887
888 #[serde(rename = "error.border")]
889 pub error_border: Option<String>,
890
891 /// Represents a hidden status, such as a file being hidden in a file tree.
892 #[serde(rename = "hidden")]
893 pub hidden: Option<String>,
894
895 #[serde(rename = "hidden.background")]
896 pub hidden_background: Option<String>,
897
898 #[serde(rename = "hidden.border")]
899 pub hidden_border: Option<String>,
900
901 /// Indicates a hint or some kind of additional information.
902 #[serde(rename = "hint")]
903 pub hint: Option<String>,
904
905 #[serde(rename = "hint.background")]
906 pub hint_background: Option<String>,
907
908 #[serde(rename = "hint.border")]
909 pub hint_border: Option<String>,
910
911 /// Indicates that something is deliberately ignored, such as a file or operation ignored by Git.
912 #[serde(rename = "ignored")]
913 pub ignored: Option<String>,
914
915 #[serde(rename = "ignored.background")]
916 pub ignored_background: Option<String>,
917
918 #[serde(rename = "ignored.border")]
919 pub ignored_border: Option<String>,
920
921 /// Represents informational status updates or messages.
922 #[serde(rename = "info")]
923 pub info: Option<String>,
924
925 #[serde(rename = "info.background")]
926 pub info_background: Option<String>,
927
928 #[serde(rename = "info.border")]
929 pub info_border: Option<String>,
930
931 /// Indicates a changed or altered status, like a file that has been edited.
932 #[serde(rename = "modified")]
933 pub modified: Option<String>,
934
935 #[serde(rename = "modified.background")]
936 pub modified_background: Option<String>,
937
938 #[serde(rename = "modified.border")]
939 pub modified_border: Option<String>,
940
941 /// Indicates something that is predicted, like automatic code completion, or generated code.
942 #[serde(rename = "predictive")]
943 pub predictive: Option<String>,
944
945 #[serde(rename = "predictive.background")]
946 pub predictive_background: Option<String>,
947
948 #[serde(rename = "predictive.border")]
949 pub predictive_border: Option<String>,
950
951 /// Represents a renamed status, such as a file that has been renamed.
952 #[serde(rename = "renamed")]
953 pub renamed: Option<String>,
954
955 #[serde(rename = "renamed.background")]
956 pub renamed_background: Option<String>,
957
958 #[serde(rename = "renamed.border")]
959 pub renamed_border: Option<String>,
960
961 /// Indicates a successful operation or task completion.
962 #[serde(rename = "success")]
963 pub success: Option<String>,
964
965 #[serde(rename = "success.background")]
966 pub success_background: Option<String>,
967
968 #[serde(rename = "success.border")]
969 pub success_border: Option<String>,
970
971 /// Indicates some kind of unreachable status, like a block of code that can never be reached.
972 #[serde(rename = "unreachable")]
973 pub unreachable: Option<String>,
974
975 #[serde(rename = "unreachable.background")]
976 pub unreachable_background: Option<String>,
977
978 #[serde(rename = "unreachable.border")]
979 pub unreachable_border: Option<String>,
980
981 /// Represents a warning status, like an operation that is about to fail.
982 #[serde(rename = "warning")]
983 pub warning: Option<String>,
984
985 #[serde(rename = "warning.background")]
986 pub warning_background: Option<String>,
987
988 #[serde(rename = "warning.border")]
989 pub warning_border: Option<String>,
990}
991
992impl StatusColorsContent {
993 /// Returns a [`StatusColorsRefinement`] based on the colors in the [`StatusColorsContent`].
994 pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
995 StatusColorsRefinement {
996 conflict: self
997 .conflict
998 .as_ref()
999 .and_then(|color| try_parse_color(color).ok()),
1000 conflict_background: self
1001 .conflict_background
1002 .as_ref()
1003 .and_then(|color| try_parse_color(color).ok()),
1004 conflict_border: self
1005 .conflict_border
1006 .as_ref()
1007 .and_then(|color| try_parse_color(color).ok()),
1008 created: self
1009 .created
1010 .as_ref()
1011 .and_then(|color| try_parse_color(color).ok()),
1012 created_background: self
1013 .created_background
1014 .as_ref()
1015 .and_then(|color| try_parse_color(color).ok()),
1016 created_border: self
1017 .created_border
1018 .as_ref()
1019 .and_then(|color| try_parse_color(color).ok()),
1020 deleted: self
1021 .deleted
1022 .as_ref()
1023 .and_then(|color| try_parse_color(color).ok()),
1024 deleted_background: self
1025 .deleted_background
1026 .as_ref()
1027 .and_then(|color| try_parse_color(color).ok()),
1028 deleted_border: self
1029 .deleted_border
1030 .as_ref()
1031 .and_then(|color| try_parse_color(color).ok()),
1032 error: self
1033 .error
1034 .as_ref()
1035 .and_then(|color| try_parse_color(color).ok()),
1036 error_background: self
1037 .error_background
1038 .as_ref()
1039 .and_then(|color| try_parse_color(color).ok()),
1040 error_border: self
1041 .error_border
1042 .as_ref()
1043 .and_then(|color| try_parse_color(color).ok()),
1044 hidden: self
1045 .hidden
1046 .as_ref()
1047 .and_then(|color| try_parse_color(color).ok()),
1048 hidden_background: self
1049 .hidden_background
1050 .as_ref()
1051 .and_then(|color| try_parse_color(color).ok()),
1052 hidden_border: self
1053 .hidden_border
1054 .as_ref()
1055 .and_then(|color| try_parse_color(color).ok()),
1056 hint: self
1057 .hint
1058 .as_ref()
1059 .and_then(|color| try_parse_color(color).ok()),
1060 hint_background: self
1061 .hint_background
1062 .as_ref()
1063 .and_then(|color| try_parse_color(color).ok()),
1064 hint_border: self
1065 .hint_border
1066 .as_ref()
1067 .and_then(|color| try_parse_color(color).ok()),
1068 ignored: self
1069 .ignored
1070 .as_ref()
1071 .and_then(|color| try_parse_color(color).ok()),
1072 ignored_background: self
1073 .ignored_background
1074 .as_ref()
1075 .and_then(|color| try_parse_color(color).ok()),
1076 ignored_border: self
1077 .ignored_border
1078 .as_ref()
1079 .and_then(|color| try_parse_color(color).ok()),
1080 info: self
1081 .info
1082 .as_ref()
1083 .and_then(|color| try_parse_color(color).ok()),
1084 info_background: self
1085 .info_background
1086 .as_ref()
1087 .and_then(|color| try_parse_color(color).ok()),
1088 info_border: self
1089 .info_border
1090 .as_ref()
1091 .and_then(|color| try_parse_color(color).ok()),
1092 modified: self
1093 .modified
1094 .as_ref()
1095 .and_then(|color| try_parse_color(color).ok()),
1096 modified_background: self
1097 .modified_background
1098 .as_ref()
1099 .and_then(|color| try_parse_color(color).ok()),
1100 modified_border: self
1101 .modified_border
1102 .as_ref()
1103 .and_then(|color| try_parse_color(color).ok()),
1104 predictive: self
1105 .predictive
1106 .as_ref()
1107 .and_then(|color| try_parse_color(color).ok()),
1108 predictive_background: self
1109 .predictive_background
1110 .as_ref()
1111 .and_then(|color| try_parse_color(color).ok()),
1112 predictive_border: self
1113 .predictive_border
1114 .as_ref()
1115 .and_then(|color| try_parse_color(color).ok()),
1116 renamed: self
1117 .renamed
1118 .as_ref()
1119 .and_then(|color| try_parse_color(color).ok()),
1120 renamed_background: self
1121 .renamed_background
1122 .as_ref()
1123 .and_then(|color| try_parse_color(color).ok()),
1124 renamed_border: self
1125 .renamed_border
1126 .as_ref()
1127 .and_then(|color| try_parse_color(color).ok()),
1128 success: self
1129 .success
1130 .as_ref()
1131 .and_then(|color| try_parse_color(color).ok()),
1132 success_background: self
1133 .success_background
1134 .as_ref()
1135 .and_then(|color| try_parse_color(color).ok()),
1136 success_border: self
1137 .success_border
1138 .as_ref()
1139 .and_then(|color| try_parse_color(color).ok()),
1140 unreachable: self
1141 .unreachable
1142 .as_ref()
1143 .and_then(|color| try_parse_color(color).ok()),
1144 unreachable_background: self
1145 .unreachable_background
1146 .as_ref()
1147 .and_then(|color| try_parse_color(color).ok()),
1148 unreachable_border: self
1149 .unreachable_border
1150 .as_ref()
1151 .and_then(|color| try_parse_color(color).ok()),
1152 warning: self
1153 .warning
1154 .as_ref()
1155 .and_then(|color| try_parse_color(color).ok()),
1156 warning_background: self
1157 .warning_background
1158 .as_ref()
1159 .and_then(|color| try_parse_color(color).ok()),
1160 warning_border: self
1161 .warning_border
1162 .as_ref()
1163 .and_then(|color| try_parse_color(color).ok()),
1164 }
1165 }
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1169pub struct PlayerColorContent {
1170 pub cursor: Option<String>,
1171 pub background: Option<String>,
1172 pub selection: Option<String>,
1173}
1174
1175#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
1176#[serde(rename_all = "snake_case")]
1177pub enum FontStyleContent {
1178 Normal,
1179 Italic,
1180 Oblique,
1181}
1182
1183impl From<FontStyleContent> for FontStyle {
1184 fn from(value: FontStyleContent) -> Self {
1185 match value {
1186 FontStyleContent::Normal => FontStyle::Normal,
1187 FontStyleContent::Italic => FontStyle::Italic,
1188 FontStyleContent::Oblique => FontStyle::Oblique,
1189 }
1190 }
1191}
1192
1193#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
1194#[repr(u16)]
1195pub enum FontWeightContent {
1196 Thin = 100,
1197 ExtraLight = 200,
1198 Light = 300,
1199 Normal = 400,
1200 Medium = 500,
1201 Semibold = 600,
1202 Bold = 700,
1203 ExtraBold = 800,
1204 Black = 900,
1205}
1206
1207impl JsonSchema for FontWeightContent {
1208 fn schema_name() -> String {
1209 "FontWeightContent".to_owned()
1210 }
1211
1212 fn is_referenceable() -> bool {
1213 false
1214 }
1215
1216 fn json_schema(_: &mut SchemaGenerator) -> Schema {
1217 SchemaObject {
1218 enum_values: Some(vec![
1219 100.into(),
1220 200.into(),
1221 300.into(),
1222 400.into(),
1223 500.into(),
1224 600.into(),
1225 700.into(),
1226 800.into(),
1227 900.into(),
1228 ]),
1229 ..Default::default()
1230 }
1231 .into()
1232 }
1233}
1234
1235impl From<FontWeightContent> for FontWeight {
1236 fn from(value: FontWeightContent) -> Self {
1237 match value {
1238 FontWeightContent::Thin => FontWeight::THIN,
1239 FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT,
1240 FontWeightContent::Light => FontWeight::LIGHT,
1241 FontWeightContent::Normal => FontWeight::NORMAL,
1242 FontWeightContent::Medium => FontWeight::MEDIUM,
1243 FontWeightContent::Semibold => FontWeight::SEMIBOLD,
1244 FontWeightContent::Bold => FontWeight::BOLD,
1245 FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD,
1246 FontWeightContent::Black => FontWeight::BLACK,
1247 }
1248 }
1249}
1250
1251#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
1252#[serde(default)]
1253pub struct HighlightStyleContent {
1254 pub color: Option<String>,
1255
1256 #[serde(deserialize_with = "treat_error_as_none")]
1257 pub font_style: Option<FontStyleContent>,
1258
1259 #[serde(deserialize_with = "treat_error_as_none")]
1260 pub font_weight: Option<FontWeightContent>,
1261}
1262
1263impl HighlightStyleContent {
1264 pub fn is_empty(&self) -> bool {
1265 self.color.is_none() && self.font_style.is_none() && self.font_weight.is_none()
1266 }
1267}
1268
1269fn treat_error_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
1270where
1271 T: Deserialize<'de>,
1272 D: Deserializer<'de>,
1273{
1274 let value: Value = Deserialize::deserialize(deserializer)?;
1275 Ok(T::deserialize(value).ok())
1276}