Fix panic involving growing and shrinking selections while buffer size changes (#48898)
Repro steps: 1. Make a Rust file and check it in to git 2. Delete a lot of the file 3. Expand all the diffs (cmd+") 4. Select larger syntax node (ctrl+cmd+right) 5. Collapse all the diffs 6. Select smaller syntax node (ctrl+cmd+left) 7. Panic! The problem was we were using byte offsets instead of anchors so after we shrank the buffer by collapsing diffs we'd be pointing past the end of it. Release notes: - N/A
This commit is contained in:
1 parent
b2af9984c5
commit
b945adf5fa
2 files changed
+67
-2
No files matched your search
@@ -1790,7 +1790,7 @@ impl ClipboardSelection {
|
||||
|
||||
// selections, scroll behavior, was newest selection reversed
|
||||
type SelectSyntaxNodeHistoryState = (
|
||||
Box<[Selection<MultiBufferOffset>]>,
|
||||
Box<[Selection<Anchor>]>,
|
||||
SelectSyntaxNodeScrollBehavior,
|
||||
bool,
|
||||
);
|
||||
@@ -16590,6 +16590,10 @@ impl Editor {
|
||||
SelectSyntaxNodeScrollBehavior::CursorBottom
|
||||
};
|
||||
|
||||
let old_selections: Box<[Selection<Anchor>]> = old_selections
|
||||
.iter()
|
||||
.map(|s| s.map(|offset| buffer.anchor_before(offset)))
|
||||
.collect();
|
||||
self.select_syntax_node_history.push((
|
||||
old_selections,
|
||||
scroll_behavior,
|
||||
@@ -16612,9 +16616,15 @@ impl Editor {
|
||||
selection.reversed = is_selection_reversed;
|
||||
}
|
||||
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let selections: Vec<Selection<MultiBufferOffset>> = selections
|
||||
.iter()
|
||||
.map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
|
||||
.collect();
|
||||
|
||||
self.select_syntax_node_history.disable_clearing = true;
|
||||
self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select(selections.to_vec());
|
||||
s.select(selections);
|
||||
});
|
||||
self.select_syntax_node_history.disable_clearing = false;
|
||||
|
||||
|
||||
@@ -23113,6 +23113,61 @@ async fn test_toggle_deletion_hunk_at_start_of_file(
|
||||
cx.assert_state_with_diff(hunk_expanded);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_smaller_syntax_node_after_diff_hunk_collapse(
|
||||
executor: BackgroundExecutor,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(rust_lang()), cx));
|
||||
|
||||
cx.set_state(
|
||||
&r#"
|
||||
fn main() {
|
||||
let x = ˇ1;
|
||||
}
|
||||
"#
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
let diff_base = r#"
|
||||
fn removed_one() {
|
||||
println!("this function was deleted");
|
||||
}
|
||||
|
||||
fn removed_two() {
|
||||
println!("this function was also deleted");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 1;
|
||||
}
|
||||
"#
|
||||
.unindent();
|
||||
cx.set_head_text(&diff_base);
|
||||
executor.run_until_parked();
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
|
||||
});
|
||||
executor.run_until_parked();
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.select_larger_syntax_node(&SelectLargerSyntaxNode, window, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.collapse_all_diff_hunks(&CollapseAllDiffHunks, window, cx);
|
||||
});
|
||||
executor.run_until_parked();
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.select_smaller_syntax_node(&SelectSmallerSyntaxNode, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_expand_first_line_diff_hunk_keeps_deleted_lines_visible(
|
||||
executor: BackgroundExecutor,
|
||||
|
||||
Reference in new issue
Block a user