Skip to content

Commit fa67c5c

Browse files
dead10ckthe-mikedavis
authored andcommitted
feat(command): select_all_children
1 parent 87c4161 commit fa67c5c

File tree

4 files changed

+170
-6
lines changed

4 files changed

+170
-6
lines changed

helix-core/src/object.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,21 @@ pub fn select_all_siblings(tree: &Tree, text: RopeSlice, selection: Selection) -
6363
root_node
6464
.descendant_for_byte_range(from, to)
6565
.and_then(find_parent_with_more_children)
66-
.map(|parent| select_children(parent, text, range.direction()))
66+
.and_then(|parent| select_children(parent, text, range.direction()))
67+
.unwrap_or_else(|| vec![range].into_iter())
68+
})
69+
}
70+
71+
pub fn select_all_children(tree: &Tree, text: RopeSlice, selection: Selection) -> Selection {
72+
let root_node = &tree.root_node();
73+
74+
selection.transform_iter(|range| {
75+
let from = text.char_to_byte(range.from());
76+
let to = text.char_to_byte(range.to());
77+
78+
root_node
79+
.descendant_for_byte_range(from, to)
80+
.and_then(|parent| select_children(parent, text, range.direction()))
6781
.unwrap_or_else(|| vec![range].into_iter())
6882
})
6983
}
@@ -72,10 +86,11 @@ fn select_children(
7286
node: Node,
7387
text: RopeSlice,
7488
direction: Direction,
75-
) -> <Vec<Range> as std::iter::IntoIterator>::IntoIter {
89+
) -> Option<<Vec<Range> as std::iter::IntoIterator>::IntoIter> {
7690
let mut cursor = node.walk();
7791

78-
node.named_children(&mut cursor)
92+
let children = node
93+
.named_children(&mut cursor)
7994
.map(|child| {
8095
let from = text.byte_to_char(child.start_byte());
8196
let to = text.byte_to_char(child.end_byte());
@@ -86,8 +101,13 @@ fn select_children(
86101
Range::new(from, to)
87102
}
88103
})
89-
.collect::<Vec<_>>()
90-
.into_iter()
104+
.collect::<Vec<_>>();
105+
106+
if !children.is_empty() {
107+
Some(children.into_iter())
108+
} else {
109+
None
110+
}
91111
}
92112

93113
fn find_sibling_recursive<F>(node: Node, sibling_fn: F) -> Option<Node>

helix-term/src/commands.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ impl MappableCommand {
440440
shrink_selection, "Shrink selection to previously expanded syntax node",
441441
select_next_sibling, "Select next sibling in the syntax tree",
442442
select_prev_sibling, "Select previous sibling the in syntax tree",
443-
select_all_siblings, "Select all siblings in the syntax tree",
443+
select_all_siblings, "Select all siblings of the current node",
444+
select_all_children, "Select all children of the current node",
444445
jump_forward, "Jump forward on jumplist",
445446
jump_backward, "Jump backward on jumplist",
446447
save_selection, "Save current selection to jumplist",
@@ -4991,6 +4992,23 @@ fn select_all_siblings(cx: &mut Context) {
49914992
cx.editor.apply_motion(motion);
49924993
}
49934994

4995+
fn select_all_children(cx: &mut Context) {
4996+
let motion = |editor: &mut Editor| {
4997+
let (view, doc) = current!(editor);
4998+
4999+
if let Some(syntax) = doc.syntax() {
5000+
let text = doc.text().slice(..);
5001+
let current_selection = doc.selection(view.id);
5002+
let selection =
5003+
object::select_all_children(syntax.tree(), text, current_selection.clone());
5004+
doc.set_selection(view.id, selection);
5005+
}
5006+
};
5007+
5008+
motion(cx.editor);
5009+
cx.editor.last_motion = Some(Motion(Box::new(motion)));
5010+
}
5011+
49945012
fn match_brackets(cx: &mut Context) {
49955013
let (view, doc) = current!(cx.editor);
49965014
let is_select = cx.editor.mode == Mode::Select;

helix-term/src/keymap/default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
8787
"A-;" => flip_selections,
8888
"A-o" | "A-up" => expand_selection,
8989
"A-i" | "A-down" => shrink_selection,
90+
"A-I" | "A-S-down" => select_all_children,
9091
"A-p" | "A-left" => select_prev_sibling,
9192
"A-n" | "A-right" => select_next_sibling,
9293
"A-e" => move_parent_node_end,

helix-term/tests/test/commands/movement.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,3 +601,128 @@ async fn select_all_siblings() -> anyhow::Result<()> {
601601

602602
Ok(())
603603
}
604+
605+
#[tokio::test(flavor = "multi_thread")]
606+
async fn select_all_children() -> anyhow::Result<()> {
607+
let tests = vec![
608+
// basic tests
609+
(
610+
helpers::platform_line(indoc! {r##"
611+
let foo = bar#[(a, b, c)|]#;
612+
"##}),
613+
"<A-I>",
614+
helpers::platform_line(indoc! {r##"
615+
let foo = bar(#[a|]#, #(b|)#, #(c|)#);
616+
"##}),
617+
),
618+
(
619+
helpers::platform_line(indoc! {r##"
620+
let a = #[[
621+
1,
622+
2,
623+
3,
624+
4,
625+
5,
626+
]|]#;
627+
"##}),
628+
"<A-I>",
629+
helpers::platform_line(indoc! {r##"
630+
let a = [
631+
#[1|]#,
632+
#(2|)#,
633+
#(3|)#,
634+
#(4|)#,
635+
#(5|)#,
636+
];
637+
"##}),
638+
),
639+
// direction is preserved
640+
(
641+
helpers::platform_line(indoc! {r##"
642+
let a = #[|[
643+
1,
644+
2,
645+
3,
646+
4,
647+
5,
648+
]]#;
649+
"##}),
650+
"<A-I>",
651+
helpers::platform_line(indoc! {r##"
652+
let a = [
653+
#[|1]#,
654+
#(|2)#,
655+
#(|3)#,
656+
#(|4)#,
657+
#(|5)#,
658+
];
659+
"##}),
660+
),
661+
// can't pick any more children - selection stays the same
662+
(
663+
helpers::platform_line(indoc! {r##"
664+
let a = [
665+
#[1|]#,
666+
#(2|)#,
667+
#(3|)#,
668+
#(4|)#,
669+
#(5|)#,
670+
];
671+
"##}),
672+
"<A-I>",
673+
helpers::platform_line(indoc! {r##"
674+
let a = [
675+
#[1|]#,
676+
#(2|)#,
677+
#(3|)#,
678+
#(4|)#,
679+
#(5|)#,
680+
];
681+
"##}),
682+
),
683+
// each cursor does the sibling select independently
684+
(
685+
helpers::platform_line(indoc! {r##"
686+
let a = #[|[
687+
1,
688+
2,
689+
3,
690+
4,
691+
5,
692+
]]#;
693+
694+
let b = #([
695+
"one",
696+
"two",
697+
"three",
698+
"four",
699+
"five",
700+
]|)#;
701+
"##}),
702+
"<A-I>",
703+
helpers::platform_line(indoc! {r##"
704+
let a = [
705+
#[|1]#,
706+
#(|2)#,
707+
#(|3)#,
708+
#(|4)#,
709+
#(|5)#,
710+
];
711+
712+
let b = [
713+
#("one"|)#,
714+
#("two"|)#,
715+
#("three"|)#,
716+
#("four"|)#,
717+
#("five"|)#,
718+
];
719+
"##}),
720+
),
721+
];
722+
723+
for test in tests {
724+
test_with_config(AppBuilder::new().with_file("foo.rs", None), test).await?;
725+
}
726+
727+
Ok(())
728+
}

0 commit comments

Comments
 (0)