Skip to content

Commit f909a66

Browse files
committed
feat: manipulating one of the selections optional: assign # for togging primary manipulation optional: allow for styling primary only selection
1 parent 1b90a69 commit f909a66

File tree

7 files changed

+83
-18
lines changed

7 files changed

+83
-18
lines changed

helix-core/src/selection.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ impl From<Range> for helix_stdx::Range {
417417
pub struct Selection {
418418
ranges: SmallVec<[Range; 1]>,
419419
primary_index: usize,
420+
edit_only_primary: bool,
420421
}
421422

422423
#[allow(clippy::len_without_is_empty)] // a Selection is never empty
@@ -440,10 +441,7 @@ impl Selection {
440441
if self.ranges.len() == 1 {
441442
self
442443
} else {
443-
Self {
444-
ranges: smallvec![self.ranges[self.primary_index]],
445-
primary_index: 0,
446-
}
444+
Self::new(smallvec![self.ranges[self.primary_index]], 0)
447445
}
448446
}
449447

@@ -536,6 +534,14 @@ impl Selection {
536534
self.primary_index = idx;
537535
}
538536

537+
pub fn edit_only_primary(&self) -> bool {
538+
self.edit_only_primary
539+
}
540+
541+
pub fn set_edit_only_primary(&mut self, val: bool) {
542+
self.edit_only_primary = val;
543+
}
544+
539545
#[must_use]
540546
/// Constructs a selection holding a single range.
541547
pub fn single(anchor: usize, head: usize) -> Self {
@@ -546,6 +552,7 @@ impl Selection {
546552
old_visual_position: None
547553
}],
548554
primary_index: 0,
555+
edit_only_primary: false,
549556
}
550557
}
551558

@@ -627,6 +634,9 @@ impl Selection {
627634
let selection = Self {
628635
ranges,
629636
primary_index,
637+
638+
// This is not a parameter since everywhere a Selection is constructed, editing only the primary is not desired.
639+
edit_only_primary: false,
630640
};
631641

632642
selection.normalize()
@@ -637,9 +647,14 @@ impl Selection {
637647
where
638648
F: FnMut(Range) -> Range,
639649
{
640-
for range in self.ranges.iter_mut() {
641-
*range = f(*range)
650+
if self.edit_only_primary {
651+
self.ranges[self.primary_index] = f(self.ranges[self.primary_index])
652+
} else {
653+
for range in self.ranges.iter_mut() {
654+
*range = f(*range)
655+
}
642656
}
657+
643658
self.normalize()
644659
}
645660

@@ -728,10 +743,7 @@ impl FromIterator<Range> for Selection {
728743

729744
impl From<Range> for Selection {
730745
fn from(range: Range) -> Self {
731-
Self {
732-
ranges: smallvec![range],
733-
primary_index: 0,
734-
}
746+
Self::new(smallvec![range], 0)
735747
}
736748
}
737749

helix-term/src/commands.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ impl MappableCommand {
525525
rotate_selections_backward, "Rotate selections backward",
526526
rotate_selection_contents_forward, "Rotate selection contents forward",
527527
rotate_selection_contents_backward, "Rotate selections contents backward",
528+
toggle_move_primary_selection_only, "Toggle movement only of primary selection",
528529
reverse_selection_contents, "Reverse selections contents",
529530
expand_selection, "Expand selection to parent syntax node",
530531
shrink_selection, "Shrink selection to previously expanded syntax node",
@@ -5457,6 +5458,14 @@ fn reverse_selection_contents(cx: &mut Context) {
54575458
reorder_selection_contents(cx, ReorderStrategy::Reverse)
54585459
}
54595460

5461+
fn toggle_move_primary_selection_only(cx: &mut Context) {
5462+
let (view, doc) = current!(cx.editor);
5463+
let mut selection = doc.selection(view.id).clone();
5464+
5465+
selection.set_edit_only_primary(!selection.edit_only_primary());
5466+
doc.set_selection(view.id, selection);
5467+
}
5468+
54605469
// tree sitter node selection
54615470

54625471
fn expand_selection(cx: &mut Context) {

helix-term/src/keymap/default.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
182182
"A-(" => rotate_selection_contents_backward,
183183
"A-)" => rotate_selection_contents_forward,
184184

185+
"#" => toggle_move_primary_selection_only,
186+
185187
"A-:" => ensure_selections_forward,
186188

187189
"esc" => normal_mode,

helix-term/src/ui/statusline.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,33 @@ where
333333
{
334334
let selection = context.doc.selection(context.view.id);
335335
let count = selection.len();
336-
write(
337-
context,
338-
if count == 1 {
339-
" 1 sel ".into()
340-
} else {
341-
format!(" {}/{count} sels ", selection.primary_index() + 1).into()
342-
},
343-
);
336+
337+
match (count, selection.edit_only_primary()) {
338+
(1, _) => write(context, " 1 sel ".into()),
339+
(_, true) => {
340+
write(context, " ".into());
341+
342+
write(
343+
context,
344+
Span::styled(
345+
"primary",
346+
context
347+
.editor
348+
.theme
349+
.get("ui.statusline.selections.primary-only"),
350+
),
351+
);
352+
353+
write(
354+
context,
355+
format!(" {}/{count} ", selection.primary_index() + 1).into(),
356+
)
357+
}
358+
(_, false) => write(
359+
context,
360+
format!(" {}/{count} sels ", selection.primary_index() + 1).into(),
361+
),
362+
}
344363
}
345364

346365
fn render_primary_selection_length<'a, F>(context: &mut RenderContext<'a>, write: F)

helix-term/tests/test/helpers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub struct TestCase {
6565
pub out_text: String,
6666
pub out_selection: Selection,
6767

68+
// remove when this is used in any test.
69+
#[allow(dead_code)]
6870
pub line_feed_handling: LineFeedHandling,
6971
}
7072

helix-term/tests/test/movement.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,26 @@ async fn test_surround_delete() -> anyhow::Result<()> {
670670
Ok(())
671671
}
672672

673+
#[tokio::test(flavor = "multi_thread")]
674+
async fn test_movement_of_primary_only() -> anyhow::Result<()> {
675+
test((
676+
indoc! {"\
677+
#[|]#Test1 Test2 Test3
678+
Test1 Test2 Test3
679+
Test1 Test2 Test3
680+
"},
681+
"wlCCt (#wwcTest4<esc>#b;",
682+
indoc! {"\
683+
Test1 #(T|)#est4 Test3
684+
Test1 Test2 #[T|]#est4
685+
Test1 #(T|)#est4 Test3
686+
"},
687+
))
688+
.await?;
689+
690+
Ok(())
691+
}
692+
673693
#[tokio::test(flavor = "multi_thread")]
674694
async fn tree_sitter_motions_work_across_injections() -> anyhow::Result<()> {
675695
test_with_config(

theme.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ tabstop = { modifiers = ["italic"], bg = "bossanova" }
5050
"ui.linenr.selected" = { fg = "lilac" }
5151
"ui.statusline" = { fg = "lilac", bg = "revolver" }
5252
"ui.statusline.inactive" = { fg = "lavender", bg = "revolver" }
53+
"ui.statusline.selections.primary-only" = { fg = "apricot" }
5354
"ui.popup" = { bg = "revolver" }
5455
"ui.window" = { fg = "bossanova" }
5556
"ui.help" = { bg = "#7958DC", fg = "#171452" }

0 commit comments

Comments
 (0)