diff --git a/helix-core/src/commands.rs b/helix-core/src/commands.rs index 8dc817da..6c09c3ad 100644 --- a/helix-core/src/commands.rs +++ b/helix-core/src/commands.rs @@ -62,8 +62,8 @@ pub fn append_mode(state: &mut State, _count: usize) { // TODO: I, A, o and O can share a lot of the primitives. +// calculate line numbers for each selection range fn selection_lines(state: &State) -> Vec { - // calculate line numbers for each selection range let mut lines = state .selection .ranges() diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 2ae2d7c8..80bb6a0c 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -100,8 +100,8 @@ impl Range { #[derive(Debug, Clone)] pub struct Selection { // TODO: decide how many ranges to inline SmallVec<[Range; 1]> - pub(crate) ranges: SmallVec<[Range; 1]>, - pub(crate) primary_index: usize, + ranges: SmallVec<[Range; 1]>, + primary_index: usize, } impl Selection { @@ -205,6 +205,14 @@ impl Selection { } } + // fast path for a single selection (cursor) + if ranges.len() == 1 { + return Selection { + ranges, + primary_index: 0, + }; + } + // TODO: only normalize if needed (any ranges out of order) normalize(ranges, primary_index) } diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs index 511df8fe..be2b3317 100644 --- a/helix-core/src/state.rs +++ b/helix-core/src/state.rs @@ -122,7 +122,7 @@ impl State { // TODO: move all selections according to normal cursor move semantics by collapsing it // into cursors and moving them vertically - let ranges = self.selection.ranges.iter().map(|range| { + self.selection.transform(|range| { // let pos = if !range.is_empty() { // // if selection already exists, bump it to the start or end of current select first // if dir == Direction::Backward { @@ -134,10 +134,7 @@ impl State { let pos = self.move_pos(range.head, dir, granularity, count); // }; SelectionRange::new(pos, pos) - }); - - Selection::new(ranges.collect(), self.selection.primary_index) - // TODO: update selection in state via transaction + }) } pub fn extend_selection( @@ -146,13 +143,10 @@ impl State { granularity: Granularity, count: usize, ) -> Selection { - let ranges = self.selection.ranges.iter().map(|range| { + self.selection.transform(|range| { let pos = self.move_pos(range.head, dir, granularity, count); SelectionRange::new(range.anchor, pos) - }); - - Selection::new(ranges.collect(), self.selection.primary_index) - // TODO: update selection in state via transaction + }) } } @@ -203,7 +197,6 @@ mod test { fn test_coords_at_pos() { let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ"); assert_eq!(coords_at_pos(&text.slice(..), 0), (0, 0)); - // TODO: what is the coordinate of newline? assert_eq!(coords_at_pos(&text.slice(..), 5), (0, 5)); // position on \n assert_eq!(coords_at_pos(&text.slice(..), 6), (1, 0)); // position on w assert_eq!(coords_at_pos(&text.slice(..), 7), (1, 1)); // position on o diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index 4ecc575a..b4cc83c6 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -3,13 +3,6 @@ use crate::{Rope, Selection, SelectionRange, State, Tendril}; /// (from, to, replacement) pub type Change = (usize, usize, Option); -// TODO: divided into three different operations, I sort of like having just -// Splice { extent, Option, distance } better. -// insert: Splice { extent: 0, text: Some("a"), distance: 2 } -// delete: Splice { extent: 2, text: None, distance: 2 } -// replace: Splice { extent: 2, text: Some("abc"), distance: 2 } -// unchanged?: Splice { extent: 0, text: None, distance: 2 } -// harder to compose though. #[derive(Debug, Clone, PartialEq, Eq)] enum Operation { /// Move cursor by n characters. @@ -364,7 +357,6 @@ impl Transaction { let mut last = 0; for (from, to, tendril) in changes { - // TODO: need to fill the in-between ranges too // Retain from last "to" to current "from" acc.push(Operation::Retain(from - last)); match tendril { @@ -383,7 +375,7 @@ impl Transaction { where F: Fn(&SelectionRange) -> Change, { - Self::change(state, state.selection.ranges.iter().map(f)) + Self::change(state, state.selection.ranges().iter().map(f)) } /// Insert text at each selection head.