Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/editor/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
82 changes: 82 additions & 0 deletions crates/editor/src/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, |_| {});
Expand Down
1 change: 1 addition & 0 deletions crates/editor/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions crates/editor/src/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading