Fix crash in Editor::rotate_selections using cursors on nonconsecutive lines (#63937)
Crash repro: ``` A 1234567890 B C ``` 1. place cursors before `A`, `B`, and `C` 2. run `editor: rotate selections forward` Caused by the current `Editor::rotate_selections` logic assuming that the text between the end of one of the lines being rotated and the start of the next one is just the newline: ```rs let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0; ``` which fails when those lines aren't consecutive in the editor. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed crash in `editor: rotate selections forward` and `editor: rotate selections backward` using cursors on nonconsecutive lines.
This commit is contained in:
1 parent
a6b3c5b33d
commit
bcf8c7bee7
2 files changed
+42
-7
No files matched your search
@@ -6826,13 +6826,14 @@ impl Editor {
|
||||
.map(|(i, &row)| (row, i))
|
||||
.collect();
|
||||
|
||||
// Compute new line start offsets after rotation (handles CRLF)
|
||||
let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
|
||||
let first_line_start = line_ranges[0].start.0;
|
||||
let mut new_line_starts: Vec<usize> = vec![first_line_start];
|
||||
for text in line_texts.iter().take(num_rows - 1) {
|
||||
let prev_start = *new_line_starts.last().unwrap();
|
||||
new_line_starts.push(prev_start + text.len() + newline_len);
|
||||
let mut old_line_end = 0;
|
||||
let mut new_line_end = 0;
|
||||
let mut new_line_starts = Vec::new();
|
||||
for (range, text) in line_ranges.iter().zip(&line_texts) {
|
||||
let line_start = new_line_end + (range.start.0 - old_line_end);
|
||||
new_line_starts.push(line_start);
|
||||
old_line_end = range.end.0;
|
||||
new_line_end = line_start + text.len();
|
||||
}
|
||||
|
||||
let new_selections = selections
|
||||
|
||||
@@ -8623,6 +8623,40 @@ async fn test_rotate_selections(cx: &mut TestAppContext) {
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rotate_selections_nonconsecutive_lines(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
ˇline1
|
||||
line2
|
||||
liˇne3
|
||||
line4ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.rotate_selections_forward(&RotateSelectionsForward, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
line4ˇ
|
||||
line2
|
||||
ˇline1
|
||||
liˇne3
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, window, cx| {
|
||||
e.rotate_selections_backward(&RotateSelectionsBackward, window, cx)
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
ˇline1
|
||||
line2
|
||||
liˇne3
|
||||
line4ˇ
|
||||
"});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_line_up_down(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in new issue
Block a user