diff --git a/assets/settings/default.json b/assets/settings/default.json index 0e55da877d..1c5a75a4df 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -845,7 +845,7 @@ // "hunk_style": "transparent" // 2. Show unstaged hunks with a pattern background: // "hunk_style": "pattern" - "hunk_style": "transparent" + "hunk_style": "staged_border" }, // Configuration for how direnv configuration should be loaded. May take 2 values: // 1. Load direnv configuration using `direnv export json` directly. diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 49176f7823..f2ede14142 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -14882,14 +14882,14 @@ impl Editor { &self, window: &mut Window, cx: &mut App, - ) -> BTreeMap { + ) -> BTreeMap { let snapshot = self.snapshot(window, cx); let mut used_highlight_orders = HashMap::default(); self.highlighted_rows .iter() .flat_map(|(_, highlighted_rows)| highlighted_rows.iter()) .fold( - BTreeMap::::new(), + BTreeMap::::new(), |mut unique_rows, highlight| { let start = highlight.range.start.to_display_point(&snapshot); let end = highlight.range.end.to_display_point(&snapshot); @@ -18426,3 +18426,27 @@ impl Render for MissingEditPredictionKeybindingTooltip { }) } } + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct LineHighlight { + pub background: Background, + pub border: Option, +} + +impl From for LineHighlight { + fn from(hsla: Hsla) -> Self { + Self { + background: hsla.into(), + border: None, + } + } +} + +impl From for LineHighlight { + fn from(background: Background) -> Self { + Self { + background, + border: None, + } + } +} diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 473a98ed50..c4e7b08fa8 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -20,10 +20,10 @@ use crate::{ DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GoToHunk, GoToPreviousHunk, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput, HoveredCursor, - InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineUp, OpenExcerpts, PageDown, - PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SoftWrap, - StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR, - FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, + InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight, LineUp, + OpenExcerpts, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, + Selection, SoftWrap, StickyHeaderExcerpt, ToPoint, ToggleFold, COLUMNAR_SELECTION_MODIFIERS, + CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, }; use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind}; @@ -4132,46 +4132,74 @@ impl EditorElement { } } - let mut paint_highlight = - |highlight_row_start: DisplayRow, highlight_row_end: DisplayRow, color| { - let origin = point( - layout.hitbox.origin.x, - layout.hitbox.origin.y - + (highlight_row_start.as_f32() - scroll_top) - * layout.position_map.line_height, - ); - let size = size( - layout.hitbox.size.width, - layout.position_map.line_height - * highlight_row_end.next_row().minus(highlight_row_start) as f32, - ); - window.paint_quad(fill(Bounds { origin, size }, color)); - }; + let mut paint_highlight = |highlight_row_start: DisplayRow, + highlight_row_end: DisplayRow, + highlight: crate::LineHighlight, + edges| { + let origin = point( + layout.hitbox.origin.x, + layout.hitbox.origin.y + + (highlight_row_start.as_f32() - scroll_top) + * layout.position_map.line_height, + ); + let size = size( + layout.hitbox.size.width, + layout.position_map.line_height + * highlight_row_end.next_row().minus(highlight_row_start) as f32, + ); + let mut quad = fill(Bounds { origin, size }, highlight.background); + if let Some(border_color) = highlight.border { + quad.border_color = border_color; + quad.border_widths = edges + } + window.paint_quad(quad); + }; - let mut current_paint: Option<(gpui::Background, Range)> = None; + let mut current_paint: Option<(LineHighlight, Range, Edges)> = + None; for (&new_row, &new_background) in &layout.highlighted_rows { match &mut current_paint { - Some((current_background, current_range)) => { + Some((current_background, current_range, mut edges)) => { let current_background = *current_background; let new_range_started = current_background != new_background || current_range.end.next_row() != new_row; if new_range_started { + if current_range.end.next_row() == new_row { + edges.bottom = px(0.); + }; paint_highlight( current_range.start, current_range.end, current_background, + edges, ); - current_paint = Some((new_background, new_row..new_row)); + let edges = Edges { + top: if current_range.end.next_row() != new_row { + px(1.) + } else { + px(0.) + }, + bottom: px(1.), + ..Default::default() + }; + current_paint = Some((new_background, new_row..new_row, edges)); continue; } else { current_range.end = current_range.end.next_row(); } } - None => current_paint = Some((new_background, new_row..new_row)), + None => { + let edges = Edges { + top: px(1.), + bottom: px(1.), + ..Default::default() + }; + current_paint = Some((new_background, new_row..new_row, edges)) + } }; } - if let Some((color, range)) = current_paint { - paint_highlight(range.start, range.end, color); + if let Some((color, range, edges)) = current_paint { + paint_highlight(range.start, range.end, color, edges); } let scroll_left = @@ -4431,6 +4459,9 @@ impl EditorElement { background_color.opacity(if is_light { 0.2 } else { 0.32 }); } } + GitHunkStyleSetting::StagedBorder | GitHunkStyleSetting::Border => { + // Don't change the background color + } } // Flatten the background color with the editor color to prevent @@ -6775,12 +6806,15 @@ impl Element for EditorElement { let hunk_opacity = if is_light { 0.16 } else { 0.12 }; let slash_width = line_height.0 / 1.5; // ~16 by default - let staged_background = match hunk_style { - GitHunkStyleSetting::Transparent | GitHunkStyleSetting::Pattern => { - solid_background(background_color.opacity(hunk_opacity)) + let staged_highlight: LineHighlight = match hunk_style { + GitHunkStyleSetting::Transparent + | GitHunkStyleSetting::Pattern + | GitHunkStyleSetting::Border => { + solid_background(background_color.opacity(hunk_opacity)).into() } GitHunkStyleSetting::StagedPattern => { pattern_slash(background_color.opacity(hunk_opacity), slash_width) + .into() } GitHunkStyleSetting::StagedTransparent => { solid_background(background_color.opacity(if is_light { @@ -6788,30 +6822,56 @@ impl Element for EditorElement { } else { 0.04 })) + .into() } + GitHunkStyleSetting::StagedBorder => 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) + }), + }, }; - let unstaged_background = match hunk_style { + let unstaged_highlight = match hunk_style { GitHunkStyleSetting::Transparent => { solid_background(background_color.opacity(if is_light { 0.08 } else { 0.04 })) + .into() } GitHunkStyleSetting::Pattern => { pattern_slash(background_color.opacity(hunk_opacity), slash_width) + .into() } + GitHunkStyleSetting::Border => LineHighlight { + background: (background_color.opacity(if is_light { + 0.08 + } else { + 0.02 + })) + .into(), + border: Some(background_color.opacity(0.5)), + }, GitHunkStyleSetting::StagedPattern - | GitHunkStyleSetting::StagedTransparent => { - solid_background(background_color.opacity(hunk_opacity)) + | GitHunkStyleSetting::StagedTransparent + | GitHunkStyleSetting::StagedBorder => { + solid_background(background_color.opacity(hunk_opacity)).into() } }; let background = if unstaged { - unstaged_background + unstaged_highlight } else { - staged_background + staged_highlight }; highlighted_rows @@ -7660,7 +7720,7 @@ pub struct EditorLayout { indent_guides: Option>, visible_display_row_range: Range, active_rows: BTreeMap, - highlighted_rows: BTreeMap, + highlighted_rows: BTreeMap, line_elements: SmallVec<[AnyElement; 1]>, line_numbers: Arc>, display_hunks: Vec<(DisplayDiffHunk, Option)>, diff --git a/crates/gpui/src/color.rs b/crates/gpui/src/color.rs index 25e92b0dbc..ffc729a896 100644 --- a/crates/gpui/src/color.rs +++ b/crates/gpui/src/color.rs @@ -634,7 +634,7 @@ impl Display for ColorSpace { } /// A background color, which can be either a solid color or a linear gradient. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] #[repr(C)] pub struct Background { pub(crate) tag: BackgroundTag, @@ -646,6 +646,28 @@ pub struct Background { pad: u32, } +impl std::fmt::Debug for Background { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.tag { + BackgroundTag::Solid => write!(f, "Solid({:?})", self.solid), + BackgroundTag::LinearGradient => { + write!( + f, + "LinearGradient({}, {:?}, {:?})", + self.gradient_angle_or_pattern_height, self.colors[0], self.colors[1] + ) + } + BackgroundTag::PatternSlash => { + write!( + f, + "PatternSlash({:?}, {})", + self.solid, self.gradient_angle_or_pattern_height + ) + } + } + } +} + impl Eq for Background {} impl Default for Background { fn default() -> Self { diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index e825742d4f..2396a987f1 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -212,10 +212,15 @@ pub enum GitHunkStyleSetting { Transparent, /// Show unstaged hunks with a pattern background Pattern, + /// Show unstaged hunks with a border background + Border, + /// Show staged hunks with a pattern background StagedPattern, /// Show staged hunks with a pattern background StagedTransparent, + /// Show staged hunks with a pattern background + StagedBorder, } #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]