schema.rs

   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}