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