Detailed changes
@@ -10083,8 +10083,6 @@ impl Element for EditorElement {
.editor
.update(cx, |editor, cx| editor.highlighted_display_rows(window, cx));
- let is_light = cx.theme().appearance().is_light();
-
let mut highlighted_ranges = self
.editor_with_selections(cx)
.map(|editor| {
@@ -10124,42 +10122,49 @@ impl Element for EditorElement {
})
.unwrap_or_default();
+ struct DiffHunkHighlightColors {
+ filled_background: Hsla,
+ hollow_background: Hsla,
+ hollow_border: Hsla,
+ }
+
+ let colors = cx.theme().colors();
+ let added_diff_hunk_colors = DiffHunkHighlightColors {
+ filled_background: colors.editor_diff_hunk_added_background,
+ hollow_background: colors.editor_diff_hunk_added_hollow_background,
+ hollow_border: colors.editor_diff_hunk_added_hollow_border,
+ };
+ let deleted_diff_hunk_colors = DiffHunkHighlightColors {
+ filled_background: colors.editor_diff_hunk_deleted_background,
+ hollow_background: colors.editor_diff_hunk_deleted_hollow_background,
+ hollow_border: colors.editor_diff_hunk_deleted_hollow_border,
+ };
+ let drag_highlight_color = colors.editor_active_line_background;
+ let drag_border_color = colors.border_focused;
+
for (ix, row_info) in row_infos.iter().enumerate() {
let Some(diff_status) = row_info.diff_status else {
continue;
};
- let background_color = match diff_status.kind {
- DiffHunkStatusKind::Added => cx.theme().colors().version_control_added,
- DiffHunkStatusKind::Deleted => {
- cx.theme().colors().version_control_deleted
- }
+ let diff_hunk_colors = match diff_status.kind {
+ DiffHunkStatusKind::Added => &added_diff_hunk_colors,
+ DiffHunkStatusKind::Deleted => &deleted_diff_hunk_colors,
DiffHunkStatusKind::Modified => {
debug_panic!("modified diff status for row info");
continue;
}
};
- let hunk_opacity = if is_light { 0.16 } else { 0.12 };
-
let hollow_highlight = LineHighlight {
- background: (background_color.opacity(if is_light {
- 0.08
- } else {
- 0.06
- }))
- .into(),
- border: Some(if is_light {
- background_color.opacity(0.48)
- } else {
- background_color.opacity(0.36)
- }),
+ background: diff_hunk_colors.hollow_background.into(),
+ border: Some(diff_hunk_colors.hollow_border),
include_gutter: true,
type_id: None,
};
let filled_highlight = LineHighlight {
- background: solid_background(background_color.opacity(hunk_opacity)),
+ background: solid_background(diff_hunk_colors.filled_background),
border: None,
include_gutter: true,
type_id: None,
@@ -10184,11 +10189,9 @@ impl Element for EditorElement {
let range = drag_state.row_range(&snapshot.display_snapshot);
let start_row = range.start().0;
let end_row = range.end().0;
- let drag_highlight_color =
- cx.theme().colors().editor_active_line_background;
let drag_highlight = LineHighlight {
background: solid_background(drag_highlight_color),
- border: Some(cx.theme().colors().border_focused),
+ border: Some(drag_border_color),
include_gutter: true,
type_id: None,
};
@@ -854,6 +854,30 @@ pub struct ThemeColorsContent {
#[serde(rename = "editor.document_highlight.bracket_background")]
pub editor_document_highlight_bracket_background: Option<String>,
+ /// Filled background color for added diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.added.background")]
+ pub editor_diff_hunk_added_background: Option<String>,
+
+ /// Hollow background color for added diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.added.hollow_background")]
+ pub editor_diff_hunk_added_hollow_background: Option<String>,
+
+ /// Hollow border color for added diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.added.hollow_border")]
+ pub editor_diff_hunk_added_hollow_border: Option<String>,
+
+ /// Filled background color for deleted diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.deleted.background")]
+ pub editor_diff_hunk_deleted_background: Option<String>,
+
+ /// Hollow background color for deleted diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.deleted.hollow_background")]
+ pub editor_diff_hunk_deleted_hollow_background: Option<String>,
+
+ /// Hollow border color for deleted diff hunk row highlights in the editor.
+ #[serde(rename = "editor.diff_hunk.deleted.hollow_border")]
+ pub editor_diff_hunk_deleted_hollow_border: Option<String>,
+
/// Terminal background color.
#[serde(rename = "terminal.background")]
pub terminal_background: Option<String>,
@@ -129,6 +129,12 @@ impl ThemeColors {
editor_document_highlight_read_background: neutral().light_alpha().step_3(),
editor_document_highlight_write_background: neutral().light_alpha().step_4(),
editor_document_highlight_bracket_background: green().light_alpha().step_5(),
+ editor_diff_hunk_added_background: ADDED_COLOR.opacity(0.16),
+ editor_diff_hunk_added_hollow_background: ADDED_COLOR.opacity(0.08),
+ editor_diff_hunk_added_hollow_border: ADDED_COLOR.opacity(0.48),
+ editor_diff_hunk_deleted_background: REMOVED_COLOR.opacity(0.16),
+ editor_diff_hunk_deleted_hollow_background: REMOVED_COLOR.opacity(0.08),
+ editor_diff_hunk_deleted_hollow_border: REMOVED_COLOR.opacity(0.48),
terminal_background: neutral().light().step_1(),
terminal_foreground: black().light().step_12(),
terminal_bright_foreground: black().light().step_11(),
@@ -276,6 +282,12 @@ impl ThemeColors {
editor_document_highlight_read_background: neutral().dark_alpha().step_4(),
editor_document_highlight_write_background: neutral().dark_alpha().step_4(),
editor_document_highlight_bracket_background: green().dark_alpha().step_6(),
+ editor_diff_hunk_added_background: ADDED_COLOR.opacity(0.12),
+ editor_diff_hunk_added_hollow_background: ADDED_COLOR.opacity(0.06),
+ editor_diff_hunk_added_hollow_border: ADDED_COLOR.opacity(0.36),
+ editor_diff_hunk_deleted_background: REMOVED_COLOR.opacity(0.12),
+ editor_diff_hunk_deleted_hollow_background: REMOVED_COLOR.opacity(0.06),
+ editor_diff_hunk_deleted_hollow_border: REMOVED_COLOR.opacity(0.36),
terminal_background: neutral().dark().step_1(),
terminal_ansi_background: neutral().dark().step_1(),
terminal_foreground: white().dark().step_12(),
@@ -185,6 +185,12 @@ pub(crate) fn zed_default_dark() -> Theme {
),
editor_document_highlight_write_background: gpui::red(),
editor_document_highlight_bracket_background: gpui::green(),
+ editor_diff_hunk_added_background: ADDED_COLOR.opacity(0.12),
+ editor_diff_hunk_added_hollow_background: ADDED_COLOR.opacity(0.06),
+ editor_diff_hunk_added_hollow_border: ADDED_COLOR.opacity(0.36),
+ editor_diff_hunk_deleted_background: REMOVED_COLOR.opacity(0.12),
+ editor_diff_hunk_deleted_hollow_background: REMOVED_COLOR.opacity(0.06),
+ editor_diff_hunk_deleted_hollow_border: REMOVED_COLOR.opacity(0.36),
terminal_background: bg,
// todo("Use one colors for terminal")
@@ -241,6 +241,18 @@ pub struct ThemeColors {
///
/// Matching brackets in the cursor scope are highlighted with this background color.
pub editor_document_highlight_bracket_background: Hsla,
+ /// Filled background color for added diff hunk row highlights in the editor.
+ pub editor_diff_hunk_added_background: Hsla,
+ /// Hollow background color for added diff hunk row highlights in the editor.
+ pub editor_diff_hunk_added_hollow_background: Hsla,
+ /// Hollow border color for added diff hunk row highlights in the editor.
+ pub editor_diff_hunk_added_hollow_border: Hsla,
+ /// Filled background color for deleted diff hunk row highlights in the editor.
+ pub editor_diff_hunk_deleted_background: Hsla,
+ /// Hollow background color for deleted diff hunk row highlights in the editor.
+ pub editor_diff_hunk_deleted_hollow_background: Hsla,
+ /// Hollow border color for deleted diff hunk row highlights in the editor.
+ pub editor_diff_hunk_deleted_hollow_border: Hsla,
// ===
// Terminal
@@ -13,6 +13,13 @@ pub use settings::{FontWeightContent, WindowBackgroundContent};
use theme::{StatusColorsRefinement, ThemeColorsRefinement};
+const LIGHT_DIFF_HUNK_FILLED_OPACITY: f32 = 0.16;
+const LIGHT_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY: f32 = 0.08;
+const LIGHT_DIFF_HUNK_HOLLOW_BORDER_OPACITY: f32 = 0.48;
+const DARK_DIFF_HUNK_FILLED_OPACITY: f32 = 0.12;
+const DARK_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY: f32 = 0.06;
+const DARK_DIFF_HUNK_HOLLOW_BORDER_OPACITY: f32 = 0.36;
+
/// The content of a serialized theme family.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ThemeFamilyContent {
@@ -230,6 +237,7 @@ pub fn status_colors_refinement(colors: &settings::StatusColorsContent) -> Statu
pub fn theme_colors_refinement(
this: &settings::ThemeColorsContent,
status_colors: &StatusColorsRefinement,
+ is_light: bool,
) -> ThemeColorsRefinement {
let border = this
.border
@@ -278,6 +286,29 @@ pub fn theme_colors_refinement(
.as_ref()
.and_then(|color| try_parse_color(color).ok())
.or(search_match_background);
+ let version_control_added = this
+ .version_control_added
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(status_colors.created);
+ let version_control_deleted = this
+ .version_control_deleted
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or(status_colors.deleted);
+ let (hunk_fill, hunk_hollow_bg, hunk_hollow_border) = if is_light {
+ (
+ LIGHT_DIFF_HUNK_FILLED_OPACITY,
+ LIGHT_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY,
+ LIGHT_DIFF_HUNK_HOLLOW_BORDER_OPACITY,
+ )
+ } else {
+ (
+ DARK_DIFF_HUNK_FILLED_OPACITY,
+ DARK_DIFF_HUNK_HOLLOW_BACKGROUND_OPACITY,
+ DARK_DIFF_HUNK_HOLLOW_BORDER_OPACITY,
+ )
+ };
ThemeColorsRefinement {
border,
border_variant: this
@@ -576,6 +607,36 @@ pub fn theme_colors_refinement(
.as_ref()
.and_then(|color| try_parse_color(color).ok())
.or(editor_document_highlight_read_background),
+ editor_diff_hunk_added_background: this
+ .editor_diff_hunk_added_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_added.map(|c| c.opacity(hunk_fill))),
+ editor_diff_hunk_added_hollow_background: this
+ .editor_diff_hunk_added_hollow_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_added.map(|c| c.opacity(hunk_hollow_bg))),
+ editor_diff_hunk_added_hollow_border: this
+ .editor_diff_hunk_added_hollow_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_added.map(|c| c.opacity(hunk_hollow_border))),
+ editor_diff_hunk_deleted_background: this
+ .editor_diff_hunk_deleted_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_fill))),
+ editor_diff_hunk_deleted_hollow_background: this
+ .editor_diff_hunk_deleted_hollow_background
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_hollow_bg))),
+ editor_diff_hunk_deleted_hollow_border: this
+ .editor_diff_hunk_deleted_hollow_border
+ .as_ref()
+ .and_then(|color| try_parse_color(color).ok())
+ .or_else(|| version_control_deleted.map(|c| c.opacity(hunk_hollow_border))),
terminal_background: this
.terminal_background
.as_ref()
@@ -696,16 +757,8 @@ pub fn theme_colors_refinement(
.link_text_hover
.as_ref()
.and_then(|color| try_parse_color(color).ok()),
- version_control_added: this
- .version_control_added
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(status_colors.created),
- version_control_deleted: this
- .version_control_deleted
- .as_ref()
- .and_then(|color| try_parse_color(color).ok())
- .or(status_colors.deleted),
+ version_control_added,
+ version_control_deleted,
version_control_modified: this
.version_control_modified
.as_ref()
@@ -856,7 +909,165 @@ fn try_parse_color(color: &str) -> anyhow::Result<Hsla> {
#[cfg(test)]
mod tests {
- use super::*;
+ use theme::StatusColorsRefinement;
+
+ use super::{
+ StatusColorsContent, ThemeColorsContent, status_colors_refinement, theme_colors_refinement,
+ try_parse_color,
+ };
+
+ #[test]
+ fn explicit_diff_hunk_colors_take_precedence_over_fallbacks() {
+ let mut colors = ThemeColorsContent::default();
+ colors.editor_diff_hunk_added_background = Some("#112233".to_string());
+ colors.editor_diff_hunk_added_hollow_background = Some("#223344".to_string());
+ colors.editor_diff_hunk_added_hollow_border = Some("#334455".to_string());
+ colors.editor_diff_hunk_deleted_background = Some("#445566".to_string());
+ colors.editor_diff_hunk_deleted_hollow_background = Some("#556677".to_string());
+ colors.editor_diff_hunk_deleted_hollow_border = Some("#667788".to_string());
+ colors.version_control_added = Some("#00ff00".to_string());
+ colors.version_control_deleted = Some("#ff0000".to_string());
+
+ let refinement = theme_colors_refinement(
+ &colors,
+ &status_colors_refinement(&StatusColorsContent::default()),
+ true,
+ );
+
+ assert_eq!(
+ refinement.editor_diff_hunk_added_background,
+ Some(parse_color("#112233"))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_added_hollow_background,
+ Some(parse_color("#223344"))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_added_hollow_border,
+ Some(parse_color("#334455"))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_background,
+ Some(parse_color("#445566"))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_hollow_background,
+ Some(parse_color("#556677"))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_hollow_border,
+ Some(parse_color("#667788"))
+ );
+ }
+
+ #[test]
+ fn diff_hunk_colors_fallback_to_version_control_colors() {
+ let mut colors = ThemeColorsContent::default();
+ colors.version_control_added = Some("#00ff00".to_string());
+ colors.version_control_deleted = Some("#ff0000".to_string());
+
+ let refinement = theme_colors_refinement(
+ &colors,
+ &status_colors_refinement(&StatusColorsContent::default()),
+ true,
+ );
+
+ let added = parse_color("#00ff00");
+ let deleted = parse_color("#ff0000");
+
+ assert_eq!(
+ refinement.editor_diff_hunk_added_background,
+ Some(added.opacity(0.16))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_added_hollow_background,
+ Some(added.opacity(0.08))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_added_hollow_border,
+ Some(added.opacity(0.48))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_background,
+ Some(deleted.opacity(0.16))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_hollow_background,
+ Some(deleted.opacity(0.08))
+ );
+ assert_eq!(
+ refinement.editor_diff_hunk_deleted_hollow_border,
+ Some(deleted.opacity(0.48))
+ );
+ }
+
+ #[test]
+ fn diff_hunk_opacity_fallbacks_use_correct_values_for_light_and_dark_themes() {
+ let mut colors = ThemeColorsContent::default();
+ colors.version_control_added = Some("#00ff00".to_string());
+
+ let light_refinement = theme_colors_refinement(
+ &colors,
+ &status_colors_refinement(&StatusColorsContent::default()),
+ true,
+ );
+ let dark_refinement = theme_colors_refinement(
+ &colors,
+ &status_colors_refinement(&StatusColorsContent::default()),
+ false,
+ );
+
+ let added = parse_color("#00ff00");
+
+ assert_eq!(
+ light_refinement.editor_diff_hunk_added_background,
+ Some(added.opacity(0.16))
+ );
+ assert_eq!(
+ light_refinement.editor_diff_hunk_added_hollow_background,
+ Some(added.opacity(0.08))
+ );
+ assert_eq!(
+ light_refinement.editor_diff_hunk_added_hollow_border,
+ Some(added.opacity(0.48))
+ );
+
+ assert_eq!(
+ dark_refinement.editor_diff_hunk_added_background,
+ Some(added.opacity(0.12))
+ );
+ assert_eq!(
+ dark_refinement.editor_diff_hunk_added_hollow_background,
+ Some(added.opacity(0.06))
+ );
+ assert_eq!(
+ dark_refinement.editor_diff_hunk_added_hollow_border,
+ Some(added.opacity(0.36))
+ );
+ }
+
+ #[test]
+ fn diff_hunk_fallbacks_are_absent_when_status_and_version_control_colors_are_missing() {
+ let refinement = theme_colors_refinement(
+ &ThemeColorsContent::default(),
+ &status_colors_refinement(&StatusColorsContent::default()),
+ true,
+ );
+
+ assert_eq!(refinement.editor_diff_hunk_added_background, None);
+ assert_eq!(refinement.editor_diff_hunk_added_hollow_background, None);
+ assert_eq!(refinement.editor_diff_hunk_added_hollow_border, None);
+ assert_eq!(refinement.editor_diff_hunk_deleted_background, None);
+ assert_eq!(refinement.editor_diff_hunk_deleted_hollow_background, None);
+ assert_eq!(refinement.editor_diff_hunk_deleted_hollow_border, None);
+ }
+
+ fn parse_color(color: &str) -> gpui::Hsla {
+ match try_parse_color(color) {
+ Ok(color) => color,
+ Err(error) => panic!("failed to parse color {color}: {error}"),
+ }
+ }
#[test]
fn helix_jump_label_color_uses_theme_color_or_status_error() {
@@ -867,8 +1078,11 @@ mod tests {
..Default::default()
};
- let fallback_refinement =
- theme_colors_refinement(&ThemeColorsContent::default(), &status_colors);
+ let fallback_refinement = theme_colors_refinement(
+ &ThemeColorsContent::default(),
+ &status_colors,
+ Default::default(),
+ );
assert_eq!(
fallback_refinement.vim_helix_jump_label_foreground,
@@ -881,6 +1095,7 @@ mod tests {
..Default::default()
},
&status_colors,
+ Default::default(),
);
assert_eq!(
@@ -476,10 +476,12 @@ impl ThemeSettings {
}
let status_color_refinement = status_colors_refinement(&theme_overrides.status);
- base_theme.styles.colors.refine(&theme_colors_refinement(
+ let theme_color_refinement = theme_colors_refinement(
&theme_overrides.colors,
&status_color_refinement,
- ));
+ base_theme.appearance.is_light(),
+ );
+ base_theme.styles.colors.refine(&theme_color_refinement);
base_theme.styles.status.refine(&status_color_refinement);
merge_player_colors(&mut base_theme.styles.player, &theme_overrides.players);
merge_accent_colors(&mut base_theme.styles.accents, &theme_overrides.accents);
@@ -296,8 +296,11 @@ pub fn refine_theme(theme: &ThemeContent) -> Theme {
AppearanceContent::Light => ThemeColors::light(),
AppearanceContent::Dark => ThemeColors::dark(),
};
- let mut theme_colors_refinement =
- theme_colors_refinement(&theme.style.colors, &status_colors_refinement);
+ let mut theme_colors_refinement = theme_colors_refinement(
+ &theme.style.colors,
+ &status_colors_refinement,
+ theme.appearance == AppearanceContent::Light,
+ );
theme::apply_theme_color_defaults(&mut theme_colors_refinement, &refined_player_colors);
refined_theme_colors.refine(&theme_colors_refinement);