@@ -396,6 +396,7 @@ impl MappableCommand {
396
396
rotate_selection_contents_backward, "Rotate selections contents backward" ,
397
397
expand_selection, "Expand selection to parent syntax node" ,
398
398
shrink_selection, "Shrink selection to previously expanded syntax node" ,
399
+ expand_selection_around, "Expand selection to parent syntax node, but exclude the selection you started with" ,
399
400
select_next_sibling, "Select next sibling in syntax tree" ,
400
401
select_prev_sibling, "Select previous sibling in syntax tree" ,
401
402
jump_forward, "Jump forward on jumplist" ,
@@ -4372,6 +4373,8 @@ fn rotate_selection_contents_backward(cx: &mut Context) {
4372
4373
// tree sitter node selection
4373
4374
4374
4375
const EXPAND_KEY : & str = "expand" ;
4376
+ const EXPAND_AROUND_BASE_KEY : & str = "expand_around_base" ;
4377
+ const PARENTS_KEY : & str = "parents" ;
4375
4378
4376
4379
fn expand_selection ( cx : & mut Context ) {
4377
4380
let motion = |editor : & mut Editor | {
@@ -4416,6 +4419,33 @@ fn shrink_selection(cx: &mut Context) {
4416
4419
if let Some ( prev_selection) = prev_expansions. pop ( ) {
4417
4420
// allow shrinking the selection only if current selection contains the previous object selection
4418
4421
doc. set_selection_clear ( view. id , prev_selection, false ) ;
4422
+
4423
+ // Do a corresponding pop of the parents from `expand_selection_around`
4424
+ doc. view_data_mut ( view. id )
4425
+ . object_selections
4426
+ . entry ( PARENTS_KEY )
4427
+ . and_modify ( |parents| {
4428
+ parents. pop ( ) ;
4429
+ } ) ;
4430
+
4431
+ // need to do this again because borrowing
4432
+ let prev_expansions = doc
4433
+ . view_data_mut ( view. id )
4434
+ . object_selections
4435
+ . entry ( EXPAND_KEY )
4436
+ . or_default ( ) ;
4437
+
4438
+ // if we've emptied out the previous expansions, then clear out the
4439
+ // base history as well so it doesn't get used again erroneously
4440
+ if prev_expansions. is_empty ( ) {
4441
+ doc. view_data_mut ( view. id )
4442
+ . object_selections
4443
+ . entry ( EXPAND_AROUND_BASE_KEY )
4444
+ . and_modify ( |base| {
4445
+ base. clear ( ) ;
4446
+ } ) ;
4447
+ }
4448
+
4419
4449
return ;
4420
4450
}
4421
4451
@@ -4431,6 +4461,82 @@ fn shrink_selection(cx: &mut Context) {
4431
4461
cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4432
4462
}
4433
4463
4464
+ fn expand_selection_around ( cx : & mut Context ) {
4465
+ let motion = |editor : & mut Editor | {
4466
+ let ( view, doc) = current ! ( editor) ;
4467
+
4468
+ if doc. syntax ( ) . is_some ( ) {
4469
+ // [NOTE] we do this pop and push dance because if we don't take
4470
+ // ownership of the objects, then we require multiple
4471
+ // mutable references to the view's object selections
4472
+ let mut parents_selection = doc
4473
+ . view_data_mut ( view. id )
4474
+ . object_selections
4475
+ . entry ( PARENTS_KEY )
4476
+ . or_default ( )
4477
+ . pop ( ) ;
4478
+
4479
+ let mut base_selection = doc
4480
+ . view_data_mut ( view. id )
4481
+ . object_selections
4482
+ . entry ( EXPAND_AROUND_BASE_KEY )
4483
+ . or_default ( )
4484
+ . pop ( ) ;
4485
+
4486
+ let current_selection = doc. selection ( view. id ) . clone ( ) ;
4487
+
4488
+ if parents_selection. is_none ( ) || base_selection. is_none ( ) {
4489
+ parents_selection = Some ( current_selection. clone ( ) ) ;
4490
+ base_selection = Some ( current_selection. clone ( ) ) ;
4491
+ }
4492
+
4493
+ let text = doc. text ( ) . slice ( ..) ;
4494
+ let syntax = doc. syntax ( ) . unwrap ( ) ;
4495
+
4496
+ let outside_selection =
4497
+ object:: expand_selection ( syntax, text, parents_selection. clone ( ) . unwrap ( ) ) ;
4498
+
4499
+ let target_selection = match outside_selection
4500
+ . clone ( )
4501
+ . without ( & base_selection. clone ( ) . unwrap ( ) )
4502
+ {
4503
+ Some ( sel) => sel,
4504
+ None => outside_selection. clone ( ) ,
4505
+ } ;
4506
+
4507
+ // check if selection is different from the last one
4508
+ if target_selection != current_selection {
4509
+ // save current selection so it can be restored using shrink_selection
4510
+ doc. view_data_mut ( view. id )
4511
+ . object_selections
4512
+ . entry ( EXPAND_KEY )
4513
+ . or_default ( )
4514
+ . push ( current_selection) ;
4515
+
4516
+ doc. set_selection_clear ( view. id , target_selection, false ) ;
4517
+ }
4518
+
4519
+ let parents = doc
4520
+ . view_data_mut ( view. id )
4521
+ . object_selections
4522
+ . entry ( PARENTS_KEY )
4523
+ . or_default ( ) ;
4524
+
4525
+ parents. push ( parents_selection. unwrap ( ) ) ;
4526
+ parents. push ( outside_selection) ;
4527
+
4528
+ doc. view_data_mut ( view. id )
4529
+ . object_selections
4530
+ . entry ( EXPAND_AROUND_BASE_KEY )
4531
+ . or_default ( )
4532
+ . push ( base_selection. unwrap ( ) ) ;
4533
+ }
4534
+ } ;
4535
+
4536
+ motion ( cx. editor ) ;
4537
+ cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4538
+ }
4539
+
4434
4540
fn select_sibling_impl < F > ( cx : & mut Context , sibling_fn : & ' static F )
4435
4541
where
4436
4542
F : Fn ( Node ) -> Option < Node > ,
0 commit comments