Git · DiffViewer: rows move to their own file; shorter comments

This commit is contained in:
ZacharyZhang-NY committed 2026-10-07 00:45:24 -04:00
1 parent e69b96c9e3
commit e226d7979b
5 files changed
+110 -94

No files matched your search

+2 -2
View File
@@ -152,7 +152,7 @@ pub(crate) fn pair_places(lines: &[DiffLine]) -> Vec<(Option<usize>, Option<usiz
out
}
/// A hunk's lines side by side: unchanged lines on both sides, each run of removals beside the additions after it.
/// A hunk's lines side by side: removals beside following additions.
pub fn pairs(lines: &[DiffLine]) -> 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 {
+2
View File
@@ -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};
+96
View File
@@ -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<Shown> {
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>, 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<usize>, 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))
}
+2 -2
View File
@@ -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);
+8 -90
View File
@@ -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<Shown> {
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<dyn Fn(usize, &mut Window, &mut App)>;
type OnLayout = Rc<dyn Fn(DiffLayout, &mut Window, &mut App)>;
type OnLine = Rc<dyn Fn(Place, bool, &mut Window, &mut App)>;
@@ -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<SharedString>,
@@ -204,38 +154,6 @@ impl DiffViewer {
}
}
/// The wash behind a line, and behind its changed words.
fn washes(kind: LineKind, colors: &Palette) -> (Option<Hsla>, 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<usize>, 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();