@@ -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" ,
@@ -4356,6 +4357,8 @@ fn rotate_selection_contents_backward(cx: &mut Context) {
4356
4357
// tree sitter node selection
4357
4358
4358
4359
const EXPAND_KEY : & str = "expand" ;
4360
+ const EXPAND_AROUND_BASE_KEY : & str = "expand_around_base" ;
4361
+ const PARENTS_KEY : & str = "parents" ;
4359
4362
4360
4363
fn expand_selection ( cx : & mut Context ) {
4361
4364
let motion = |editor : & mut Editor | {
@@ -4400,6 +4403,33 @@ fn shrink_selection(cx: &mut Context) {
4400
4403
if let Some ( prev_selection) = prev_expansions. pop ( ) {
4401
4404
// allow shrinking the selection only if current selection contains the previous object selection
4402
4405
doc. set_selection_clear ( view. id , prev_selection, false ) ;
4406
+
4407
+ // Do a corresponding pop of the parents from `expand_selection_around`
4408
+ doc. view_data_mut ( view. id )
4409
+ . object_selections
4410
+ . entry ( PARENTS_KEY )
4411
+ . and_modify ( |parents| {
4412
+ parents. pop ( ) ;
4413
+ } ) ;
4414
+
4415
+ // need to do this again because borrowing
4416
+ let prev_expansions = doc
4417
+ . view_data_mut ( view. id )
4418
+ . object_selections
4419
+ . entry ( EXPAND_KEY )
4420
+ . or_default ( ) ;
4421
+
4422
+ // if we've emptied out the previous expansions, then clear out the
4423
+ // base history as well so it doesn't get used again erroneously
4424
+ if prev_expansions. is_empty ( ) {
4425
+ doc. view_data_mut ( view. id )
4426
+ . object_selections
4427
+ . entry ( EXPAND_AROUND_BASE_KEY )
4428
+ . and_modify ( |base| {
4429
+ base. clear ( ) ;
4430
+ } ) ;
4431
+ }
4432
+
4403
4433
return ;
4404
4434
}
4405
4435
@@ -4415,6 +4445,82 @@ fn shrink_selection(cx: &mut Context) {
4415
4445
cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4416
4446
}
4417
4447
4448
+ fn expand_selection_around ( cx : & mut Context ) {
4449
+ let motion = |editor : & mut Editor | {
4450
+ let ( view, doc) = current ! ( editor) ;
4451
+
4452
+ if doc. syntax ( ) . is_some ( ) {
4453
+ // [NOTE] we do this pop and push dance because if we don't take
4454
+ // ownership of the objects, then we require multiple
4455
+ // mutable references to the view's object selections
4456
+ let mut parents_selection = doc
4457
+ . view_data_mut ( view. id )
4458
+ . object_selections
4459
+ . entry ( PARENTS_KEY )
4460
+ . or_default ( )
4461
+ . pop ( ) ;
4462
+
4463
+ let mut base_selection = doc
4464
+ . view_data_mut ( view. id )
4465
+ . object_selections
4466
+ . entry ( EXPAND_AROUND_BASE_KEY )
4467
+ . or_default ( )
4468
+ . pop ( ) ;
4469
+
4470
+ let current_selection = doc. selection ( view. id ) . clone ( ) ;
4471
+
4472
+ if parents_selection. is_none ( ) || base_selection. is_none ( ) {
4473
+ parents_selection = Some ( current_selection. clone ( ) ) ;
4474
+ base_selection = Some ( current_selection. clone ( ) ) ;
4475
+ }
4476
+
4477
+ let text = doc. text ( ) . slice ( ..) ;
4478
+ let syntax = doc. syntax ( ) . unwrap ( ) ;
4479
+
4480
+ let outside_selection =
4481
+ object:: expand_selection ( syntax, text, parents_selection. clone ( ) . unwrap ( ) ) ;
4482
+
4483
+ let target_selection = match outside_selection
4484
+ . clone ( )
4485
+ . without ( & base_selection. clone ( ) . unwrap ( ) )
4486
+ {
4487
+ Some ( sel) => sel,
4488
+ None => outside_selection. clone ( ) ,
4489
+ } ;
4490
+
4491
+ // check if selection is different from the last one
4492
+ if target_selection != current_selection {
4493
+ // save current selection so it can be restored using shrink_selection
4494
+ doc. view_data_mut ( view. id )
4495
+ . object_selections
4496
+ . entry ( EXPAND_KEY )
4497
+ . or_default ( )
4498
+ . push ( current_selection) ;
4499
+
4500
+ doc. set_selection_clear ( view. id , target_selection, false ) ;
4501
+ }
4502
+
4503
+ let parents = doc
4504
+ . view_data_mut ( view. id )
4505
+ . object_selections
4506
+ . entry ( PARENTS_KEY )
4507
+ . or_default ( ) ;
4508
+
4509
+ parents. push ( parents_selection. unwrap ( ) ) ;
4510
+ parents. push ( outside_selection) ;
4511
+
4512
+ doc. view_data_mut ( view. id )
4513
+ . object_selections
4514
+ . entry ( EXPAND_AROUND_BASE_KEY )
4515
+ . or_default ( )
4516
+ . push ( base_selection. unwrap ( ) ) ;
4517
+ }
4518
+ } ;
4519
+
4520
+ motion ( cx. editor ) ;
4521
+ cx. editor . last_motion = Some ( Motion ( Box :: new ( motion) ) ) ;
4522
+ }
4523
+
4418
4524
fn select_sibling_impl < F > ( cx : & mut Context , sibling_fn : & ' static F )
4419
4525
where
4420
4526
F : Fn ( Node ) -> Option < Node > ,
0 commit comments