fix: skip omitted diff lines instead of expanding excerpts during navigation

This commit is contained in:
2026-10-11 03:02:47 +02:00
parent 54bb196192
commit 86d22d4fa2
5 changed files with 302 additions and 79 deletions
+138
View File
@@ -182,3 +182,141 @@ fn typed_diff_edits_unsaved_working_after_and_preserves_undo_on_explicit_close()
assert_eq!(fixture.git(&["rev-parse", "HEAD"]), base);
assert_eq!(fixture.git(&["status", "--porcelain"]), "M buffer.rs");
}
#[test]
fn editable_diff_navigation_skips_omitted_lines_without_expanding_excerpts() {
use vamp::diff::DisplayRow;
let fixture = Fixture::new();
fixture.git(&["init", "-q"]);
let source = fixture.0.join("repo/context.txt");
let mut lines = (1..=100)
.map(|line| format!("line_{line:03} words"))
.collect::<Vec<_>>();
lines[49].clear(); // Hidden short lines must not clamp the visible caret column.
fs::write(&source, format!("{}\n", lines.join("\n"))).unwrap();
fixture.git(&["add", "context.txt"]);
fixture.git(&["commit", "-qm", "base"]);
let base = fixture.git(&["rev-parse", "HEAD"]);
lines[19] = "changed_020 words".into();
lines[79] = "changed_080 words".into();
let text = format!("{}\n", lines.join("\n"));
fs::write(&source, &text).unwrap();
let config = fixture.0.join("config.rb");
fs::write(&config, "Vamp.configure do |config|\n config.clipboard_mode = 'separate'\n config.diff_context_lines = 1\n config.diff_expand_lines = 2\n config.bind 'ctrl+f', config.core.down\n config.bind 'ctrl+b', config.core.up\nend\n").unwrap();
let mut session =
EditorSession::open(Some(source.clone()), vec!["text".into()], Some(&config)).unwrap();
session.execute("diff");
wait_until(&mut session, |session| {
session
.editor()
.diff()
.is_some_and(|diff| diff.catalog.is_some() && !diff.loading)
});
type_query(&mut session, &base);
session.handle(Input::Down);
session.handle(Input::Enter);
session.handle(Input::Enter);
wait_until(&mut session, |session| {
session
.editor()
.diff()
.is_some_and(|diff| diff.phase == Phase::View)
});
session.handle(Input::BackTab);
let shown = |session: &EditorSession| {
let diff = session.editor().diff().unwrap();
diff.display_rows
.iter()
.filter_map(|row| match *row {
DisplayRow::Line(index) => diff.rows[index].after,
DisplayRow::Fold { .. } => None,
})
.collect::<Vec<_>>()
};
let excerpts = vec![18, 19, 20, 78, 79, 80];
assert_eq!(shown(&session), excerpts);
assert_eq!(session.editor().cursor().row, 18);
type_query(&mut session, "5l");
for (keys, row) in [("2j", 20), ("j", 78), ("k", 20), ("3j", 80), ("5k", 18)] {
type_query(&mut session, keys);
assert_eq!(session.editor().cursor().row, row, "{keys}");
assert_eq!(session.editor().cursor().column, 5, "{keys}");
assert_eq!(shown(&session), excerpts, "{keys}");
}
for (keys, row) in [
("gb", 80),
("gt", 18),
("2j$w", 78),
("b", 20),
("$2w", 78),
("0B", 20),
] {
type_query(&mut session, keys);
assert_eq!(session.editor().cursor().row, row, "{keys}");
assert_eq!(shown(&session), excerpts, "{keys}");
}
session.handle(Input::Ctrl('f'));
assert_eq!(session.editor().cursor().row, 78);
session.handle(Input::Ctrl('b'));
assert_eq!(session.editor().cursor().row, 20);
type_query(&mut session, "Vj");
assert_eq!(session.editor().cursor().row, 78);
session.handle(Input::Escape);
session.handle(Input::Ctrl('k'));
assert_eq!(
session
.editor()
.carets()
.iter()
.map(|caret| caret.row)
.collect::<Vec<_>>(),
[20, 78]
);
type_query(&mut session, "j");
assert_eq!(
session
.editor()
.carets()
.iter()
.map(|caret| caret.row)
.collect::<Vec<_>>(),
[78, 79]
);
session.handle(Input::Escape);
type_query(&mut session, "gt2j$v");
session.handle(Input::Ctrl('a'));
assert_eq!(
session
.editor()
.carets()
.iter()
.map(|caret| caret.row)
.collect::<Vec<_>>(),
excerpts
);
assert_eq!(shown(&session), excerpts);
session.handle(Input::Escape);
session.handle(Input::Escape);
type_query(&mut session, "/line_040");
session.handle(Input::Enter);
assert_eq!(session.editor().cursor().row, 20);
type_query(&mut session, "/line_079");
session.handle(Input::Enter);
assert_eq!(session.editor().cursor().row, 78);
assert_eq!(shown(&session), excerpts);
type_query(&mut session, "gt2ji");
session.handle(Input::Down);
assert_eq!(session.editor().cursor().row, 78);
session.handle(Input::Up);
assert_eq!(session.editor().cursor().row, 20);
session.handle(Input::Escape);
assert_eq!(shown(&session), excerpts);
assert_eq!(session.editor().context().text, text);
assert!(!session.editor().dirty());
assert_eq!(fs::read_to_string(&source).unwrap(), text);
type_query(&mut session, "[");
assert_eq!(shown(&session), [16, 17, 18, 19, 20, 78, 79, 80]);
type_query(&mut session, "]");
assert_eq!(shown(&session), [16, 17, 18, 19, 20, 21, 22, 78, 79, 80]);
}