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:
Myself authored and GitHub committed 2026-06-11 04:04:36 +00:00
1 parent a95501c554
commit 915de507cd
4 files changed
+122

No files matched your search

+2
View File
@@ -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.
+82
View File
@@ -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, |_| {});
+1
View File
@@ -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);
+37
View File
@@ -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,