feat(command): select_all_children

This commit is contained in:
Skyler Hawthorne 2023-06-22 21:33:40 -04:00 committed by Michael Davis
parent 87c4161732
commit fa67c5c474
4 changed files with 170 additions and 6 deletions

View file

@ -63,7 +63,21 @@ pub fn select_all_siblings(tree: &Tree, text: RopeSlice, selection: Selection) -
root_node root_node
.descendant_for_byte_range(from, to) .descendant_for_byte_range(from, to)
.and_then(find_parent_with_more_children) .and_then(find_parent_with_more_children)
.map(|parent| select_children(parent, text, range.direction())) .and_then(|parent| select_children(parent, text, range.direction()))
.unwrap_or_else(|| vec![range].into_iter())
})
}
pub fn select_all_children(tree: &Tree, text: RopeSlice, selection: Selection) -> Selection {
let root_node = &tree.root_node();
selection.transform_iter(|range| {
let from = text.char_to_byte(range.from());
let to = text.char_to_byte(range.to());
root_node
.descendant_for_byte_range(from, to)
.and_then(|parent| select_children(parent, text, range.direction()))
.unwrap_or_else(|| vec![range].into_iter()) .unwrap_or_else(|| vec![range].into_iter())
}) })
} }
@ -72,10 +86,11 @@ fn select_children(
node: Node, node: Node,
text: RopeSlice, text: RopeSlice,
direction: Direction, direction: Direction,
) -> <Vec<Range> as std::iter::IntoIterator>::IntoIter { ) -> Option<<Vec<Range> as std::iter::IntoIterator>::IntoIter> {
let mut cursor = node.walk(); let mut cursor = node.walk();
node.named_children(&mut cursor) let children = node
.named_children(&mut cursor)
.map(|child| { .map(|child| {
let from = text.byte_to_char(child.start_byte()); let from = text.byte_to_char(child.start_byte());
let to = text.byte_to_char(child.end_byte()); let to = text.byte_to_char(child.end_byte());
@ -86,8 +101,13 @@ fn select_children(
Range::new(from, to) Range::new(from, to)
} }
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>();
.into_iter()
if !children.is_empty() {
Some(children.into_iter())
} else {
None
}
} }
fn find_sibling_recursive<F>(node: Node, sibling_fn: F) -> Option<Node> fn find_sibling_recursive<F>(node: Node, sibling_fn: F) -> Option<Node>

View file

@ -440,7 +440,8 @@ impl MappableCommand {
shrink_selection, "Shrink selection to previously expanded syntax node", shrink_selection, "Shrink selection to previously expanded syntax node",
select_next_sibling, "Select next sibling in the syntax tree", select_next_sibling, "Select next sibling in the syntax tree",
select_prev_sibling, "Select previous sibling the in syntax tree", select_prev_sibling, "Select previous sibling the in syntax tree",
select_all_siblings, "Select all siblings in the syntax tree", select_all_siblings, "Select all siblings of the current node",
select_all_children, "Select all children of the current node",
jump_forward, "Jump forward on jumplist", jump_forward, "Jump forward on jumplist",
jump_backward, "Jump backward on jumplist", jump_backward, "Jump backward on jumplist",
save_selection, "Save current selection to jumplist", save_selection, "Save current selection to jumplist",
@ -4991,6 +4992,23 @@ fn select_all_siblings(cx: &mut Context) {
cx.editor.apply_motion(motion); cx.editor.apply_motion(motion);
} }
fn select_all_children(cx: &mut Context) {
let motion = |editor: &mut Editor| {
let (view, doc) = current!(editor);
if let Some(syntax) = doc.syntax() {
let text = doc.text().slice(..);
let current_selection = doc.selection(view.id);
let selection =
object::select_all_children(syntax.tree(), text, current_selection.clone());
doc.set_selection(view.id, selection);
}
};
motion(cx.editor);
cx.editor.last_motion = Some(Motion(Box::new(motion)));
}
fn match_brackets(cx: &mut Context) { fn match_brackets(cx: &mut Context) {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
let is_select = cx.editor.mode == Mode::Select; let is_select = cx.editor.mode == Mode::Select;

View file

@ -87,6 +87,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"A-;" => flip_selections, "A-;" => flip_selections,
"A-o" | "A-up" => expand_selection, "A-o" | "A-up" => expand_selection,
"A-i" | "A-down" => shrink_selection, "A-i" | "A-down" => shrink_selection,
"A-I" | "A-S-down" => select_all_children,
"A-p" | "A-left" => select_prev_sibling, "A-p" | "A-left" => select_prev_sibling,
"A-n" | "A-right" => select_next_sibling, "A-n" | "A-right" => select_next_sibling,
"A-e" => move_parent_node_end, "A-e" => move_parent_node_end,

View file

@ -601,3 +601,128 @@ async fn select_all_siblings() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test(flavor = "multi_thread")]
async fn select_all_children() -> anyhow::Result<()> {
let tests = vec![
// basic tests
(
helpers::platform_line(indoc! {r##"
let foo = bar#[(a, b, c)|]#;
"##}),
"<A-I>",
helpers::platform_line(indoc! {r##"
let foo = bar(#[a|]#, #(b|)#, #(c|)#);
"##}),
),
(
helpers::platform_line(indoc! {r##"
let a = #[[
1,
2,
3,
4,
5,
]|]#;
"##}),
"<A-I>",
helpers::platform_line(indoc! {r##"
let a = [
#[1|]#,
#(2|)#,
#(3|)#,
#(4|)#,
#(5|)#,
];
"##}),
),
// direction is preserved
(
helpers::platform_line(indoc! {r##"
let a = #[|[
1,
2,
3,
4,
5,
]]#;
"##}),
"<A-I>",
helpers::platform_line(indoc! {r##"
let a = [
#[|1]#,
#(|2)#,
#(|3)#,
#(|4)#,
#(|5)#,
];
"##}),
),
// can't pick any more children - selection stays the same
(
helpers::platform_line(indoc! {r##"
let a = [
#[1|]#,
#(2|)#,
#(3|)#,
#(4|)#,
#(5|)#,
];
"##}),
"<A-I>",
helpers::platform_line(indoc! {r##"
let a = [
#[1|]#,
#(2|)#,
#(3|)#,
#(4|)#,
#(5|)#,
];
"##}),
),
// each cursor does the sibling select independently
(
helpers::platform_line(indoc! {r##"
let a = #[|[
1,
2,
3,
4,
5,
]]#;
let b = #([
"one",
"two",
"three",
"four",
"five",
]|)#;
"##}),
"<A-I>",
helpers::platform_line(indoc! {r##"
let a = [
#[|1]#,
#(|2)#,
#(|3)#,
#(|4)#,
#(|5)#,
];
let b = [
#("one"|)#,
#("two"|)#,
#("three"|)#,
#("four"|)#,
#("five"|)#,
];
"##}),
),
];
for test in tests {
test_with_config(AppBuilder::new().with_file("foo.rs", None), test).await?;
}
Ok(())
}