fix: target caret excerpts and retain diff viewport offsets

This commit is contained in:
2026-10-11 10:50:54 +02:00
parent 86d22d4fa2
commit 976cfc9e62
5 changed files with 237 additions and 4 deletions
+84
View File
@@ -1462,6 +1462,22 @@ impl Editor {
}
}
pub(crate) fn set_diff_viewport(
&mut self,
comparison_id: u64,
file: usize,
top: usize,
left_after: usize,
) {
if let Some(diff) = &mut self.diff
&& diff.comparison_id == Some(comparison_id)
&& diff.file == file
{
diff.top = top;
diff.left_after = left_after;
}
}
fn handle_diff(&mut self, input: Input) -> Option<Invocation> {
if input == Input::Escape
&& (self.count.is_some() || self.g_pending || self.line_pending.is_some())
@@ -1565,6 +1581,14 @@ impl Editor {
if live && !matches!(input, Input::Char('\t' | '[' | ']') | Input::BackTab) {
return self.handle_input(input);
}
if live && matches!(input, Input::Char('[' | ']')) {
let row = self.cursor().row;
self.diff
.as_mut()
.expect("active diff")
.expand_after_excerpt(input == Input::Char(']'), row);
return None;
}
if !files_focused
&& self
.diff
@@ -7050,6 +7074,66 @@ mod tests {
buffers
}
#[test]
fn editable_diff_brackets_expand_caret_excerpt_without_moving_source_anchor() {
for selected in [30, 50] {
let directory = Directory::new();
let path = directory.path("file.txt");
let before: Vec<String> = (0..80).map(|row| format!("line {row}")).collect();
fs::write(&path, before.join("\n")).unwrap();
let mut editor = Editor::open(Some(path), vec![]).unwrap();
let mut after = before;
for row in [10, 30, 50] {
after[row] = format!("changed {row}");
}
editor
.apply(vec![Effect::Replace(after.join("\n"))])
.unwrap();
editor.set_diff_context_options(1, 4);
working_diff(&mut editor, &directory.0, &["file.txt"]);
editor.handle(Input::BackTab);
assert!(editor.diff_editing());
editor.active.targets = vec![Target::caret(Cursor {
row: selected,
column: 2,
})];
let top = editor.diff().unwrap().after_display_row(9).unwrap();
editor.diff.as_mut().unwrap().top = top;
let cursor = editor.cursor();
let visible = |editor: &Editor| editor.diff().unwrap().after_rows().collect::<Vec<_>>();
assert_eq!(
visible(&editor),
(9..12).chain(29..32).chain(49..52).collect::<Vec<_>>()
);
editor.handle(Input::Char('['));
editor.handle(Input::Char(']'));
let expected = if selected == 30 {
(9..12).chain(25..36).chain(49..52).collect::<Vec<_>>()
} else {
(9..12).chain(29..32).chain(45..56).collect::<Vec<_>>()
};
assert_eq!(visible(&editor), expected);
assert_eq!(editor.cursor(), cursor);
assert_eq!(
editor.diff().unwrap().after_display_row(9),
Some(editor.diff().unwrap().top)
);
if selected == 30 {
// The caret keeps targeting the second excerpt after it merges
// with the third, rather than expanding the first excerpt.
for _ in 0..5 {
editor.handle(Input::Char(']'));
}
assert_eq!(visible(&editor), (9..12).chain(25..56).collect::<Vec<_>>());
assert_eq!(editor.cursor(), cursor);
assert_eq!(
editor.diff().unwrap().after_display_row(9),
Some(editor.diff().unwrap().top)
);
}
}
}
#[test]
fn working_diff_reuses_dirty_buffers_and_normal_edit_undo_save_paths() {
let directory = Directory::new();