improve: show whole-diff totals alongside file stats

This commit is contained in:
Valdemar Lorenzen
2026-10-20 12:29:06 +02:00
parent a5a246db77
commit 759947736e
15 changed files with 394 additions and 48 deletions
+65 -17
View File
@@ -1985,6 +1985,7 @@ fn syntax_diff_file(path: &str, before: &str, after: &str) -> ChangedFile {
let after: Vec<String> = after.split('\n').map(str::to_owned).collect();
ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -2608,6 +2609,7 @@ fn diff_comparison(files: Vec<ChangedFile>) -> DiffState {
fn deletion_file() -> ChangedFile {
ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -3559,6 +3561,7 @@ fn diff_excerpts_render_original_lines_and_expand_without_losing_changes() {
after[60] = "let second_change = 901;".into();
let mut diff = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -3699,6 +3702,7 @@ fn diff_row_tint_covers_blank_lines_and_wrapped_gutters() {
fn diff_added_rows_hatch_missing_before_but_tint_real_empty_after_lines() {
let mut diff = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -3757,6 +3761,7 @@ fn diff_added_rows_hatch_missing_before_but_tint_real_empty_after_lines() {
fn diff_changed_backgrounds_preserve_syntax_and_clipped_source_cells() {
let mut diff = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -3887,6 +3892,7 @@ fn diff_changed_backgrounds_preserve_syntax_and_clipped_source_cells() {
fn diff_view_changes_files_focus_and_keeps_horizontal_offsets_independent() {
let added = ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -4649,6 +4655,7 @@ fn large_generated_additions_render_first_scroll_and_final_rows_without_reparsin
let hunks = crate::diff::line_hunks(&[], false, &after, true);
let mut diff = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
path: "parser.c".into(),
old_path: None,
status: FileStatus::Added,
@@ -4699,6 +4706,7 @@ fn diff_notices_empty_comparisons_and_tiny_layouts_remain_renderable() {
assert!(screen_text(&terminal).contains("Explorer · Tree"));
let binary = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -4714,6 +4722,7 @@ fn diff_notices_empty_comparisons_and_tiny_layouts_remain_renderable() {
assert!(screen_text(&terminal).contains("Binary file"));
let blank = diff_comparison(vec![ChangedFile {
review_revisions: None,
stats: None,
loaded: true,
before_eof: false,
after_eof: false,
@@ -5664,17 +5673,13 @@ fn comparison_titles_follow_loaded_counts_and_each_tabs_context() {
})),
);
}
let view = if phase == "view" {
"Diff · +3 -2 · "
} else {
"Diff · "
};
let changes = if phase == "view" { " +3 -2" } else { "" };
let default_tabs = editor.top_bar_config().segment("tabs").clone();
for (template, ordinary, prefix, uses_view) in [
(None, "1 source.rs", "2 ", true),
(Some("{index} {name}{modified}"), "1 source.rs", "2 ", false),
(
Some("T{index}:{view}{name}{modified}"),
Some("T{index}:{view}{name}{changes}{modified}"),
"T1:source.rs",
"T2:",
true,
@@ -5688,7 +5693,8 @@ fn comparison_titles_follow_loaded_counts_and_each_tabs_context() {
bottom.left = vec!["mode".into()];
bottom.center.clear();
bottom.right.clear();
bottom.segments.get_mut("mode").unwrap().title = "Current:{view}{name}".into();
bottom.segments.get_mut("mode").unwrap().title =
"Current:{view}{name}{changes}".into();
});
for comparison_active in [true, false] {
let mut terminal =
@@ -5709,16 +5715,33 @@ fn comparison_titles_follow_loaded_counts_and_each_tabs_context() {
.unwrap();
let titles = pane_screen(&terminal, Rect::new(0, 0, 160, 1));
assert!(titles.contains(ordinary), "{phase}: {titles}");
let comparison = format!("{prefix}{}source.rs", if uses_view { view } else { "" });
let comparison = if uses_view {
format!("{prefix}Diff · source.rs{changes}")
} else {
format!("{prefix}source.rs")
};
assert!(titles.contains(&comparison), "{phase}: {titles}");
assert_eq!(
titles.matches("Diff · ").count(),
usize::from(uses_view),
"{phase}: {titles}"
);
if matches!(phase, "view" | "unloaded") && comparison_active {
let total = if phase == "view" {
"Diff +3 -2"
} else {
"Diff …"
};
assert!(titles.contains(total), "{phase}: {titles}");
} else {
assert!(
!titles.contains("Diff +") && !titles.contains("Diff …"),
"{phase}: {titles}"
);
}
let current = pane_screen(&terminal, Rect::new(0, 1, 160, 1));
let expected = if comparison_active {
format!("Current:{view}source.rs")
format!("Current:Diff · source.rs{changes}")
} else {
"Current:source.rs".into()
};
@@ -5745,6 +5768,12 @@ fn comparison_title_counts_follow_live_edits_and_undo_in_hidden_tabs() {
editor.handle(Input::Enter);
editor.handle(Input::Enter);
let (id, _) = editor.take_diff_request().unwrap();
let mut unopened = syntax_diff_file("unopened.rs", "", "");
unopened.loaded = false;
unopened.before.clear();
unopened.after.clear();
unopened.hunks.clear();
unopened.stats = Some((7, 4));
editor.apply_diff_result(
id,
Ok(crate::diff::Outcome::Comparison(crate::diff::Comparison {
@@ -5754,17 +5783,25 @@ fn comparison_title_counts_follow_live_edits_and_undo_in_hidden_tabs() {
before: "baseline".into(),
after: "Working checkout".into(),
revisions: ["before-snapshot".into(), "after-snapshot".into()],
files: vec![syntax_diff_file("live.rs", "old", "new")],
files: vec![syntax_diff_file("live.rs", "old", "new"), unopened],
})),
);
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 14)).unwrap();
let mut views = BufferViewports::default();
for input in [Input::Down, Input::Char('l'), Input::Ctrl('u'), Input::Home] {
editor.handle(input);
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
let titles = pane_screen(&terminal, Rect::new(0, 0, 120, 1));
assert!(titles.contains("Diff +8 -5"), "{titles}");
}
editor.handle(Input::Char('\t'));
editor.handle(Input::Char('\t'));
assert!(editor.diff_editing());
editor.execute("tab.new");
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(120, 14)).unwrap();
let mut views = BufferViewports::default();
for (inputs, counts) in [
(vec![], "+1 -1"),
for (inputs, counts, total) in [
(vec![], "+1 -1", "+8 -5"),
(
vec![
Input::Char('i'),
@@ -5772,9 +5809,10 @@ fn comparison_title_counts_follow_live_edits_and_undo_in_hidden_tabs() {
Input::Escape,
],
"+2 -1",
"+9 -5",
),
(vec![Input::Char('u')], "+1 -1"),
(vec![Input::Char('U')], "+2 -1"),
(vec![Input::Char('u')], "+1 -1", "+8 -5"),
(vec![Input::Char('U')], "+2 -1", "+9 -5"),
] {
for input in inputs {
editor.handle(input);
@@ -5785,10 +5823,18 @@ fn comparison_title_counts_follow_live_edits_and_undo_in_hidden_tabs() {
.unwrap();
let titles = pane_screen(&terminal, Rect::new(0, 0, 120, 1));
assert!(
titles.contains(&format!("1 Diff · {counts} · live.rs")),
titles.contains(&format!("1 Diff · live.rs {counts}")),
"{titles}"
);
assert!(titles.contains("2 live.rs"), "{titles}");
assert!(!titles.contains("Diff +"), "{titles}");
editor.execute("tab.previous");
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
let titles = pane_screen(&terminal, Rect::new(0, 0, 120, 1));
assert!(titles.contains(&format!("Diff {total}")), "{titles}");
editor.execute("tab.next");
}
}
@@ -7049,6 +7095,7 @@ fn custom_value_stays_above_split_overlays_and_diff_geometry() {
assert_eq!(pane_screen(&terminal, Rect::new(0, 0, 120, 1)), title);
let mut editor = readonly_diff_editor(deletion_file());
configure_chrome(&mut editor, |top, _| top.right = vec!["battery".into()]);
terminal
.draw(|frame| views.draw(frame, &mut editor))
.unwrap();
@@ -7234,6 +7281,7 @@ fn native_diff_dock_and_panes_fill_the_workspace_below_only_configured_chrome()
for (width, height) in [(120, 30), (32, 9)] {
for chrome in [false, true] {
let mut editor = readonly_diff_editor(deletion_file());
configure_chrome(&mut editor, |top, _| top.right = vec!["battery".into()]);
if chrome {
editor.set_label("battery".into(), "live".into()).unwrap();
}