1#![allow(missing_docs)]
2
3use gpui::{HighlightStyle, Hsla};
4use palette::FromColor;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use settings::IntoGpui;
8pub use settings::{
9 FontStyleContent, HighlightStyleContent, StatusColorsContent, ThemeColorsContent,
10 ThemeStyleContent,
11};
12pub use settings::{FontWeightContent, WindowBackgroundContent};
13
14use theme::{StatusColorsRefinement, ThemeColorsRefinement};
15
16const LIGHT_DIFF_HUNK_FILLED_OPACITY: f32 = 0.16;
17const LIGHT_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY: f32 = 0.08;
18const LIGHT_DIFF_HUNK_HOLLOW_BORDER_OPACITY: f32 = 0.48;
19const DARK_DIFF_HUNK_FILLED_OPACITY: f32 = 0.12;
20const DARK_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY: f32 = 0.06;
21const DARK_DIFF_HUNK_HOLLOW_BORDER_OPACITY: f32 = 0.36;
22
23/// The content of a serialized theme family.
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct ThemeFamilyContent {
26 pub name: String,
27 pub author: String,
28 pub themes: Vec<ThemeContent>,
29}
30
31/// The content of a serialized theme.
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
33pub struct ThemeContent {
34 pub name: String,
35 pub appearance: theme::AppearanceContent,
36 pub style: settings::ThemeStyleContent,
37}
38
39/// Returns the syntax style overrides in the [`ThemeContent`].
40pub fn syntax_overrides(this: &settings::ThemeStyleContent) -> Vec<(String, HighlightStyle)> {
41 this.syntax
42 .iter()
43 .map(|(key, style)| {
44 (
45 key.clone(),
46 HighlightStyle {
47 color: style
48 .color
49 .as_ref()
50 .and_then(|color| theme::try_parse_color(color).ok()),
51 background_color: style
52 .background_color
53 .as_ref()
54 .and_then(|color| theme::try_parse_color(color).ok()),
55 font_style: style.font_style.map(|s| s.into_gpui()),
56 font_weight: style.font_weight.map(|w| w.into_gpui()),
57 ..Default::default()
58 },
59 )
60 })
61 .collect()
62}
63
64pub fn status_colors_refinement(colors: &settings::StatusColorsContent) -> StatusColorsRefinement {
65 StatusColorsRefinement {
66 conflict: colors
67 .conflict
68 .as_ref()
69 .and_then(|color| try_parse_color(color).ok()),
70 conflict_background: colors
71 .conflict_background
72 .as_ref()
73 .and_then(|color| try_parse_color(color).ok()),
74 conflict_border: colors
75 .conflict_border
76 .as_ref()
77 .and_then(|color| try_parse_color(color).ok()),
78 created: colors
79 .created
80 .as_ref()
81 .and_then(|color| try_parse_color(color).ok()),
82 created_background: colors
83 .created_background
84 .as_ref()
85 .and_then(|color| try_parse_color(color).ok()),
86 created_border: colors
87 .created_border
88 .as_ref()
89 .and_then(|color| try_parse_color(color).ok()),
90 deleted: colors
91 .deleted
92 .as_ref()
93 .and_then(|color| try_parse_color(color).ok()),
94 deleted_background: colors
95 .deleted_background
96 .as_ref()
97 .and_then(|color| try_parse_color(color).ok()),
98 deleted_border: colors
99 .deleted_border
100 .as_ref()
101 .and_then(|color| try_parse_color(color).ok()),
102 error: colors
103 .error
104 .as_ref()
105 .and_then(|color| try_parse_color(color).ok()),
106 error_background: colors
107 .error_background
108 .as_ref()
109 .and_then(|color| try_parse_color(color).ok()),
110 error_border: colors
111 .error_border
112 .as_ref()
113 .and_then(|color| try_parse_color(color).ok()),
114 hidden: colors
115 .hidden
116 .as_ref()
117 .and_then(|color| try_parse_color(color).ok()),
118 hidden_background: colors
119 .hidden_background
120 .as_ref()
121 .and_then(|color| try_parse_color(color).ok()),
122 hidden_border: colors
123 .hidden_border
124 .as_ref()
125 .and_then(|color| try_parse_color(color).ok()),
126 hint: colors
127 .hint
128 .as_ref()
129 .and_then(|color| try_parse_color(color).ok()),
130 hint_background: colors
131 .hint_background
132 .as_ref()
133 .and_then(|color| try_parse_color(color).ok()),
134 hint_border: colors
135 .hint_border
136 .as_ref()
137 .and_then(|color| try_parse_color(color).ok()),
138 ignored: colors
139 .ignored
140 .as_ref()
141 .and_then(|color| try_parse_color(color).ok()),
142 ignored_background: colors
143 .ignored_background
144 .as_ref()
145 .and_then(|color| try_parse_color(color).ok()),
146 ignored_border: colors
147 .ignored_border
148 .as_ref()
149 .and_then(|color| try_parse_color(color).ok()),
150 info: colors
151 .info
152 .as_ref()
153 .and_then(|color| try_parse_color(color).ok()),
154 info_background: colors
155 .info_background
156 .as_ref()
157 .and_then(|color| try_parse_color(color).ok()),
158 info_border: colors
159 .info_border
160 .as_ref()
161 .and_then(|color| try_parse_color(color).ok()),
162 modified: colors
163 .modified
164 .as_ref()
165 .and_then(|color| try_parse_color(color).ok()),
166 modified_background: colors
167 .modified_background
168 .as_ref()
169 .and_then(|color| try_parse_color(color).ok()),
170 modified_border: colors
171 .modified_border
172 .as_ref()
173 .and_then(|color| try_parse_color(color).ok()),
174 predictive: colors
175 .predictive
176 .as_ref()
177 .and_then(|color| try_parse_color(color).ok()),
178 predictive_background: colors
179 .predictive_background
180 .as_ref()
181 .and_then(|color| try_parse_color(color).ok()),
182 predictive_border: colors
183 .predictive_border
184 .as_ref()
185 .and_then(|color| try_parse_color(color).ok()),
186 renamed: colors
187 .renamed
188 .as_ref()
189 .and_then(|color| try_parse_color(color).ok()),
190 renamed_background: colors
191 .renamed_background
192 .as_ref()
193 .and_then(|color| try_parse_color(color).ok()),
194 renamed_border: colors
195 .renamed_border
196 .as_ref()
197 .and_then(|color| try_parse_color(color).ok()),
198 success: colors
199 .success
200 .as_ref()
201 .and_then(|color| try_parse_color(color).ok()),
202 success_background: colors
203 .success_background
204 .as_ref()
205 .and_then(|color| try_parse_color(color).ok()),
206 success_border: colors
207 .success_border
208 .as_ref()
209 .and_then(|color| try_parse_color(color).ok()),
210 unreachable: colors
211 .unreachable
212 .as_ref()
213 .and_then(|color| try_parse_color(color).ok()),
214 unreachable_background: colors
215 .unreachable_background
216 .as_ref()
217 .and_then(|color| try_parse_color(color).ok()),
218 unreachable_border: colors
219 .unreachable_border
220 .as_ref()
221 .and_then(|color| try_parse_color(color).ok()),
222 warning: colors
223 .warning
224 .as_ref()
225 .and_then(|color| try_parse_color(color).ok()),
226 warning_background: colors
227 .warning_background
228 .as_ref()
229 .and_then(|color| try_parse_color(color).ok()),
230 warning_border: colors
231 .warning_border
232 .as_ref()
233 .and_then(|color| try_parse_color(color).ok()),
234 }
235}
236
237pub fn theme_colors_refinement(
238 this: &settings::ThemeColorsContent,
239 status_colors: &StatusColorsRefinement,
240 is_light: bool,
241) -> ThemeColorsRefinement {
242 let border = this
243 .border
244 .as_ref()
245 .and_then(|color| try_parse_color(color).ok());
246 let editor_document_highlight_read_background = this
247 .editor_document_highlight_read_background
248 .as_ref()
249 .and_then(|color| try_parse_color(color).ok());
250 let scrollbar_thumb_background = this
251 .scrollbar_thumb_background
252 .as_ref()
253 .and_then(|color| try_parse_color(color).ok())
254 .or_else(|| {
255 this.deprecated_scrollbar_thumb_background
256 .as_ref()
257 .and_then(|color| try_parse_color(color).ok())
258 });
259 let scrollbar_thumb_hover_background = this
260 .scrollbar_thumb_hover_background
261 .as_ref()
262 .and_then(|color| try_parse_color(color).ok());
263 let scrollbar_thumb_active_background = this
264 .scrollbar_thumb_active_background
265 .as_ref()
266 .and_then(|color| try_parse_color(color).ok())
267 .or(scrollbar_thumb_background);
268 let scrollbar_thumb_border = this
269 .scrollbar_thumb_border
270 .as_ref()
271 .and_then(|color| try_parse_color(color).ok());
272 let element_hover = this
273 .element_hover
274 .as_ref()
275 .and_then(|color| try_parse_color(color).ok());
276 let panel_background = this
277 .panel_background
278 .as_ref()
279 .and_then(|color| try_parse_color(color).ok());
280 let search_match_background = this
281 .search_match_background
282 .as_ref()
283 .and_then(|color| try_parse_color(color).ok());
284 let search_active_match_background = this
285 .search_active_match_background
286 .as_ref()
287 .and_then(|color| try_parse_color(color).ok())
288 .or(search_match_background);
289 let version_control_added = this
290 .version_control_added
291 .as_ref()
292 .and_then(|color| try_parse_color(color).ok())
293 .or(status_colors.created);
294 let version_control_deleted = this
295 .version_control_deleted
296 .as_ref()
297 .and_then(|color| try_parse_color(color).ok())
298 .or(status_colors.deleted);
299 let (hunk_fill, hunk_hollow_bg, hunk_hollow_border) = if is_light {
300 (
301 LIGHT_DIFF_HUNK_FILLED_OPACITY,
302 LIGHT_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY,
303 LIGHT_DIFF_HUNK_HOLLOW_BORDER_OPACITY,
304 )
305 } else {
306 (
307 DARK_DIFF_HUNK_FILLED_OPACITY,
308 DARK_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY,
309 DARK_DIFF_HUNK_HOLLOW_BORDER_OPACITY,
310 )
311 };
312 ThemeColorsRefinement {
313 border,
314 border_variant: this
315 .border_variant
316 .as_ref()
317 .and_then(|color| try_parse_color(color).ok()),
318 border_focused: this
319 .border_focused
320 .as_ref()
321 .and_then(|color| try_parse_color(color).ok()),
322 border_selected: this
323 .border_selected
324 .as_ref()
325 .and_then(|color| try_parse_color(color).ok()),
326 border_transparent: this
327 .border_transparent
328 .as_ref()
329 .and_then(|color| try_parse_color(color).ok()),
330 border_disabled: this
331 .border_disabled
332 .as_ref()
333 .and_then(|color| try_parse_color(color).ok()),
334 elevated_surface_background: this
335 .elevated_surface_background
336 .as_ref()
337 .and_then(|color| try_parse_color(color).ok()),
338 surface_background: this
339 .surface_background
340 .as_ref()
341 .and_then(|color| try_parse_color(color).ok()),
342 background: this
343 .background
344 .as_ref()
345 .and_then(|color| try_parse_color(color).ok()),
346 element_background: this
347 .element_background
348 .as_ref()
349 .and_then(|color| try_parse_color(color).ok()),
350 element_hover,
351 element_active: this
352 .element_active
353 .as_ref()
354 .and_then(|color| try_parse_color(color).ok()),
355 element_selected: this
356 .element_selected
357 .as_ref()
358 .and_then(|color| try_parse_color(color).ok()),
359 element_disabled: this
360 .element_disabled
361 .as_ref()
362 .and_then(|color| try_parse_color(color).ok()),
363 element_selection_background: this
364 .element_selection_background
365 .as_ref()
366 .and_then(|color| try_parse_color(color).ok()),
367 drop_target_background: this
368 .drop_target_background
369 .as_ref()
370 .and_then(|color| try_parse_color(color).ok()),
371 drop_target_border: this
372 .drop_target_border
373 .as_ref()
374 .and_then(|color| try_parse_color(color).ok()),
375 ghost_element_background: this
376 .ghost_element_background
377 .as_ref()
378 .and_then(|color| try_parse_color(color).ok()),
379 ghost_element_hover: this
380 .ghost_element_hover
381 .as_ref()
382 .and_then(|color| try_parse_color(color).ok()),
383 ghost_element_active: this
384 .ghost_element_active
385 .as_ref()
386 .and_then(|color| try_parse_color(color).ok()),
387 ghost_element_selected: this
388 .ghost_element_selected
389 .as_ref()
390 .and_then(|color| try_parse_color(color).ok()),
391 ghost_element_disabled: this
392 .ghost_element_disabled
393 .as_ref()
394 .and_then(|color| try_parse_color(color).ok()),
395 text: this
396 .text
397 .as_ref()
398 .and_then(|color| try_parse_color(color).ok()),
399 text_muted: this
400 .text_muted
401 .as_ref()
402 .and_then(|color| try_parse_color(color).ok()),
403 text_placeholder: this
404 .text_placeholder
405 .as_ref()
406 .and_then(|color| try_parse_color(color).ok()),
407 text_disabled: this
408 .text_disabled
409 .as_ref()
410 .and_then(|color| try_parse_color(color).ok()),
411 text_accent: this
412 .text_accent
413 .as_ref()
414 .and_then(|color| try_parse_color(color).ok()),
415 icon: this
416 .icon
417 .as_ref()
418 .and_then(|color| try_parse_color(color).ok()),
419 icon_muted: this
420 .icon_muted
421 .as_ref()
422 .and_then(|color| try_parse_color(color).ok()),
423 icon_disabled: this
424 .icon_disabled
425 .as_ref()
426 .and_then(|color| try_parse_color(color).ok()),
427 icon_placeholder: this
428 .icon_placeholder
429 .as_ref()
430 .and_then(|color| try_parse_color(color).ok()),
431 icon_accent: this
432 .icon_accent
433 .as_ref()
434 .and_then(|color| try_parse_color(color).ok()),
435 debugger_accent: this
436 .debugger_accent
437 .as_ref()
438 .and_then(|color| try_parse_color(color).ok()),
439 status_bar_background: this
440 .status_bar_background
441 .as_ref()
442 .and_then(|color| try_parse_color(color).ok()),
443 title_bar_background: this
444 .title_bar_background
445 .as_ref()
446 .and_then(|color| try_parse_color(color).ok()),
447 title_bar_inactive_background: this
448 .title_bar_inactive_background
449 .as_ref()
450 .and_then(|color| try_parse_color(color).ok()),
451 toolbar_background: this
452 .toolbar_background
453 .as_ref()
454 .and_then(|color| try_parse_color(color).ok()),
455 tab_bar_background: this
456 .tab_bar_background
457 .as_ref()
458 .and_then(|color| try_parse_color(color).ok()),
459 tab_inactive_background: this
460 .tab_inactive_background
461 .as_ref()
462 .and_then(|color| try_parse_color(color).ok()),
463 tab_active_background: this
464 .tab_active_background
465 .as_ref()
466 .and_then(|color| try_parse_color(color).ok()),
467 search_match_background,
468 search_active_match_background,
469 panel_background,
470 panel_focused_border: this
471 .panel_focused_border
472 .as_ref()
473 .and_then(|color| try_parse_color(color).ok()),
474 panel_indent_guide: this
475 .panel_indent_guide
476 .as_ref()
477 .and_then(|color| try_parse_color(color).ok()),
478 panel_indent_guide_hover: this
479 .panel_indent_guide_hover
480 .as_ref()
481 .and_then(|color| try_parse_color(color).ok()),
482 panel_indent_guide_active: this
483 .panel_indent_guide_active
484 .as_ref()
485 .and_then(|color| try_parse_color(color).ok()),
486 panel_overlay_background: this
487 .panel_overlay_background
488 .as_ref()
489 .and_then(|color| try_parse_color(color).ok())
490 .or(panel_background.map(ensure_opaque)),
491 panel_overlay_hover: this
492 .panel_overlay_hover
493 .as_ref()
494 .and_then(|color| try_parse_color(color).ok())
495 .or(panel_background
496 .zip(element_hover)
497 .map(|(panel_bg, hover_bg)| panel_bg.blend(hover_bg))
498 .map(ensure_opaque)),
499 pane_focused_border: this
500 .pane_focused_border
501 .as_ref()
502 .and_then(|color| try_parse_color(color).ok()),
503 pane_group_border: this
504 .pane_group_border
505 .as_ref()
506 .and_then(|color| try_parse_color(color).ok())
507 .or(border),
508 scrollbar_thumb_background,
509 scrollbar_thumb_hover_background,
510 scrollbar_thumb_active_background,
511 scrollbar_thumb_border,
512 scrollbar_track_background: this
513 .scrollbar_track_background
514 .as_ref()
515 .and_then(|color| try_parse_color(color).ok()),
516 scrollbar_track_border: this
517 .scrollbar_track_border
518 .as_ref()
519 .and_then(|color| try_parse_color(color).ok()),
520 minimap_thumb_background: this
521 .minimap_thumb_background
522 .as_ref()
523 .and_then(|color| try_parse_color(color).ok())
524 .or(scrollbar_thumb_background.map(ensure_non_opaque)),
525 minimap_thumb_hover_background: this
526 .minimap_thumb_hover_background
527 .as_ref()
528 .and_then(|color| try_parse_color(color).ok())
529 .or(scrollbar_thumb_hover_background.map(ensure_non_opaque)),
530 minimap_thumb_active_background: this
531 .minimap_thumb_active_background
532 .as_ref()
533 .and_then(|color| try_parse_color(color).ok())
534 .or(scrollbar_thumb_active_background.map(ensure_non_opaque)),
535 minimap_thumb_border: this
536 .minimap_thumb_border
537 .as_ref()
538 .and_then(|color| try_parse_color(color).ok())
539 .or(scrollbar_thumb_border),
540 editor_foreground: this
541 .editor_foreground
542 .as_ref()
543 .and_then(|color| try_parse_color(color).ok()),
544 editor_background: this
545 .editor_background
546 .as_ref()
547 .and_then(|color| try_parse_color(color).ok()),
548 editor_gutter_background: this
549 .editor_gutter_background
550 .as_ref()
551 .and_then(|color| try_parse_color(color).ok()),
552 editor_subheader_background: this
553 .editor_subheader_background
554 .as_ref()
555 .and_then(|color| try_parse_color(color).ok()),
556 editor_active_line_background: this
557 .editor_active_line_background
558 .as_ref()
559 .and_then(|color| try_parse_color(color).ok()),
560 editor_highlighted_line_background: this
561 .editor_highlighted_line_background
562 .as_ref()
563 .and_then(|color| try_parse_color(color).ok()),
564 editor_debugger_active_line_background: this
565 .editor_debugger_active_line_background
566 .as_ref()
567 .and_then(|color| try_parse_color(color).ok()),
568 editor_line_number: this
569 .editor_line_number
570 .as_ref()
571 .and_then(|color| try_parse_color(color).ok()),
572 editor_hover_line_number: this
573 .editor_hover_line_number
574 .as_ref()
575 .and_then(|color| try_parse_color(color).ok()),
576 editor_active_line_number: this
577 .editor_active_line_number
578 .as_ref()
579 .and_then(|color| try_parse_color(color).ok()),
580 editor_invisible: this
581 .editor_invisible
582 .as_ref()
583 .and_then(|color| try_parse_color(color).ok()),
584 editor_wrap_guide: this
585 .editor_wrap_guide
586 .as_ref()
587 .and_then(|color| try_parse_color(color).ok()),
588 editor_active_wrap_guide: this
589 .editor_active_wrap_guide
590 .as_ref()
591 .and_then(|color| try_parse_color(color).ok()),
592 editor_indent_guide: this
593 .editor_indent_guide
594 .as_ref()
595 .and_then(|color| try_parse_color(color).ok()),
596 editor_indent_guide_active: this
597 .editor_indent_guide_active
598 .as_ref()
599 .and_then(|color| try_parse_color(color).ok()),
600 editor_document_highlight_read_background,
601 editor_document_highlight_write_background: this
602 .editor_document_highlight_write_background
603 .as_ref()
604 .and_then(|color| try_parse_color(color).ok()),
605 editor_document_highlight_bracket_background: this
606 .editor_document_highlight_bracket_background
607 .as_ref()
608 .and_then(|color| try_parse_color(color).ok())
609 .or(editor_document_highlight_read_background),
610 editor_diff_hunk_added_background: this
611 .editor_diff_hunk_added_background
612 .as_ref()
613 .and_then(|color| try_parse_color(color).ok())
614 .or_else(|| version_control_added.map(|c| c.opacity(hunk_fill))),
615 editor_diff_hunk_added_hollow_background: this
616 .editor_diff_hunk_added_hollow_background
617 .as_ref()
618 .and_then(|color| try_parse_color(color).ok())
619 .or_else(|| version_control_added.map(|c| c.opacity(hunk_hollow_bg))),
620 editor_diff_hunk_added_hollow_border: this
621 .editor_diff_hunk_added_hollow_border
622 .as_ref()
623 .and_then(|color| try_parse_color(color).ok())
624 .or_else(|| version_control_added.map(|c| c.opacity(hunk_hollow_border))),
625 editor_diff_hunk_deleted_background: this
626 .editor_diff_hunk_deleted_background
627 .as_ref()
628 .and_then(|color| try_parse_color(color).ok())
629 .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_fill))),
630 editor_diff_hunk_deleted_hollow_background: this
631 .editor_diff_hunk_deleted_hollow_background
632 .as_ref()
633 .and_then(|color| try_parse_color(color).ok())
634 .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_hollow_bg))),
635 editor_diff_hunk_deleted_hollow_border: this
636 .editor_diff_hunk_deleted_hollow_border
637 .as_ref()
638 .and_then(|color| try_parse_color(color).ok())
639 .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_hollow_border))),
640 terminal_background: this
641 .terminal_background
642 .as_ref()
643 .and_then(|color| try_parse_color(color).ok()),
644 terminal_ansi_background: this
645 .terminal_ansi_background
646 .as_ref()
647 .and_then(|color| try_parse_color(color).ok()),
648 terminal_foreground: this
649 .terminal_foreground
650 .as_ref()
651 .and_then(|color| try_parse_color(color).ok()),
652 terminal_bright_foreground: this
653 .terminal_bright_foreground
654 .as_ref()
655 .and_then(|color| try_parse_color(color).ok()),
656 terminal_dim_foreground: this
657 .terminal_dim_foreground
658 .as_ref()
659 .and_then(|color| try_parse_color(color).ok()),
660 terminal_ansi_black: this
661 .terminal_ansi_black
662 .as_ref()
663 .and_then(|color| try_parse_color(color).ok()),
664 terminal_ansi_bright_black: this
665 .terminal_ansi_bright_black
666 .as_ref()
667 .and_then(|color| try_parse_color(color).ok()),
668 terminal_ansi_dim_black: this
669 .terminal_ansi_dim_black
670 .as_ref()
671 .and_then(|color| try_parse_color(color).ok()),
672 terminal_ansi_red: this
673 .terminal_ansi_red
674 .as_ref()
675 .and_then(|color| try_parse_color(color).ok()),
676 terminal_ansi_bright_red: this
677 .terminal_ansi_bright_red
678 .as_ref()
679 .and_then(|color| try_parse_color(color).ok()),
680 terminal_ansi_dim_red: this
681 .terminal_ansi_dim_red
682 .as_ref()
683 .and_then(|color| try_parse_color(color).ok()),
684 terminal_ansi_green: this
685 .terminal_ansi_green
686 .as_ref()
687 .and_then(|color| try_parse_color(color).ok()),
688 terminal_ansi_bright_green: this
689 .terminal_ansi_bright_green
690 .as_ref()
691 .and_then(|color| try_parse_color(color).ok()),
692 terminal_ansi_dim_green: this
693 .terminal_ansi_dim_green
694 .as_ref()
695 .and_then(|color| try_parse_color(color).ok()),
696 terminal_ansi_yellow: this
697 .terminal_ansi_yellow
698 .as_ref()
699 .and_then(|color| try_parse_color(color).ok()),
700 terminal_ansi_bright_yellow: this
701 .terminal_ansi_bright_yellow
702 .as_ref()
703 .and_then(|color| try_parse_color(color).ok()),
704 terminal_ansi_dim_yellow: this
705 .terminal_ansi_dim_yellow
706 .as_ref()
707 .and_then(|color| try_parse_color(color).ok()),
708 terminal_ansi_blue: this
709 .terminal_ansi_blue
710 .as_ref()
711 .and_then(|color| try_parse_color(color).ok()),
712 terminal_ansi_bright_blue: this
713 .terminal_ansi_bright_blue
714 .as_ref()
715 .and_then(|color| try_parse_color(color).ok()),
716 terminal_ansi_dim_blue: this
717 .terminal_ansi_dim_blue
718 .as_ref()
719 .and_then(|color| try_parse_color(color).ok()),
720 terminal_ansi_magenta: this
721 .terminal_ansi_magenta
722 .as_ref()
723 .and_then(|color| try_parse_color(color).ok()),
724 terminal_ansi_bright_magenta: this
725 .terminal_ansi_bright_magenta
726 .as_ref()
727 .and_then(|color| try_parse_color(color).ok()),
728 terminal_ansi_dim_magenta: this
729 .terminal_ansi_dim_magenta
730 .as_ref()
731 .and_then(|color| try_parse_color(color).ok()),
732 terminal_ansi_cyan: this
733 .terminal_ansi_cyan
734 .as_ref()
735 .and_then(|color| try_parse_color(color).ok()),
736 terminal_ansi_bright_cyan: this
737 .terminal_ansi_bright_cyan
738 .as_ref()
739 .and_then(|color| try_parse_color(color).ok()),
740 terminal_ansi_dim_cyan: this
741 .terminal_ansi_dim_cyan
742 .as_ref()
743 .and_then(|color| try_parse_color(color).ok()),
744 terminal_ansi_white: this
745 .terminal_ansi_white
746 .as_ref()
747 .and_then(|color| try_parse_color(color).ok()),
748 terminal_ansi_bright_white: this
749 .terminal_ansi_bright_white
750 .as_ref()
751 .and_then(|color| try_parse_color(color).ok()),
752 terminal_ansi_dim_white: this
753 .terminal_ansi_dim_white
754 .as_ref()
755 .and_then(|color| try_parse_color(color).ok()),
756 link_text_hover: this
757 .link_text_hover
758 .as_ref()
759 .and_then(|color| try_parse_color(color).ok()),
760 version_control_added,
761 version_control_deleted,
762 version_control_modified: this
763 .version_control_modified
764 .as_ref()
765 .and_then(|color| try_parse_color(color).ok())
766 .or(status_colors.modified),
767 version_control_renamed: this
768 .version_control_renamed
769 .as_ref()
770 .and_then(|color| try_parse_color(color).ok())
771 .or(status_colors.modified),
772 version_control_conflict: this
773 .version_control_conflict
774 .as_ref()
775 .and_then(|color| try_parse_color(color).ok())
776 .or(status_colors.ignored),
777 version_control_ignored: this
778 .version_control_ignored
779 .as_ref()
780 .and_then(|color| try_parse_color(color).ok())
781 .or(status_colors.ignored),
782 version_control_word_added: this
783 .version_control_word_added
784 .as_ref()
785 .and_then(|color| try_parse_color(color).ok()),
786 version_control_word_deleted: this
787 .version_control_word_deleted
788 .as_ref()
789 .and_then(|color| try_parse_color(color).ok()),
790 #[allow(deprecated)]
791 version_control_conflict_marker_ours: this
792 .version_control_conflict_marker_ours
793 .as_ref()
794 .or(this.version_control_conflict_ours_background.as_ref())
795 .and_then(|color| try_parse_color(color).ok()),
796 #[allow(deprecated)]
797 version_control_conflict_marker_theirs: this
798 .version_control_conflict_marker_theirs
799 .as_ref()
800 .or(this.version_control_conflict_theirs_background.as_ref())
801 .and_then(|color| try_parse_color(color).ok()),
802 vim_normal_background: this
803 .vim_normal_background
804 .as_ref()
805 .and_then(|color| try_parse_color(color).ok()),
806 vim_insert_background: this
807 .vim_insert_background
808 .as_ref()
809 .and_then(|color| try_parse_color(color).ok()),
810 vim_replace_background: this
811 .vim_replace_background
812 .as_ref()
813 .and_then(|color| try_parse_color(color).ok()),
814 vim_visual_background: this
815 .vim_visual_background
816 .as_ref()
817 .and_then(|color| try_parse_color(color).ok()),
818 vim_visual_line_background: this
819 .vim_visual_line_background
820 .as_ref()
821 .and_then(|color| try_parse_color(color).ok()),
822 vim_visual_block_background: this
823 .vim_visual_block_background
824 .as_ref()
825 .and_then(|color| try_parse_color(color).ok()),
826 vim_yank_background: this
827 .vim_yank_background
828 .as_ref()
829 .and_then(|color| try_parse_color(color).ok())
830 .or(editor_document_highlight_read_background),
831 vim_helix_jump_label_foreground: this
832 .vim_helix_jump_label_foreground
833 .as_ref()
834 .and_then(|color| try_parse_color(color).ok())
835 .or(status_colors.error),
836 vim_helix_normal_background: this
837 .vim_helix_normal_background
838 .as_ref()
839 .and_then(|color| try_parse_color(color).ok()),
840 vim_helix_select_background: this
841 .vim_helix_select_background
842 .as_ref()
843 .and_then(|color| try_parse_color(color).ok()),
844 vim_normal_foreground: this
845 .vim_normal_foreground
846 .as_ref()
847 .and_then(|color| try_parse_color(color).ok()),
848 vim_insert_foreground: this
849 .vim_insert_foreground
850 .as_ref()
851 .and_then(|color| try_parse_color(color).ok()),
852 vim_replace_foreground: this
853 .vim_replace_foreground
854 .as_ref()
855 .and_then(|color| try_parse_color(color).ok()),
856 vim_visual_foreground: this
857 .vim_visual_foreground
858 .as_ref()
859 .and_then(|color| try_parse_color(color).ok()),
860 vim_visual_line_foreground: this
861 .vim_visual_line_foreground
862 .as_ref()
863 .and_then(|color| try_parse_color(color).ok()),
864 vim_visual_block_foreground: this
865 .vim_visual_block_foreground
866 .as_ref()
867 .and_then(|color| try_parse_color(color).ok()),
868 vim_helix_normal_foreground: this
869 .vim_helix_normal_foreground
870 .as_ref()
871 .and_then(|color| try_parse_color(color).ok()),
872 vim_helix_select_foreground: this
873 .vim_helix_select_foreground
874 .as_ref()
875 .and_then(|color| try_parse_color(color).ok()),
876 }
877}
878
879fn ensure_non_opaque(color: Hsla) -> Hsla {
880 const MAXIMUM_OPACITY: f32 = 0.7;
881 if color.a <= MAXIMUM_OPACITY {
882 color
883 } else {
884 Hsla {
885 a: MAXIMUM_OPACITY,
886 ..color
887 }
888 }
889}
890
891fn ensure_opaque(color: Hsla) -> Hsla {
892 Hsla { a: 1.0, ..color }
893}
894
895fn try_parse_color(color: &str) -> anyhow::Result<Hsla> {
896 let rgba = gpui::Rgba::try_from(color)?;
897 let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
898 let hsla = palette::Hsla::from_color(rgba);
899
900 let hsla = gpui::hsla(
901 hsla.hue.into_positive_degrees() / 360.,
902 hsla.saturation,
903 hsla.lightness,
904 hsla.alpha,
905 );
906
907 Ok(hsla)
908}
909
910#[cfg(test)]
911mod tests {
912 use theme::StatusColorsRefinement;
913
914 use super::{
915 StatusColorsContent, ThemeColorsContent, status_colors_refinement, theme_colors_refinement,
916 try_parse_color,
917 };
918
919 #[test]
920 fn explicit_diff_hunk_colors_take_precedence_over_fallbacks() {
921 let mut colors = ThemeColorsContent::default();
922 colors.editor_diff_hunk_added_background = Some("#112233".to_string());
923 colors.editor_diff_hunk_added_hollow_background = Some("#223344".to_string());
924 colors.editor_diff_hunk_added_hollow_border = Some("#334455".to_string());
925 colors.editor_diff_hunk_deleted_background = Some("#445566".to_string());
926 colors.editor_diff_hunk_deleted_hollow_background = Some("#556677".to_string());
927 colors.editor_diff_hunk_deleted_hollow_border = Some("#667788".to_string());
928 colors.version_control_added = Some("#00ff00".to_string());
929 colors.version_control_deleted = Some("#ff0000".to_string());
930
931 let refinement = theme_colors_refinement(
932 &colors,
933 &status_colors_refinement(&StatusColorsContent::default()),
934 true,
935 );
936
937 assert_eq!(
938 refinement.editor_diff_hunk_added_background,
939 Some(parse_color("#112233"))
940 );
941 assert_eq!(
942 refinement.editor_diff_hunk_added_hollow_background,
943 Some(parse_color("#223344"))
944 );
945 assert_eq!(
946 refinement.editor_diff_hunk_added_hollow_border,
947 Some(parse_color("#334455"))
948 );
949 assert_eq!(
950 refinement.editor_diff_hunk_deleted_background,
951 Some(parse_color("#445566"))
952 );
953 assert_eq!(
954 refinement.editor_diff_hunk_deleted_hollow_background,
955 Some(parse_color("#556677"))
956 );
957 assert_eq!(
958 refinement.editor_diff_hunk_deleted_hollow_border,
959 Some(parse_color("#667788"))
960 );
961 }
962
963 #[test]
964 fn diff_hunk_colors_fallback_to_version_control_colors() {
965 let mut colors = ThemeColorsContent::default();
966 colors.version_control_added = Some("#00ff00".to_string());
967 colors.version_control_deleted = Some("#ff0000".to_string());
968
969 let refinement = theme_colors_refinement(
970 &colors,
971 &status_colors_refinement(&StatusColorsContent::default()),
972 true,
973 );
974
975 let added = parse_color("#00ff00");
976 let deleted = parse_color("#ff0000");
977
978 assert_eq!(
979 refinement.editor_diff_hunk_added_background,
980 Some(added.opacity(0.16))
981 );
982 assert_eq!(
983 refinement.editor_diff_hunk_added_hollow_background,
984 Some(added.opacity(0.08))
985 );
986 assert_eq!(
987 refinement.editor_diff_hunk_added_hollow_border,
988 Some(added.opacity(0.48))
989 );
990 assert_eq!(
991 refinement.editor_diff_hunk_deleted_background,
992 Some(deleted.opacity(0.16))
993 );
994 assert_eq!(
995 refinement.editor_diff_hunk_deleted_hollow_background,
996 Some(deleted.opacity(0.08))
997 );
998 assert_eq!(
999 refinement.editor_diff_hunk_deleted_hollow_border,
1000 Some(deleted.opacity(0.48))
1001 );
1002 }
1003
1004 #[test]
1005 fn diff_hunk_opacity_fallbacks_use_correct_values_for_light_and_dark_themes() {
1006 let mut colors = ThemeColorsContent::default();
1007 colors.version_control_added = Some("#00ff00".to_string());
1008
1009 let light_refinement = theme_colors_refinement(
1010 &colors,
1011 &status_colors_refinement(&StatusColorsContent::default()),
1012 true,
1013 );
1014 let dark_refinement = theme_colors_refinement(
1015 &colors,
1016 &status_colors_refinement(&StatusColorsContent::default()),
1017 false,
1018 );
1019
1020 let added = parse_color("#00ff00");
1021
1022 assert_eq!(
1023 light_refinement.editor_diff_hunk_added_background,
1024 Some(added.opacity(0.16))
1025 );
1026 assert_eq!(
1027 light_refinement.editor_diff_hunk_added_hollow_background,
1028 Some(added.opacity(0.08))
1029 );
1030 assert_eq!(
1031 light_refinement.editor_diff_hunk_added_hollow_border,
1032 Some(added.opacity(0.48))
1033 );
1034
1035 assert_eq!(
1036 dark_refinement.editor_diff_hunk_added_background,
1037 Some(added.opacity(0.12))
1038 );
1039 assert_eq!(
1040 dark_refinement.editor_diff_hunk_added_hollow_background,
1041 Some(added.opacity(0.06))
1042 );
1043 assert_eq!(
1044 dark_refinement.editor_diff_hunk_added_hollow_border,
1045 Some(added.opacity(0.36))
1046 );
1047 }
1048
1049 #[test]
1050 fn diff_hunk_fallbacks_are_absent_when_status_and_version_control_colors_are_missing() {
1051 let refinement = theme_colors_refinement(
1052 &ThemeColorsContent::default(),
1053 &status_colors_refinement(&StatusColorsContent::default()),
1054 true,
1055 );
1056
1057 assert_eq!(refinement.editor_diff_hunk_added_background, None);
1058 assert_eq!(refinement.editor_diff_hunk_added_hollow_background, None);
1059 assert_eq!(refinement.editor_diff_hunk_added_hollow_border, None);
1060 assert_eq!(refinement.editor_diff_hunk_deleted_background, None);
1061 assert_eq!(refinement.editor_diff_hunk_deleted_hollow_background, None);
1062 assert_eq!(refinement.editor_diff_hunk_deleted_hollow_border, None);
1063 }
1064
1065 fn parse_color(color: &str) -> gpui::Hsla {
1066 match try_parse_color(color) {
1067 Ok(color) => color,
1068 Err(error) => panic!("failed to parse color {color}: {error}"),
1069 }
1070 }
1071
1072 #[test]
1073 fn helix_jump_label_color_uses_theme_color_or_status_error() {
1074 let status_error = try_parse_color("#e63333").expect("valid color");
1075 let override_color = try_parse_color("#00ff00").expect("valid color");
1076 let status_colors = StatusColorsRefinement {
1077 error: Some(status_error),
1078 ..Default::default()
1079 };
1080
1081 let fallback_refinement = theme_colors_refinement(
1082 &ThemeColorsContent::default(),
1083 &status_colors,
1084 Default::default(),
1085 );
1086
1087 assert_eq!(
1088 fallback_refinement.vim_helix_jump_label_foreground,
1089 Some(status_error)
1090 );
1091
1092 let override_refinement = theme_colors_refinement(
1093 &ThemeColorsContent {
1094 vim_helix_jump_label_foreground: Some("#00ff00".to_string()),
1095 ..Default::default()
1096 },
1097 &status_colors,
1098 Default::default(),
1099 );
1100
1101 assert_eq!(
1102 override_refinement.vim_helix_jump_label_foreground,
1103 Some(override_color)
1104 );
1105 }
1106}