feat: add pierre-style presentation to diff view
This commit is contained in:
@@ -5,7 +5,7 @@ use gpui::{
|
||||
AppContext as _, Context, Entity, IntoElement, MouseButton, ParentElement as _, Render,
|
||||
Styled as _, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
use gpui_native_editor::diff_view::{DiffRowKind, DiffViewState, diff_rows};
|
||||
use gpui_native_editor::diff_view::{DiffRow, DiffRowKind, DiffViewState, diff_rows};
|
||||
use gpui_native_editor::highlight::{Language, Role};
|
||||
|
||||
const OLD: &str = "fn a() {}\nfn b() {}\nfn c() {}\n";
|
||||
@@ -26,6 +26,10 @@ fn rows_mark_removals_additions_and_context_with_line_numbers() {
|
||||
DiffRowKind::Context,
|
||||
]
|
||||
);
|
||||
assert!(
|
||||
rows.iter().all(|row| row.kind != DiffRowKind::Hunk),
|
||||
"changes within one context window need no separator"
|
||||
);
|
||||
|
||||
let removed = &rows[1];
|
||||
assert_eq!(removed.old_number, Some(2));
|
||||
@@ -54,6 +58,64 @@ fn rows_mark_removals_additions_and_context_with_line_numbers() {
|
||||
assert_eq!(context.new_number, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hunk_separators_carry_the_collapsed_line_count_and_header() {
|
||||
// Ten lines with only line 6 changed: three lines of context trim the
|
||||
// leading unchanged region to three hidden lines.
|
||||
let mut old = String::new();
|
||||
let mut new = String::new();
|
||||
for line in 0..10 {
|
||||
old.push_str(&format!("line {line}\n"));
|
||||
if line == 6 {
|
||||
new.push_str(&format!("line {line} changed\n"));
|
||||
} else {
|
||||
new.push_str(&format!("line {line}\n"));
|
||||
}
|
||||
}
|
||||
let rows = diff_rows(&old, &new, Language::Rust);
|
||||
|
||||
let separators: Vec<&DiffRow> = rows
|
||||
.iter()
|
||||
.filter(|row| row.kind == DiffRowKind::Hunk)
|
||||
.collect();
|
||||
assert_eq!(separators.len(), 1, "one collapsed region above the hunk");
|
||||
assert_eq!(separators[0].hidden_lines, 3);
|
||||
assert_eq!(separators[0].header, "@@ -4,7 +4,7 @@");
|
||||
|
||||
let changed = rows
|
||||
.iter()
|
||||
.find(|row| row.kind == DiffRowKind::Removed)
|
||||
.expect("the changed line renders as a removal");
|
||||
assert_eq!(changed.old_number, Some(7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_changes_tint_only_the_changed_words() {
|
||||
let rows = diff_rows(OLD, NEW, Language::Rust);
|
||||
|
||||
let removed = &rows[1];
|
||||
let removed_text: String = removed
|
||||
.content
|
||||
.spans
|
||||
.iter()
|
||||
.map(|span| span.text.as_ref())
|
||||
.collect();
|
||||
assert_eq!(removed.changed_ranges.len(), 1, "one word moved");
|
||||
let range = &removed.changed_ranges[0];
|
||||
assert_eq!(&removed_text[range.clone()], "b()");
|
||||
|
||||
let added = &rows[2];
|
||||
let added_text: String = added
|
||||
.content
|
||||
.spans
|
||||
.iter()
|
||||
.map(|span| span.text.as_ref())
|
||||
.collect();
|
||||
assert_eq!(added.changed_ranges.len(), 1);
|
||||
let range = &added.changed_ranges[0];
|
||||
assert_eq!(&added_text[range.clone()], "x()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_added_line_carries_its_highlight_spans() {
|
||||
let rows = diff_rows("", "fn zed() {}\n", Language::Rust);
|
||||
@@ -72,13 +134,6 @@ fn an_added_line_carries_its_highlight_spans() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identical_versions_are_all_context() {
|
||||
let rows = diff_rows(OLD, OLD, Language::Rust);
|
||||
assert_eq!(rows.len(), 3);
|
||||
assert!(rows.iter().all(|row| row.kind == DiffRowKind::Context));
|
||||
}
|
||||
|
||||
struct Host {
|
||||
diff: Entity<DiffViewState>,
|
||||
}
|
||||
@@ -90,18 +145,17 @@ impl Render for Host {
|
||||
}
|
||||
|
||||
fn diff_view_window(
|
||||
rows: usize,
|
||||
changes_at: usize,
|
||||
lines: usize,
|
||||
changed_lines: &[usize],
|
||||
) -> (TestAppContext, VisualTestContext, Entity<DiffViewState>) {
|
||||
let mut cx = TestAppContext::single();
|
||||
cx.update(gpui_native_theme::Theme::init);
|
||||
let mut old = String::new();
|
||||
let mut new = String::new();
|
||||
for line in 0..rows {
|
||||
for line in 0..lines {
|
||||
old.push_str(&format!("line {line} unchanged\n"));
|
||||
if line == changes_at {
|
||||
if changed_lines.contains(&line) {
|
||||
new.push_str(&format!("line {line} rewritten\n"));
|
||||
new.push_str(&format!("line {line} added\n"));
|
||||
} else {
|
||||
new.push_str(&format!("line {line} unchanged\n"));
|
||||
}
|
||||
@@ -124,17 +178,33 @@ fn diff_view_window(
|
||||
|
||||
#[test]
|
||||
fn rows_render_virtualized_and_the_view_jumps_to_the_first_change() {
|
||||
let (_cx, mut visual, state) = diff_view_window(200, 150);
|
||||
let changed: Vec<usize> = (10..200).step_by(10).collect();
|
||||
let (_cx, mut visual, state) = diff_view_window(200, &changed);
|
||||
|
||||
assert!(
|
||||
visual.debug_bounds("test-diff-row-0").is_some(),
|
||||
visual.debug_bounds("diff-row-0").is_some(),
|
||||
"the first row renders"
|
||||
);
|
||||
assert!(
|
||||
visual.debug_bounds("test-diff-row-199").is_none(),
|
||||
visual.debug_bounds("diff-row-100").is_none(),
|
||||
"rows past the viewport never render"
|
||||
);
|
||||
|
||||
// Scroll to the bottom of the diff first, so the jump has somewhere to
|
||||
// go.
|
||||
visual.simulate_event(gpui::ScrollEvent {
|
||||
position: point(px(500.), px(400.)),
|
||||
delta: gpui::ScrollDelta::Pixels(point(px(0.), px(-5000.))),
|
||||
..Default::default()
|
||||
});
|
||||
visual.update(|window, cx| {
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
assert!(
|
||||
visual.debug_bounds("diff-row-0").is_none(),
|
||||
"the view scrolled to the bottom"
|
||||
);
|
||||
|
||||
state.update_in(&mut visual, |state, _window, _cx| {
|
||||
state.scroll_to_first_change()
|
||||
});
|
||||
@@ -143,13 +213,23 @@ fn rows_render_virtualized_and_the_view_jumps_to_the_first_change() {
|
||||
});
|
||||
|
||||
assert!(
|
||||
visual.debug_bounds("test-diff-row-150").is_some(),
|
||||
visual.debug_bounds("diff-row-4").is_some(),
|
||||
"the first change scrolled into view"
|
||||
);
|
||||
assert!(
|
||||
visual.debug_bounds("test-diff-row-0").is_none(),
|
||||
"the context before it scrolled out"
|
||||
visual.debug_bounds("diff-row-0").is_none(),
|
||||
"the hunk header above it scrolled out"
|
||||
);
|
||||
assert!(
|
||||
visual.debug_bounds("diff-row-100").is_none(),
|
||||
"rows past the viewport still never render"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identical_versions_produce_no_rows() {
|
||||
let rows = diff_rows(OLD, OLD, Language::Rust);
|
||||
assert!(rows.is_empty(), "identical versions have no diff to show");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -163,14 +243,8 @@ fn set_versions_re_diffs() {
|
||||
});
|
||||
});
|
||||
|
||||
let all_context = cx.update(|cx| {
|
||||
state
|
||||
.read(cx)
|
||||
.rows()
|
||||
.iter()
|
||||
.all(|row| row.kind == DiffRowKind::Context)
|
||||
});
|
||||
assert!(all_context, "identical versions diff to context rows");
|
||||
let empty = cx.update(|cx| state.read(cx).rows().is_empty());
|
||||
assert!(empty, "identical versions have no diff to show");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -193,7 +267,7 @@ fn scrolling_horizontally_moves_rows_without_vertical_drift() {
|
||||
});
|
||||
|
||||
let before = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row renders");
|
||||
|
||||
visual.simulate_event(gpui::ScrollEvent {
|
||||
@@ -206,7 +280,7 @@ fn scrolling_horizontally_moves_rows_without_vertical_drift() {
|
||||
});
|
||||
|
||||
let after = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row still renders");
|
||||
assert!(
|
||||
(after.origin.x - before.origin.x + px(200.)).abs() <= px(2.),
|
||||
@@ -222,8 +296,19 @@ fn scrolling_horizontally_moves_rows_without_vertical_drift() {
|
||||
fn scrolling_vertically_does_not_drift_horizontally() {
|
||||
let mut cx = TestAppContext::single();
|
||||
cx.update(gpui_native_theme::Theme::init);
|
||||
let old = "fn same() {}\n".repeat(200);
|
||||
let new = format!("{old}fn long() {{ {} }}\n", "text ".repeat(300));
|
||||
// Spread changes keep the diff taller than the viewport, so there is
|
||||
// something to scroll vertically; the long line forces a horizontal bar.
|
||||
let mut old = String::new();
|
||||
let mut body = String::new();
|
||||
for line in 0..200 {
|
||||
old.push_str(&format!("fn same_{line}() {{}}\n"));
|
||||
if line % 10 == 0 {
|
||||
body.push_str(&format!("fn changed_{line}() {{}}\n"));
|
||||
} else {
|
||||
body.push_str(&format!("fn same_{line}() {{}}\n"));
|
||||
}
|
||||
}
|
||||
let new = format!("{body}fn long() {{ {} }}\n", "text ".repeat(300));
|
||||
let state = DiffViewState::new("test-diff", old, new, Language::Rust);
|
||||
let state_cell: Rc<RefCell<Option<Entity<DiffViewState>>>> = Rc::new(RefCell::new(None));
|
||||
let cell = state_cell.clone();
|
||||
@@ -238,7 +323,7 @@ fn scrolling_vertically_does_not_drift_horizontally() {
|
||||
});
|
||||
|
||||
let before = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row renders");
|
||||
|
||||
// A pure vertical gesture...
|
||||
@@ -259,7 +344,7 @@ fn scrolling_vertically_does_not_drift_horizontally() {
|
||||
});
|
||||
|
||||
let after = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row still renders");
|
||||
assert_eq!(
|
||||
after.origin.x, before.origin.x,
|
||||
@@ -275,8 +360,19 @@ fn scrolling_vertically_does_not_drift_horizontally() {
|
||||
fn dragging_the_vertical_thumb_leaves_the_horizontal_offset_alone() {
|
||||
let mut cx = TestAppContext::single();
|
||||
cx.update(gpui_native_theme::Theme::init);
|
||||
let old = "fn same() {}\n".repeat(200);
|
||||
let new = format!("{old}fn long() {{ {} }}\n", "text ".repeat(300));
|
||||
// Spread changes keep the diff taller than the viewport, so the vertical
|
||||
// scrollbar exists; the long line forces the horizontal one.
|
||||
let mut old = String::new();
|
||||
let mut body = String::new();
|
||||
for line in 0..200 {
|
||||
old.push_str(&format!("fn same_{line}() {{}}\n"));
|
||||
if line % 10 == 0 {
|
||||
body.push_str(&format!("fn changed_{line}() {{}}\n"));
|
||||
} else {
|
||||
body.push_str(&format!("fn same_{line}() {{}}\n"));
|
||||
}
|
||||
}
|
||||
let new = format!("{body}fn long() {{ {} }}\n", "text ".repeat(300));
|
||||
let state = DiffViewState::new("test-diff", old, new, Language::Rust);
|
||||
let state_cell: Rc<RefCell<Option<Entity<DiffViewState>>>> = Rc::new(RefCell::new(None));
|
||||
let cell = state_cell.clone();
|
||||
@@ -292,7 +388,7 @@ fn dragging_the_vertical_thumb_leaves_the_horizontal_offset_alone() {
|
||||
|
||||
// Scroll the strip left first, so horizontal drift would be observable.
|
||||
let row = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row renders");
|
||||
visual.simulate_event(gpui::ScrollEvent {
|
||||
position: row.center(),
|
||||
@@ -303,7 +399,7 @@ fn dragging_the_vertical_thumb_leaves_the_horizontal_offset_alone() {
|
||||
window.draw(cx).clear(cx);
|
||||
});
|
||||
let before = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row still renders");
|
||||
|
||||
// Press the vertical thumb: right edge, within its minimum length.
|
||||
@@ -336,7 +432,7 @@ fn dragging_the_vertical_thumb_leaves_the_horizontal_offset_alone() {
|
||||
});
|
||||
|
||||
let after = visual
|
||||
.debug_bounds("test-diff-row-0")
|
||||
.debug_bounds("diff-row-0")
|
||||
.expect("the first row still renders");
|
||||
assert_eq!(
|
||||
after.origin.x, before.origin.x,
|
||||
|
||||
Reference in New Issue
Block a user