diff options
author | Emi | 2024-03-17 22:06:24 +0000 |
---|---|---|
committer | GitHub | 2024-03-17 22:06:24 +0000 |
commit | 761df60077bf33cb1077eb4cb019885ea3dd6ae7 (patch) | |
tree | 5341b0623b3ad45317d243b1a7840c3d92da9b90 /helix-term/src | |
parent | 6fea7876a47df8627a4b40361a6fc0f692c6601f (diff) |
Keybind for Extend/shrink selection up and down (#9080)
* implement another selection modifying command
* Selection feels more ergonomic in case of swapping the direction. This also fixes a problem when starting at an empty line.
* rename select_line_up/down to select_line_above/below
* apply clippy suggestion of using cmp instead of if-chain
* revert `Extent` implementing `Clone/Copy`
* move select_line functions below extend_line implementations
* implement help add function, which saturates at the number of text lines
---------
Co-authored-by: Emi <emanuel.boehm@gmail.com>
Diffstat (limited to 'helix-term/src')
-rw-r--r-- | helix-term/src/commands.rs | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 4ac2496e..133f2d54 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -57,6 +57,7 @@ use crate::{ use crate::job::{self, Jobs}; use std::{ + cmp::Ordering, collections::{HashMap, HashSet}, fmt, future::Future, @@ -300,6 +301,8 @@ impl MappableCommand { extend_line, "Select current line, if already selected, extend to another line based on the anchor", extend_line_below, "Select current line, if already selected, extend to next line", extend_line_above, "Select current line, if already selected, extend to previous line", + select_line_above, "Select current line, if already selected, extend or shrink line above based on the anchor", + select_line_below, "Select current line, if already selected, extend or shrink line below based on the anchor", extend_to_line_bounds, "Extend selection to line bounds", shrink_to_line_bounds, "Shrink selection to line bounds", delete_selection, "Delete selection", @@ -2435,7 +2438,6 @@ fn extend_line_below(cx: &mut Context) { fn extend_line_above(cx: &mut Context) { extend_line_impl(cx, Extend::Above); } - fn extend_line_impl(cx: &mut Context, extend: Extend) { let count = cx.count(); let (view, doc) = current!(cx.editor); @@ -2474,6 +2476,59 @@ fn extend_line_impl(cx: &mut Context, extend: Extend) { doc.set_selection(view.id, selection); } +fn select_line_below(cx: &mut Context) { + select_line_impl(cx, Extend::Below); +} +fn select_line_above(cx: &mut Context) { + select_line_impl(cx, Extend::Above); +} +fn select_line_impl(cx: &mut Context, extend: Extend) { + let mut count = cx.count(); + let (view, doc) = current!(cx.editor); + let text = doc.text(); + let saturating_add = |a: usize, b: usize| (a + b).min(text.len_lines()); + let selection = doc.selection(view.id).clone().transform(|range| { + let (start_line, end_line) = range.line_range(text.slice(..)); + let start = text.line_to_char(start_line); + let end = text.line_to_char(saturating_add(end_line, 1)); + let direction = range.direction(); + + // Extending to line bounds is counted as one step + if range.from() != start || range.to() != end { + count = count.saturating_sub(1) + } + let (anchor_line, head_line) = match (&extend, direction) { + (Extend::Above, Direction::Forward) => (start_line, end_line.saturating_sub(count)), + (Extend::Above, Direction::Backward) => (end_line, start_line.saturating_sub(count)), + (Extend::Below, Direction::Forward) => (start_line, saturating_add(end_line, count)), + (Extend::Below, Direction::Backward) => (end_line, saturating_add(start_line, count)), + }; + let (anchor, head) = match anchor_line.cmp(&head_line) { + Ordering::Less => ( + text.line_to_char(anchor_line), + text.line_to_char(saturating_add(head_line, 1)), + ), + Ordering::Equal => match extend { + Extend::Above => ( + text.line_to_char(saturating_add(anchor_line, 1)), + text.line_to_char(head_line), + ), + Extend::Below => ( + text.line_to_char(head_line), + text.line_to_char(saturating_add(anchor_line, 1)), + ), + }, + + Ordering::Greater => ( + text.line_to_char(saturating_add(anchor_line, 1)), + text.line_to_char(head_line), + ), + }; + Range::new(anchor, head) + }); + + doc.set_selection(view.id, selection); +} fn extend_to_line_bounds(cx: &mut Context) { let (view, doc) = current!(cx.editor); |