editor: Add multi cursor support for AddSelectionAbove
/AddSelectionBelow
(#32204)
Closes #31648 This PR adds support for: - Expanding multiple cursors above/below - Expanding multiple selections above/below - Adding new cursors/selections when expansion has already been done. Existing expansions preserve their state and expand/shrink according to the action, while new cursors/selections act like freshly created ones. Tests for both cursor and selections: - below/above cases - undo/redo cases - adding new cursors/selections with existing expansion Before/After: https://github.com/user-attachments/assets/d2fd556b-8972-4719-bd86-e633d42a1aa3 Release Notes: - Improved `AddSelectionAbove` and `AddSelectionBelow` to extend multiple cursors/selections.
This commit is contained in:
parent
711a9e5753
commit
6a8fdbfd62
2 changed files with 383 additions and 43 deletions
|
@ -6300,6 +6300,296 @@ async fn test_add_selection_above_below(cx: &mut TestAppContext) {
|
|||
));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_add_selection_above_below_multi_cursor(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
cx.set_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne two
|
||||
line three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple cursors expand in the same direction
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
liˇne three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple cursors expand below overflow
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
liˇne thˇree
|
||||
liˇne foˇur"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple cursors retrieves back correctly
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
liˇne thˇree
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple cursor groups maintain independent direction - first expands up, second shrinks above
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"liˇne onˇe
|
||||
liˇne two
|
||||
line three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.undo_selection(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test undo
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
line three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.redo_selection(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test redo
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"liˇne onˇe
|
||||
liˇne two
|
||||
line three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.set_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
ijkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple selections expand in the same direction
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"ab«cdˇ»
|
||||
ef«ghˇ»
|
||||
«iˇ»jkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple selection upward overflow
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"ab«cdˇ»
|
||||
«eˇ»f«ghˇ»
|
||||
«iˇ»jkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple selection retrieves back correctly
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
«iˇ»jkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test multiple cursor groups maintain independent direction - first shrinks down, second expands below
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
ij«klˇ»
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.undo_selection(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test undo
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
«iˇ»jkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.redo_selection(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test redo
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
ij«klˇ»
|
||||
«mˇ»nop"#
|
||||
));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_add_selection_above_below_multi_cursor_existing_state(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
cx.set_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne two
|
||||
line three
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// initial state with two multi cursor groups
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
liˇne thˇree
|
||||
liˇne foˇur"#
|
||||
));
|
||||
|
||||
// add single cursor in middle - simulate opt click
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
let new_cursor_point = DisplayPoint::new(DisplayRow(2), 4);
|
||||
editor.begin_selection(new_cursor_point, true, 1, window, cx);
|
||||
editor.end_selection(window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇne twˇo
|
||||
liˇneˇ thˇree
|
||||
liˇne foˇur"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test new added selection expands above and existing selection shrinks
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"line onˇe
|
||||
liˇneˇ twˇo
|
||||
liˇneˇ thˇree
|
||||
line four"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test new added selection expands above and existing selection shrinks
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"lineˇ onˇe
|
||||
liˇneˇ twˇo
|
||||
lineˇ three
|
||||
line four"#
|
||||
));
|
||||
|
||||
// intial state with two selection groups
|
||||
cx.set_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
ijkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
editor.add_selection_above(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"ab«cdˇ»
|
||||
«eˇ»f«ghˇ»
|
||||
«iˇ»jkl
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
// add single selection in middle - simulate opt drag
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
let new_cursor_point = DisplayPoint::new(DisplayRow(2), 3);
|
||||
editor.begin_selection(new_cursor_point, true, 1, window, cx);
|
||||
editor.update_selection(
|
||||
DisplayPoint::new(DisplayRow(2), 4),
|
||||
0,
|
||||
gpui::Point::<f32>::default(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
editor.end_selection(window, cx);
|
||||
});
|
||||
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"ab«cdˇ»
|
||||
«eˇ»f«ghˇ»
|
||||
«iˇ»jk«lˇ»
|
||||
«mˇ»nop"#
|
||||
));
|
||||
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.add_selection_below(&Default::default(), window, cx);
|
||||
});
|
||||
|
||||
// test new added selection expands below, others shrinks from above
|
||||
cx.assert_editor_state(indoc!(
|
||||
r#"abcd
|
||||
ef«ghˇ»
|
||||
«iˇ»jk«lˇ»
|
||||
«mˇ»no«pˇ»"#
|
||||
));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_next(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue