From e226d7979bab037c8273e97280c9565f98d557a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Wed, 7 Oct 2026 00:45:23 -0400 Subject: [PATCH] =?UTF-8?q?Git=20=C2=B7=20DiffViewer:=20rows=20move=20to?= =?UTF-8?q?=20their=20own=20file;=20shorter=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/git/diff.rs | 4 +- src/git/mod.rs | 2 + src/git/rows.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++ src/git/tests.rs | 4 +- src/git/viewer.rs | 98 ++++------------------------------------------- 5 files changed, 110 insertions(+), 94 deletions(-) create mode 100644 src/git/rows.rs diff --git a/src/git/diff.rs b/src/git/diff.rs index 7c2c73f..9c2dae5 100644 --- a/src/git/diff.rs +++ b/src/git/diff.rs @@ -152,7 +152,7 @@ pub(crate) fn pair_places(lines: &[DiffLine]) -> Vec<(Option, Option Vec<(Option<&DiffLine>, Option<&DiffLine>)> { pair_places(lines) .into_iter() @@ -160,7 +160,7 @@ pub fn pairs(lines: &[DiffLine]) -> Vec<(Option<&DiffLine>, Option<&DiffLine>)> .collect() } -/// Marks the words each removal and the addition beside it changed. +/// Marks words changed between each removal and its addition. pub fn mark_words(lines: &mut [DiffLine]) { for (left, right) in pair_places(lines) { let (Some(left), Some(right)) = (left, right) else { diff --git a/src/git/mod.rs b/src/git/mod.rs index ab0b5d3..511b0ee 100644 --- a/src/git/mod.rs +++ b/src/git/mod.rs @@ -9,6 +9,7 @@ mod merge; mod merging; mod refs; mod review; +mod rows; mod tags; #[cfg(all(test, feature = "test-support"))] mod tests; @@ -25,5 +26,6 @@ pub use merge::{ConflictResolver, ThreeWayMerge}; pub use merging::{Conflict, Region, RegionKind, Take, conflicts, regions, resolve, result}; pub use refs::{Branch, BranchList, BranchSelector}; pub use review::{PullRequest, PullRequestCard, PullState, ReviewComment, ReviewNote}; +pub use rows::Place; pub use tags::{GitTag, Stash, StashAction, StashList, TagList}; pub use viewer::{DiffLayout, DiffViewer}; diff --git a/src/git/rows.rs b/src/git/rows.rs new file mode 100644 index 0000000..0266c3b --- /dev/null +++ b/src/git/rows.rs @@ -0,0 +1,96 @@ +//! The rows a diff viewer draws, and their colors. + +use std::ops::Range; + +use gpui::{App, HighlightStyle, Hsla, SharedString, StyledText}; + +use super::{ + diff::{DiffLine, LineKind, Stretch, pair_places}, + viewer::DiffLayout, +}; +use crate::{ + editor::{code_colors, stack}, + theme::Palette, +}; + +/// A line's place: its stretch, then its line there. +pub type Place = (usize, usize); + +/// A row as the viewer draws it. +#[derive(Clone)] +pub(super) enum Shown { + /// A hunk's header and its place among the stretches. + Header(usize, SharedString), + Line(Place, DiffLine), + Pair(Option<(Place, DiffLine)>, Option<(Place, DiffLine)>), + /// A folded stretch: its place among the stretches and how many lines it hides. + Fold(usize, usize), +} + +pub(super) fn shown(stretches: &[Stretch], layout: DiffLayout, open: &[usize]) -> Vec { + let mut rows = Vec::new(); + for (ix, stretch) in stretches.iter().enumerate() { + match stretch { + Stretch::Hunk { header, lines } => { + rows.push(Shown::Header(ix, header.clone())); + let at = |line: usize| ((ix, line), lines[line].clone()); + match layout { + DiffLayout::Unified => rows.extend((0..lines.len()).map(|line| { + let (place, line) = at(line); + Shown::Line(place, line) + })), + DiffLayout::Split => rows.extend( + pair_places(lines) + .into_iter() + .map(|(left, right)| Shown::Pair(left.map(at), right.map(at))), + ), + } + } + Stretch::Folded(lines) if open.contains(&ix) => { + rows.extend(lines.iter().enumerate().map(|(at, line)| { + let place = (ix, at); + match layout { + DiffLayout::Unified => Shown::Line(place, line.clone()), + DiffLayout::Split => { + Shown::Pair(Some((place, line.clone())), Some((place, line.clone()))) + } + } + })) + } + Stretch::Folded(lines) => rows.push(Shown::Fold(ix, lines.len())), + } + } + rows +} + +/// The wash behind a line, and behind its changed words. +pub(super) fn washes(kind: LineKind, colors: &Palette) -> (Option, Hsla) { + match kind { + LineKind::Same => (None, colors.hover), + LineKind::Added => ( + Some(colors.success.opacity(0.08)), + colors.success.opacity(0.25), + ), + LineKind::Removed => ( + Some(colors.danger.opacity(0.08)), + colors.danger.opacity(0.25), + ), + } +} + +/// A line's code, colored as code, its changed words washed. +pub(super) fn code(line: &DiffLine, colors: &Palette, cx: &App) -> StyledText { + let (_, word) = washes(line.kind, colors); + let mut styles: Vec<(Range, HighlightStyle)> = code_colors(&line.text, cx); + styles.extend(line.words.iter().map(|range| { + ( + range.clone(), + HighlightStyle { + background_color: Some(word), + ..HighlightStyle::default() + }, + ) + })); + styles.sort_by_key(|(range, _)| range.start); + StyledText::new(line.text.clone()).with_highlights(stack(styles)) +} diff --git a/src/git/tests.rs b/src/git/tests.rs index 59b5e49..b5466b6 100644 --- a/src/git/tests.rs +++ b/src/git/tests.rs @@ -152,7 +152,7 @@ fn changed_lines_and_hunks_answer_presses(cx: &mut TestAppContext) { }); settle(cx); }; - // Past the two layout segments: the hunk's button, then its lines. + // Past two layout segments: hunk button, then lines. let next = |cx: &mut VisualTestContext| { cx.update(|window, cx| window.focus_next(cx)); settle(cx); @@ -213,7 +213,7 @@ fn a_row_says_which_section_it_is_in(cx: &mut TestAppContext) { cx.update(|window, _| window.activate_window()); settle(cx); let mut rows = Vec::new(); - // Each row is a stop; its icons stay hidden until hover. + // Each row is a stop; icons hide until hover. for _ in 0..12 { cx.update(|window, cx| window.focus_next(cx)); settle(cx); diff --git a/src/git/viewer.rs b/src/git/viewer.rs index b2358eb..3bea678 100644 --- a/src/git/viewer.rs +++ b/src/git/viewer.rs @@ -1,21 +1,21 @@ -use std::{ops::Range, rc::Rc}; +use std::rc::Rc; use gpui::{ - AnyElement, App, ElementId, FontWeight, HighlightStyle, Hsla, InteractiveElement, IntoElement, - MouseButton, ParentElement, RenderOnce, Role, SharedString, StatefulInteractiveElement, Styled, - StyledText, Window, div, prelude::*, relative, transparent_black, uniform_list, + AnyElement, App, ElementId, FontWeight, InteractiveElement, IntoElement, MouseButton, + ParentElement, RenderOnce, Role, SharedString, StatefulInteractiveElement, Styled, Window, div, + prelude::*, relative, transparent_black, uniform_list, }; use super::{ badges::DiffStat, - diff::{DiffLine, LineKind, Stretch, diff, pair_places, side_numbers}, + diff::{DiffLine, LineKind, Stretch, diff, side_numbers}, + rows::{Place, Shown, code, shown, washes}, }; use crate::{ buttons::{Button, ButtonVariant, SegmentedControl}, - editor::{code_colors, stack}, i18n, primitives::FocusRing, - theme::{ActiveTheme, ControlSize, Palette, TextSize}, + theme::{ActiveTheme, ControlSize, TextSize}, typography::LEADING, }; @@ -29,56 +29,6 @@ pub enum DiffLayout { /// Unchanged lines kept around each change. const CONTEXT: usize = 3; -/// A line's place: its stretch, then its line there. -pub type Place = (usize, usize); - -/// A row as the viewer draws it. -#[derive(Clone)] -enum Shown { - /// A hunk's header and its place among the stretches. - Header(usize, SharedString), - Line(Place, DiffLine), - Pair(Option<(Place, DiffLine)>, Option<(Place, DiffLine)>), - /// A folded stretch: its place among the stretches and how many lines it hides. - Fold(usize, usize), -} - -fn shown(stretches: &[Stretch], layout: DiffLayout, open: &[usize]) -> Vec { - let mut rows = Vec::new(); - for (ix, stretch) in stretches.iter().enumerate() { - match stretch { - Stretch::Hunk { header, lines } => { - rows.push(Shown::Header(ix, header.clone())); - let at = |line: usize| ((ix, line), lines[line].clone()); - match layout { - DiffLayout::Unified => rows.extend((0..lines.len()).map(|line| { - let (place, line) = at(line); - Shown::Line(place, line) - })), - DiffLayout::Split => rows.extend( - pair_places(lines) - .into_iter() - .map(|(left, right)| Shown::Pair(left.map(at), right.map(at))), - ), - } - } - Stretch::Folded(lines) if open.contains(&ix) => { - rows.extend(lines.iter().enumerate().map(|(at, line)| { - let place = (ix, at); - match layout { - DiffLayout::Unified => Shown::Line(place, line.clone()), - DiffLayout::Split => { - Shown::Pair(Some((place, line.clone())), Some((place, line.clone()))) - } - } - })) - } - Stretch::Folded(lines) => rows.push(Shown::Fold(ix, lines.len())), - } - } - rows -} - type OnIndex = Rc; type OnLayout = Rc; type OnLine = Rc; @@ -163,7 +113,7 @@ impl DiffViewer { self } - /// A button on each hunk's header, given the hunk's place. + /// A button on each hunk header, given its place. pub fn hunk_action( mut self, label: impl Into, @@ -204,38 +154,6 @@ impl DiffViewer { } } -/// The wash behind a line, and behind its changed words. -fn washes(kind: LineKind, colors: &Palette) -> (Option, Hsla) { - match kind { - LineKind::Same => (None, colors.hover), - LineKind::Added => ( - Some(colors.success.opacity(0.08)), - colors.success.opacity(0.25), - ), - LineKind::Removed => ( - Some(colors.danger.opacity(0.08)), - colors.danger.opacity(0.25), - ), - } -} - -/// A line's code, colored as code, its changed words washed. -fn code(line: &DiffLine, colors: &Palette, cx: &App) -> StyledText { - let (_, word) = washes(line.kind, colors); - let mut styles: Vec<(Range, HighlightStyle)> = code_colors(&line.text, cx); - styles.extend(line.words.iter().map(|range| { - ( - range.clone(), - HighlightStyle { - background_color: Some(word), - ..HighlightStyle::default() - }, - ) - })); - styles.sort_by_key(|(range, _)| range.start); - StyledText::new(line.text.clone()).with_highlights(stack(styles)) -} - impl RenderOnce for DiffViewer { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { let theme = cx.theme();