mirror of https://github.com/helix-editor/helix
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>pull/9968/head
parent
6fea7876a4
commit
761df60077
|
@ -57,6 +57,7 @@ use crate::{
|
||||||
|
|
||||||
use crate::job::{self, Jobs};
|
use crate::job::{self, Jobs};
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt,
|
fmt,
|
||||||
future::Future,
|
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, "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_below, "Select current line, if already selected, extend to next line",
|
||||||
extend_line_above, "Select current line, if already selected, extend to previous 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",
|
extend_to_line_bounds, "Extend selection to line bounds",
|
||||||
shrink_to_line_bounds, "Shrink selection to line bounds",
|
shrink_to_line_bounds, "Shrink selection to line bounds",
|
||||||
delete_selection, "Delete selection",
|
delete_selection, "Delete selection",
|
||||||
|
@ -2435,7 +2438,6 @@ fn extend_line_below(cx: &mut Context) {
|
||||||
fn extend_line_above(cx: &mut Context) {
|
fn extend_line_above(cx: &mut Context) {
|
||||||
extend_line_impl(cx, Extend::Above);
|
extend_line_impl(cx, Extend::Above);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_line_impl(cx: &mut Context, extend: Extend) {
|
fn extend_line_impl(cx: &mut Context, extend: Extend) {
|
||||||
let count = cx.count();
|
let count = cx.count();
|
||||||
let (view, doc) = current!(cx.editor);
|
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);
|
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) {
|
fn extend_to_line_bounds(cx: &mut Context) {
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
|
|
Loading…
Reference in New Issue