@@ -408,6 +408,7 @@ impl MappableCommand {
408
408
rotate_selection_contents_backward, "Rotate selections contents backward" ,
409
409
expand_selection, "Expand selection to parent syntax node" ,
410
410
shrink_selection, "Shrink selection to previously expanded syntax node" ,
411
+ expand_selection_around, "Expand selection to parent syntax node, but exclude the selection you started with" ,
411
412
select_next_sibling, "Select next sibling in syntax tree" ,
412
413
select_prev_sibling, "Select previous sibling in syntax tree" ,
413
414
jump_forward, "Jump forward on jumplist" ,
@@ -4469,6 +4470,8 @@ fn rotate_selection_contents_backward(cx: &mut Context) {
4469
4470
// tree sitter node selection
4470
4471
4471
4472
const EXPAND_KEY : & str = "expand" ;
4473
+ const EXPAND_AROUND_BASE_KEY : & str = "expand_around_base" ;
4474
+ const PARENTS_KEY : & str = "parents" ;
4472
4475
4473
4476
fn expand_selection ( cx : & mut Context ) {
4474
4477
let motion = |editor : & mut Editor | {
@@ -4513,6 +4516,33 @@ fn shrink_selection(cx: &mut Context) {
4513
4516
if let Some ( prev_selection) = prev_expansions. pop ( ) {
4514
4517
// allow shrinking the selection only if current selection contains the previous object selection
4515
4518
doc. set_selection_clear ( view. id , prev_selection, false ) ;
4519
+
4520
+ // Do a corresponding pop of the parents from `expand_selection_around`
4521
+ doc. view_data_mut ( view. id )
4522
+ . object_selections
4523
+ . entry ( PARENTS_KEY )
4524
+ . and_modify ( |parents| {
4525
+ parents. pop ( ) ;
4526
+ } ) ;
4527
+
4528
+ // need to do this again because borrowing
4529
+ let prev_expansions = doc
4530
+ . view_data_mut ( view. id )
4531
+ . object_selections
4532
+ . entry ( EXPAND_KEY )
4533
+ . or_default ( ) ;
4534
+
4535
+ // if we've emptied out the previous expansions, then clear out the
4536
+ // base history as well so it doesn't get used again erroneously
4537
+ if prev_expansions. is_empty ( ) {
4538
+ doc. view_data_mut ( view. id )
4539
+ . object_selections
4540
+ . entry ( EXPAND_AROUND_BASE_KEY )
4541
+ . and_modify ( |base| {
4542
+ base. clear ( ) ;
4543
+ } ) ;
4544
+ }
4545
+
4516
4546
return ;
4517
4547
}
4518
4548
@@ -4528,6 +4558,82 @@ fn shrink_selection(cx: &mut Context) {
4528
4558
cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4529
4559
}
4530
4560
4561
+ fn expand_selection_around ( cx : & mut Context ) {
4562
+ let motion = |editor : & mut Editor | {
4563
+ let ( view, doc) = current ! ( editor) ;
4564
+
4565
+ if doc. syntax ( ) . is_some ( ) {
4566
+ // [NOTE] we do this pop and push dance because if we don't take
4567
+ // ownership of the objects, then we require multiple
4568
+ // mutable references to the view's object selections
4569
+ let mut parents_selection = doc
4570
+ . view_data_mut ( view. id )
4571
+ . object_selections
4572
+ . entry ( PARENTS_KEY )
4573
+ . or_default ( )
4574
+ . pop ( ) ;
4575
+
4576
+ let mut base_selection = doc
4577
+ . view_data_mut ( view. id )
4578
+ . object_selections
4579
+ . entry ( EXPAND_AROUND_BASE_KEY )
4580
+ . or_default ( )
4581
+ . pop ( ) ;
4582
+
4583
+ let current_selection = doc. selection ( view. id ) . clone ( ) ;
4584
+
4585
+ if parents_selection. is_none ( ) || base_selection. is_none ( ) {
4586
+ parents_selection = Some ( current_selection. clone ( ) ) ;
4587
+ base_selection = Some ( current_selection. clone ( ) ) ;
4588
+ }
4589
+
4590
+ let text = doc. text ( ) . slice ( ..) ;
4591
+ let syntax = doc. syntax ( ) . unwrap ( ) ;
4592
+
4593
+ let outside_selection =
4594
+ object:: expand_selection ( syntax, text, parents_selection. clone ( ) . unwrap ( ) ) ;
4595
+
4596
+ let target_selection = match outside_selection
4597
+ . clone ( )
4598
+ . without ( & base_selection. clone ( ) . unwrap ( ) )
4599
+ {
4600
+ Some ( sel) => sel,
4601
+ None => outside_selection. clone ( ) ,
4602
+ } ;
4603
+
4604
+ // check if selection is different from the last one
4605
+ if target_selection != current_selection {
4606
+ // save current selection so it can be restored using shrink_selection
4607
+ doc. view_data_mut ( view. id )
4608
+ . object_selections
4609
+ . entry ( EXPAND_KEY )
4610
+ . or_default ( )
4611
+ . push ( current_selection) ;
4612
+
4613
+ doc. set_selection_clear ( view. id , target_selection, false ) ;
4614
+ }
4615
+
4616
+ let parents = doc
4617
+ . view_data_mut ( view. id )
4618
+ . object_selections
4619
+ . entry ( PARENTS_KEY )
4620
+ . or_default ( ) ;
4621
+
4622
+ parents. push ( parents_selection. unwrap ( ) ) ;
4623
+ parents. push ( outside_selection) ;
4624
+
4625
+ doc. view_data_mut ( view. id )
4626
+ . object_selections
4627
+ . entry ( EXPAND_AROUND_BASE_KEY )
4628
+ . or_default ( )
4629
+ . push ( base_selection. unwrap ( ) ) ;
4630
+ }
4631
+ } ;
4632
+
4633
+ motion ( cx. editor ) ;
4634
+ cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4635
+ }
4636
+
4531
4637
fn select_sibling_impl < F > ( cx : & mut Context , sibling_fn : & ' static F )
4532
4638
where
4533
4639
F : Fn ( Node ) -> Option < Node > ,
0 commit comments