Add action to select inside enclosing brackets (#59005)
## Summary - add an editor action for selecting the contents of the innermost enclosing bracket pair - register the action without assigning a default key binding - cover cursor, selection, nested bracket, no-op, and multi-cursor cases ## Tests - cargo fmt --check - cargo test -p editor test_select_inside_enclosing_bracket --lib - cargo test -p editor test_move_to_enclosing_bracket --lib Release Notes: - Added action to select the contents enclosed by brackets
This commit is contained in:
1 parent
a95501c554
commit
915de507cd
4 files changed
+122
No files matched your search
@@ -773,6 +773,8 @@ actions!(
|
||||
SelectDown,
|
||||
/// Selects the enclosing symbol.
|
||||
SelectEnclosingSymbol,
|
||||
/// Selects inside the innermost enclosing bracket pair.
|
||||
SelectInsideEnclosingBracket,
|
||||
/// Selects to the start of the next larger syntax node.
|
||||
SelectToStartOfLargerSyntaxNode,
|
||||
/// Selects to the end of the next larger syntax node.
|
||||
|
||||
@@ -21266,6 +21266,88 @@ async fn test_move_to_enclosing_bracket(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_inside_enclosing_bracket(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
|
||||
|
||||
#[track_caller]
|
||||
fn assert_after_runs(before: &str, after: &str, runs: usize, cx: &mut EditorLspTestContext) {
|
||||
let _state_context = cx.set_state(before);
|
||||
cx.run_until_parked();
|
||||
for _ in 0..runs {
|
||||
cx.update_editor(|editor, window, cx| {
|
||||
editor.select_inside_enclosing_bracket(&SelectInsideEnclosingBracket, window, cx)
|
||||
});
|
||||
}
|
||||
cx.run_until_parked();
|
||||
cx.assert_editor_state(after);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert(before: &str, after: &str, cx: &mut EditorLspTestContext) {
|
||||
assert_after_runs(before, after, 1, cx);
|
||||
}
|
||||
|
||||
assert("console.log(ˇvar);", "console.log(«varˇ»);", &mut cx);
|
||||
assert("console.logˇ(var);", "console.log(«varˇ»);", &mut cx);
|
||||
assert("console.log(var)ˇ;", "console.log(«varˇ»);", &mut cx);
|
||||
assert(
|
||||
"let numbers = [1, ˇ2, 3];",
|
||||
"let numbers = [«1, 2, 3ˇ»];",
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
"const object = { foo: ˇbar };",
|
||||
"const object = {« foo: bar ˇ»};",
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
r#"const doubleQuoted = "foo ˇbar";"#,
|
||||
r#"const doubleQuoted = "«foo barˇ»";"#,
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
"const singleQuoted = 'foo ˇbar';",
|
||||
"const singleQuoted = '«foo barˇ»';",
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
"const template = `foo ˇbar`;",
|
||||
"const template = `«foo barˇ»`;",
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
"let result = foo(bar(ˇbaz));",
|
||||
"let result = foo(bar(«bazˇ»));",
|
||||
&mut cx,
|
||||
);
|
||||
assert(
|
||||
"let result = foo(«barˇ»(baz));",
|
||||
"let result = foo(«bar(baz)ˇ»);",
|
||||
&mut cx,
|
||||
);
|
||||
assert_after_runs(
|
||||
"let result = foo(bar(ˇbaz));",
|
||||
"let result = foo(«bar(baz)ˇ»);",
|
||||
2,
|
||||
&mut cx,
|
||||
);
|
||||
assert_after_runs(
|
||||
r#"let result = (xx[xxx{xxˇx}] xx"xxx"xx);"#,
|
||||
r#"let result = («xx[xxx{xxx}] xx"xxx"xxˇ»);"#,
|
||||
3,
|
||||
&mut cx,
|
||||
);
|
||||
assert("let plain = ˇvalue;", "let plain = ˇvalue;", &mut cx);
|
||||
assert(
|
||||
"foo(ˇone); bar(ˇtwo);",
|
||||
"foo(«oneˇ»); bar(«twoˇ»);",
|
||||
&mut cx,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_move_to_enclosing_bracket_in_markdown_code_block(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
@@ -334,6 +334,7 @@ impl EditorElement {
|
||||
register_action(editor, window, Editor::move_to_start_of_larger_syntax_node);
|
||||
register_action(editor, window, Editor::move_to_end_of_larger_syntax_node);
|
||||
register_action(editor, window, Editor::select_enclosing_symbol);
|
||||
register_action(editor, window, Editor::select_inside_enclosing_bracket);
|
||||
register_action(editor, window, Editor::move_to_enclosing_bracket);
|
||||
register_action(editor, window, Editor::undo_selection);
|
||||
register_action(editor, window, Editor::redo_selection);
|
||||
|
||||
@@ -966,6 +966,43 @@ impl Editor {
|
||||
self.select_to_syntax_nodes(window, cx, true);
|
||||
}
|
||||
|
||||
pub fn select_inside_enclosing_bracket(
|
||||
&mut self,
|
||||
_: &SelectInsideEnclosingBracket,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.change_selections(Default::default(), window, cx, |s| {
|
||||
s.move_offsets_with(&mut |snapshot, selection| {
|
||||
let Some(enclosing_bracket_ranges) =
|
||||
snapshot.enclosing_bracket_ranges(selection.start..selection.end)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut best = None;
|
||||
let mut best_length = usize::MAX;
|
||||
|
||||
for (open, close) in enclosing_bracket_ranges {
|
||||
let inside = open.end..close.start;
|
||||
if inside == (selection.start..selection.end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let length = close.end - open.start;
|
||||
if length < best_length {
|
||||
best_length = length;
|
||||
best = Some(inside);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inside) = best {
|
||||
selection.set_head_tail(inside.end, inside.start, SelectionGoal::None);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn move_to_enclosing_bracket(
|
||||
&mut self,
|
||||
_: &MoveToEnclosingBracket,
|
||||
|
||||
Reference in new issue
Block a user