@@ -451,6 +451,7 @@ impl MappableCommand {
451
451
select_prev_sibling, "Select previous sibling the in syntax tree" ,
452
452
select_all_siblings, "Select all siblings of the current node" ,
453
453
select_all_children, "Select all children of the current node" ,
454
+ expand_selection_around, "Expand selection to parent syntax node, but exclude the selection you started with" ,
454
455
jump_forward, "Jump forward on jumplist" ,
455
456
jump_backward, "Jump backward on jumplist" ,
456
457
save_selection, "Save current selection to jumplist" ,
@@ -4876,6 +4877,8 @@ fn reverse_selection_contents(cx: &mut Context) {
4876
4877
// tree sitter node selection
4877
4878
4878
4879
const EXPAND_KEY : & str = "expand" ;
4880
+ const EXPAND_AROUND_BASE_KEY : & str = "expand_around_base" ;
4881
+ const PARENTS_KEY : & str = "parents" ;
4879
4882
4880
4883
fn expand_selection ( cx : & mut Context ) {
4881
4884
let motion = |editor : & mut Editor | {
@@ -4919,6 +4922,33 @@ fn shrink_selection(cx: &mut Context) {
4919
4922
if let Some ( prev_selection) = prev_expansions. pop ( ) {
4920
4923
// allow shrinking the selection only if current selection contains the previous object selection
4921
4924
doc. set_selection_clear ( view. id , prev_selection, false ) ;
4925
+
4926
+ // Do a corresponding pop of the parents from `expand_selection_around`
4927
+ doc. view_data_mut ( view. id )
4928
+ . object_selections
4929
+ . entry ( PARENTS_KEY )
4930
+ . and_modify ( |parents| {
4931
+ parents. pop ( ) ;
4932
+ } ) ;
4933
+
4934
+ // need to do this again because borrowing
4935
+ let prev_expansions = doc
4936
+ . view_data_mut ( view. id )
4937
+ . object_selections
4938
+ . entry ( EXPAND_KEY )
4939
+ . or_default ( ) ;
4940
+
4941
+ // if we've emptied out the previous expansions, then clear out the
4942
+ // base history as well so it doesn't get used again erroneously
4943
+ if prev_expansions. is_empty ( ) {
4944
+ doc. view_data_mut ( view. id )
4945
+ . object_selections
4946
+ . entry ( EXPAND_AROUND_BASE_KEY )
4947
+ . and_modify ( |base| {
4948
+ base. clear ( ) ;
4949
+ } ) ;
4950
+ }
4951
+
4922
4952
return ;
4923
4953
}
4924
4954
@@ -4933,6 +4963,81 @@ fn shrink_selection(cx: &mut Context) {
4933
4963
cx. editor . apply_motion ( motion) ;
4934
4964
}
4935
4965
4966
+ fn expand_selection_around ( cx : & mut Context ) {
4967
+ let motion = |editor : & mut Editor | {
4968
+ let ( view, doc) = current ! ( editor) ;
4969
+
4970
+ if doc. syntax ( ) . is_some ( ) {
4971
+ // [NOTE] we do this pop and push dance because if we don't take
4972
+ // ownership of the objects, then we require multiple
4973
+ // mutable references to the view's object selections
4974
+ let mut parents_selection = doc
4975
+ . view_data_mut ( view. id )
4976
+ . object_selections
4977
+ . entry ( PARENTS_KEY )
4978
+ . or_default ( )
4979
+ . pop ( ) ;
4980
+
4981
+ let mut base_selection = doc
4982
+ . view_data_mut ( view. id )
4983
+ . object_selections
4984
+ . entry ( EXPAND_AROUND_BASE_KEY )
4985
+ . or_default ( )
4986
+ . pop ( ) ;
4987
+
4988
+ let current_selection = doc. selection ( view. id ) . clone ( ) ;
4989
+
4990
+ if parents_selection. is_none ( ) || base_selection. is_none ( ) {
4991
+ parents_selection = Some ( current_selection. clone ( ) ) ;
4992
+ base_selection = Some ( current_selection. clone ( ) ) ;
4993
+ }
4994
+
4995
+ let text = doc. text ( ) . slice ( ..) ;
4996
+ let syntax = doc. syntax ( ) . unwrap ( ) ;
4997
+
4998
+ let outside_selection =
4999
+ object:: expand_selection ( syntax, text, parents_selection. clone ( ) . unwrap ( ) ) ;
5000
+
5001
+ let target_selection = match outside_selection
5002
+ . clone ( )
5003
+ . without ( & base_selection. clone ( ) . unwrap ( ) )
5004
+ {
5005
+ Some ( sel) => sel,
5006
+ None => outside_selection. clone ( ) ,
5007
+ } ;
5008
+
5009
+ // check if selection is different from the last one
5010
+ if target_selection != current_selection {
5011
+ // save current selection so it can be restored using shrink_selection
5012
+ doc. view_data_mut ( view. id )
5013
+ . object_selections
5014
+ . entry ( EXPAND_KEY )
5015
+ . or_default ( )
5016
+ . push ( current_selection) ;
5017
+
5018
+ doc. set_selection_clear ( view. id , target_selection, false ) ;
5019
+ }
5020
+
5021
+ let parents = doc
5022
+ . view_data_mut ( view. id )
5023
+ . object_selections
5024
+ . entry ( PARENTS_KEY )
5025
+ . or_default ( ) ;
5026
+
5027
+ parents. push ( parents_selection. unwrap ( ) ) ;
5028
+ parents. push ( outside_selection) ;
5029
+
5030
+ doc. view_data_mut ( view. id )
5031
+ . object_selections
5032
+ . entry ( EXPAND_AROUND_BASE_KEY )
5033
+ . or_default ( )
5034
+ . push ( base_selection. unwrap ( ) ) ;
5035
+ }
5036
+ } ;
5037
+
5038
+ cx. editor . apply_motion ( motion) ;
5039
+ }
5040
+
4936
5041
fn select_sibling_impl < F > ( cx : & mut Context , sibling_fn : F )
4937
5042
where
4938
5043
F : Fn ( & helix_core:: Syntax , RopeSlice , Selection ) -> Selection + ' static ,
@@ -4947,6 +5052,7 @@ where
4947
5052
doc. set_selection ( view. id , selection) ;
4948
5053
}
4949
5054
} ;
5055
+
4950
5056
cx. editor . apply_motion ( motion) ;
4951
5057
}
4952
5058
0 commit comments