diff options
Diffstat (limited to 'alacritty_terminal/src/selection.rs')
-rw-r--r-- | alacritty_terminal/src/selection.rs | 567 |
1 files changed, 283 insertions, 284 deletions
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs index 1c747d99..95614502 100644 --- a/alacritty_terminal/src/selection.rs +++ b/alacritty_terminal/src/selection.rs @@ -18,13 +18,55 @@ //! finalized when the button is released. The selection should be cleared //! when text is added/removed/scrolled on the screen. The selection should //! also be cleared if the user clicks off of the selection. +use std::convert::TryFrom; +use std::mem; use std::ops::Range; -use crate::index::{Column, Line, Point, Side}; +use crate::index::{Column, Point, Side}; use crate::term::cell::Flags; use crate::term::{Search, Term}; -/// Describes a region of a 2-dimensional area +/// A Point and side within that point. +#[derive(Debug, Clone, PartialEq)] +pub struct Anchor { + point: Point<usize>, + side: Side, +} + +impl Anchor { + fn new(point: Point<usize>, side: Side) -> Anchor { + Anchor { point, side } + } +} + +/// Represents a range of selected cells. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct SelectionRange<L = usize> { + /// Start point, top left of the selection. + pub start: Point<L>, + /// End point, bottom right of the selection. + pub end: Point<L>, + /// Whether this selection is a block selection. + pub is_block: bool, +} + +impl<L> SelectionRange<L> { + pub fn new(start: Point<L>, end: Point<L>, is_block: bool) -> Self { + Self { start, end, is_block } + } + + pub fn contains(&self, col: Column, line: L) -> bool + where + L: PartialEq + PartialOrd, + { + self.start.line <= line + && self.end.line >= line + && (self.start.col <= col || (self.start.line != line && !self.is_block)) + && (self.end.col >= col || (self.end.line != line && !self.is_block)) + } +} + +/// Describes a region of a 2-dimensional area. /// /// Used to track a text selection. There are three supported modes, each with its own constructor: /// [`simple`], [`semantic`], and [`lines`]. The [`simple`] mode precisely tracks which cells are @@ -43,96 +85,61 @@ use crate::term::{Search, Term}; #[derive(Debug, Clone, PartialEq)] pub enum Selection { Simple { - /// The region representing start and end of cursor movement + /// The region representing start and end of cursor movement. region: Range<Anchor>, }, Block { - /// The region representing start and end of cursor movement + /// The region representing start and end of cursor movement. region: Range<Anchor>, }, Semantic { - /// The region representing start and end of cursor movement - region: Range<Point<isize>>, + /// The region representing start and end of cursor movement. + region: Range<Point<usize>>, }, Lines { - /// The region representing start and end of cursor movement - region: Range<Point<isize>>, + /// The region representing start and end of cursor movement. + region: Range<Point<usize>>, }, } -/// A Point and side within that point. -#[derive(Debug, Clone, PartialEq)] -pub struct Anchor { - point: Point<isize>, - side: Side, -} - -impl Anchor { - fn new(point: Point<isize>, side: Side) -> Anchor { - Anchor { point, side } - } -} - -/// A type that has 2-dimensional boundaries -pub trait Dimensions { - /// Get the size of the area - fn dimensions(&self) -> Point; -} - impl Selection { - pub fn rotate(&mut self, offset: isize) { - match *self { - Selection::Simple { ref mut region } | Selection::Block { ref mut region } => { - region.start.point.line += offset; - region.end.point.line += offset; - }, - Selection::Semantic { ref mut region } | Selection::Lines { ref mut region } => { - region.start.line += offset; - region.end.line += offset; - }, - } - } - pub fn simple(location: Point<usize>, side: Side) -> Selection { Selection::Simple { - region: Range { - start: Anchor::new(location.into(), side), - end: Anchor::new(location.into(), side), - }, + region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) }, } } pub fn block(location: Point<usize>, side: Side) -> Selection { Selection::Block { - region: Range { - start: Anchor::new(location.into(), side), - end: Anchor::new(location.into(), side), - }, + region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) }, } } pub fn semantic(point: Point<usize>) -> Selection { - Selection::Semantic { region: Range { start: point.into(), end: point.into() } } + Selection::Semantic { region: Range { start: point, end: point } } } pub fn lines(point: Point<usize>) -> Selection { - Selection::Lines { region: Range { start: point.into(), end: point.into() } } + Selection::Lines { region: Range { start: point, end: point } } } pub fn update(&mut self, location: Point<usize>, side: Side) { - // Always update the `end`; can normalize later during span generation. - match *self { - Selection::Simple { ref mut region } | Selection::Block { ref mut region } => { - region.end = Anchor::new(location.into(), side); - }, - Selection::Semantic { ref mut region } | Selection::Lines { ref mut region } => { - region.end = location.into(); - }, + let (_, end) = self.points_mut(); + *end = location; + + if let Some((_, end_side)) = self.sides_mut() { + *end_side = side; } } + pub fn rotate(&mut self, offset: isize) { + let (start, end) = self.points_mut(); + start.line = usize::try_from(start.line as isize + offset).unwrap_or(0); + end.line = usize::try_from(end.line as isize + offset).unwrap_or(0); + } + pub fn is_empty(&self) -> bool { - match *self { + match self { Selection::Simple { ref region } => { let (start, end) = if Selection::points_need_swap(region.start.point, region.end.point) { @@ -165,266 +172,260 @@ impl Selection { } } - pub fn to_span<T>(&self, term: &Term<T>) -> Option<Span> { - // Get both sides of the selection - let (mut start, mut end) = match *self { - Selection::Simple { ref region } | Selection::Block { ref region } => { - (region.start.point, region.end.point) - }, - Selection::Semantic { ref region } | Selection::Lines { ref region } => { - (region.start, region.end) - }, - }; + /// Convert selection to grid coordinates. + pub fn to_range<T>(&self, term: &Term<T>) -> Option<SelectionRange> { + let grid = term.grid(); + let num_cols = grid.num_cols(); - // Order the start/end - let needs_swap = Selection::points_need_swap(start, end); - if needs_swap { - std::mem::swap(&mut start, &mut end); - } + // Get selection boundaries + let points = self.points(); + let (start, end) = (*points.0, *points.1); - // Clamp to visible region in grid/normal - let num_cols = term.dimensions().col; - let num_lines = term.dimensions().line.0 as isize; - let (start, end) = Selection::grid_clamp(start, end, num_lines, num_cols)?; + // Get selection sides, falling back to `Side::Left` if it will not be used + let sides = self.sides().unwrap_or((&Side::Left, &Side::Left)); + let (start_side, end_side) = (*sides.0, *sides.1); - let span = match *self { - Selection::Simple { ref region } => { - let (start_side, end_side) = if needs_swap { - (region.end.side, region.start.side) - } else { - (region.start.side, region.end.side) - }; + // Order start above the end + let (start, end) = if Self::points_need_swap(start, end) { + (Anchor { point: end, side: end_side }, Anchor { point: start, side: start_side }) + } else { + (Anchor { point: start, side: start_side }, Anchor { point: end, side: end_side }) + }; - self.span_simple(term, start, end, start_side, end_side) - }, - Selection::Block { ref region } => { - let (start_side, end_side) = if needs_swap { - (region.end.side, region.start.side) - } else { - (region.start.side, region.end.side) - }; + // Clamp to inside the grid buffer + let (start, end) = Self::grid_clamp(start, end, self.is_block(), grid.len()).ok()?; - self.span_block(start, end, start_side, end_side) - }, - Selection::Semantic { .. } => Selection::span_semantic(term, start, end), - Selection::Lines { .. } => Selection::span_lines(term, start, end), + let range = match self { + Self::Simple { .. } => self.range_simple(start, end, num_cols), + Self::Block { .. } => self.range_block(start, end), + Self::Semantic { .. } => Self::range_semantic(term, start.point, end.point), + Self::Lines { .. } => Self::range_lines(term, start.point, end.point), }; - // Expand selection across double-width cells - span.map(|mut span| { - let grid = term.grid(); - - // Helper for checking if cell at `point` contains `flag` - let flag_at = |point: Point<usize>, flag: Flags| -> bool { - grid[point.line][point.col].flags.contains(flag) - }; - - // Include all double-width cells and placeholders at top left of selection - if span.start.col < num_cols { - // Expand from wide char spacer to wide char - if span.start.line + 1 != grid.len() || span.start.col.0 != 0 { - let prev = span.start.sub(num_cols.0, 1, true); - if flag_at(span.start, Flags::WIDE_CHAR_SPACER) - && flag_at(prev, Flags::WIDE_CHAR) - { - span.start = prev; - } - } + // Expand selection across fullwidth cells + range.map(|range| Self::range_expand_fullwidth(term, range)) + } - // Expand from wide char to wide char spacer for linewrapping - if span.start.line + 1 != grid.len() || span.start.col.0 != 0 { - let prev = span.start.sub(num_cols.0, 1, true); - if (prev.line + 1 != grid.len() || prev.col.0 != 0) - && flag_at(prev, Flags::WIDE_CHAR_SPACER) - && !flag_at(prev.sub(num_cols.0, 1, true), Flags::WIDE_CHAR) - { - span.start = prev; - } + /// Expand the start/end of the selection range to account for fullwidth glyphs. + fn range_expand_fullwidth<T>(term: &Term<T>, mut range: SelectionRange) -> SelectionRange { + let grid = term.grid(); + let num_cols = grid.num_cols(); + + // Helper for checking if cell at `point` contains `flag` + let flag_at = |point: Point<usize>, flag: Flags| -> bool { + grid[point.line][point.col].flags.contains(flag) + }; + + // Include all double-width cells and placeholders at top left of selection + if range.start.col < num_cols { + // Expand from wide char spacer to wide char + if range.start.line + 1 != grid.len() || range.start.col.0 != 0 { + let prev = range.start.sub(num_cols.0, 1, true); + if flag_at(range.start, Flags::WIDE_CHAR_SPACER) && flag_at(prev, Flags::WIDE_CHAR) + { + range.start = prev; } } - // Include all double-width cells and placeholders at bottom right of selection - if span.end.line != 0 || span.end.col < num_cols { - // Expand from wide char spacer for linewrapping to wide char - if (span.end.line + 1 != grid.len() || span.end.col.0 != 0) - && flag_at(span.end, Flags::WIDE_CHAR_SPACER) - && !flag_at(span.end.sub(num_cols.0, 1, true), Flags::WIDE_CHAR) + // Expand from wide char to wide char spacer for linewrapping + if range.start.line + 1 != grid.len() || range.start.col.0 != 0 { + let prev = range.start.sub(num_cols.0, 1, true); + if (prev.line + 1 != grid.len() || prev.col.0 != 0) + && flag_at(prev, Flags::WIDE_CHAR_SPACER) + && !flag_at(prev.sub(num_cols.0, 1, true), Flags::WIDE_CHAR) { - span.end = span.end.add(num_cols.0, 1, true); + range.start = prev; } + } + } - // Expand from wide char to wide char spacer - if flag_at(span.end, Flags::WIDE_CHAR) { - span.end = span.end.add(num_cols.0, 1, true); - } + // Include all double-width cells and placeholders at bottom right of selection + if range.end.line != 0 || range.end.col < num_cols { + // Expand from wide char spacer for linewrapping to wide char + if (range.end.line + 1 != grid.len() || range.end.col.0 != 0) + && flag_at(range.end, Flags::WIDE_CHAR_SPACER) + && !flag_at(range.end.sub(num_cols.0, 1, true), Flags::WIDE_CHAR) + { + range.end = range.end.add(num_cols.0, 1, true); + } + + // Expand from wide char to wide char spacer + if flag_at(range.end, Flags::WIDE_CHAR) { + range.end = range.end.add(num_cols.0, 1, true); } + } - span - }) + range } // Bring start and end points in the correct order - fn points_need_swap(start: Point<isize>, end: Point<isize>) -> bool { + fn points_need_swap(start: Point<usize>, end: Point<usize>) -> bool { start.line < end.line || start.line == end.line && start.col > end.col } - // Clamp selection inside the grid to prevent out of bounds errors + /// Clamp selection inside grid to prevent OOB. fn grid_clamp( - mut start: Point<isize>, - mut end: Point<isize>, - lines: isize, - cols: Column, - ) -> Option<(Point<isize>, Point<isize>)> { - if start.line >= lines { - // Don't show selection above visible region - if end.line >= lines { - return None; + mut start: Anchor, + end: Anchor, + is_block: bool, + lines: usize, + ) -> Result<(Anchor, Anchor), ()> { + // Clamp selection inside of grid to prevent OOB + if start.point.line >= lines { + // Remove selection if it is fully out of the grid + if end.point.line >= lines { + return Err(()); } - // Clamp selection above viewport to visible region - start.line = lines - 1; - start.col = Column(0); - } - - if end.line < 0 { - // Don't show selection below visible region - if start.line < 0 { - return None; + // Clamp to grid if it is still partially visible + if !is_block { + start.side = Side::Left; + start.point.col = Column(0); } - - // Clamp selection below viewport to visible region - end.line = 0; - end.col = cols - 1; + start.point.line = lines - 1; } - Some((start, end)) + Ok((start, end)) } - fn span_semantic<T>(term: &T, start: Point<isize>, end: Point<isize>) -> Option<Span> - where - T: Search + Dimensions, - { - let (start, end) = if start == end { - if let Some(end) = term.bracket_search(start.into()) { - (start.into(), end) - } else { - (term.semantic_search_left(start.into()), term.semantic_search_right(end.into())) + fn range_semantic<T>( + term: &Term<T>, + mut start: Point<usize>, + mut end: Point<usize>, + ) -> Option<SelectionRange> { + if start == end { + if let Some(matching) = term.bracket_search(start) { + if (matching.line == start.line && matching.col < start.col) + || (matching.line > start.line) + { + start = matching; + } else { + end = matching; + } + + return Some(SelectionRange { start, end, is_block: false }); } - } else { - (term.semantic_search_left(start.into()), term.semantic_search_right(end.into())) - }; + } + + start = term.semantic_search_left(start); + end = term.semantic_search_right(end); - Some(Span { start, end, is_block: false }) + Some(SelectionRange { start, end, is_block: false }) } - fn span_lines<T>(term: &T, start: Point<isize>, end: Point<isize>) -> Option<Span> - where - T: Search, - { - let start = term.line_search_left(start.into()); - let end = term.line_search_right(end.into()); + fn range_lines<T>( + term: &Term<T>, + mut start: Point<usize>, + mut end: Point<usize>, + ) -> Option<SelectionRange> { + start = term.line_search_left(start); + end = term.line_search_right(end); - Some(Span { start, end, is_block: false }) + Some(SelectionRange { start, end, is_block: false }) } - fn span_simple<T>( + fn range_simple( &self, - term: &T, - mut start: Point<isize>, - mut end: Point<isize>, - start_side: Side, - end_side: Side, - ) -> Option<Span> - where - T: Dimensions, - { + mut start: Anchor, + mut end: Anchor, + num_cols: Column, + ) -> Option<SelectionRange> { if self.is_empty() { return None; } // Remove last cell if selection ends to the left of a cell - if end_side == Side::Left && start != end { + if end.side == Side::Left && start.point != end.point { // Special case when selection ends to left of first cell - if end.col == Column(0) { - end.col = term.dimensions().col - 1; - end.line += 1; + if end.point.col == Column(0) { + end.point.col = num_cols; + end.point.line += 1; } else { - end.col -= 1; + end.point.col -= 1; } } // Remove first cell if selection starts at the right of a cell - if start_side == Side::Right && start != end { - start.col += 1; + if start.side == Side::Right && start.point != end.point { + start.point.col += 1; } - // Return the selection with all cells inclusive - Some(Span { start: start.into(), end: end.into(), is_block: false }) + Some(SelectionRange { start: start.point, end: end.point, is_block: false }) } - fn span_block( - &self, - mut start: Point<isize>, - mut end: Point<isize>, - mut start_side: Side, - mut end_side: Side, - ) -> Option<Span> { + fn range_block(&self, mut start: Anchor, mut end: Anchor) -> Option<SelectionRange> { if self.is_empty() { return None; } // Always go top-left -> bottom-right - if start.col > end.col { - std::mem::swap(&mut start_side, &mut end_side); - std::mem::swap(&mut start.col, &mut end.col); + if start.point.col > end.point.col { + mem::swap(&mut start.side, &mut end.side); + mem::swap(&mut start.point.col, &mut end.point.col); } // Remove last cell if selection ends to the left of a cell - if end_side == Side::Left && start != end && end.col.0 > 0 { - end.col -= 1; + if end.side == Side::Left && start.point != end.point && end.point.col.0 > 0 { + end.point.col -= 1; } // Remove first cell if selection starts at the right of a cell - if start_side == Side::Right && start != end { - start.col += 1; + if start.side == Side::Right && start.point != end.point { + start.point.col += 1; } - // Return the selection with all cells inclusive - Some(Span { start: start.into(), end: end.into(), is_block: true }) + Some(SelectionRange { start: start.point, end: end.point, is_block: true }) } -} -/// Represents a span of selected cells -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct Span { - /// Start point from bottom of buffer - pub start: Point<usize>, - /// End point towards top of buffer - pub end: Point<usize>, - /// Whether this selection is a block selection - pub is_block: bool, -} + fn points(&self) -> (&Point<usize>, &Point<usize>) { + match self { + Self::Simple { ref region } | Self::Block { ref region } => { + (®ion.start.point, ®ion.end.point) + }, + Self::Semantic { ref region } | Self::Lines { ref region } => { + (®ion.start, ®ion.end) + }, + } + } -pub struct SelectionRange { - start: Point, - end: Point, - is_block: bool, -} + fn points_mut(&mut self) -> (&mut Point<usize>, &mut Point<usize>) { + match self { + Self::Simple { ref mut region } | Self::Block { ref mut region } => { + (&mut region.start.point, &mut region.end.point) + }, + Self::Semantic { ref mut region } | Self::Lines { ref mut region } => { + (&mut region.start, &mut region.end) + }, + } + } -impl SelectionRange { - pub fn new(start: Point, end: Point, is_block: bool) -> Self { - Self { start, end, is_block } + fn sides(&self) -> Option<(&Side, &Side)> { + match self { + Self::Simple { ref region } | Self::Block { ref region } => { + Some((®ion.start.side, ®ion.end.side)) + }, + Self::Semantic { .. } | Self::Lines { .. } => None, + } } - pub fn contains(&self, col: Column, line: Line) -> bool { - self.start.line <= line - && self.end.line >= line - && (self.start.col <= col || (self.start.line != line && !self.is_block)) - && (self.end.col >= col || (self.end.line != line && !self.is_block)) + fn sides_mut(&mut self) -> Option<(&mut Side, &mut Side)> { + match self { + Self::Simple { ref mut region } | Self::Block { ref mut region } => { + Some((&mut region.start.side, &mut region.end.side)) + }, + Self::Semantic { .. } | Self::Lines { .. } => None, + } + } + + fn is_block(&self) -> bool { + match self { + Self::Block { .. } => true, + _ => false, + } } } -/// Tests for selection +/// Tests for selection. /// /// There are comments on all of the tests describing the selection. Pictograms /// are used to avoid ambiguity. Grid cells are represented by a [ ]. Only @@ -437,7 +438,7 @@ impl SelectionRange { mod test { use std::mem; - use super::{Selection, Span}; + use super::{Selection, SelectionRange}; use crate::clipboard::Clipboard; use crate::config::MockConfig; use crate::event::{Event, EventListener}; @@ -464,7 +465,7 @@ mod test { Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock) } - /// Test case of single cell selection + /// Test case of single cell selection. /// /// 1. [ ] /// 2. [B ] @@ -475,14 +476,14 @@ mod test { let mut selection = Selection::simple(location, Side::Left); selection.update(location, Side::Right); - assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span { + assert_eq!(selection.to_range(&term(1, 1)).unwrap(), SelectionRange { start: location, end: location, is_block: false }); } - /// Test case of single cell selection + /// Test case of single cell selection. /// /// 1. [ ] /// 2. [ B] @@ -493,14 +494,14 @@ mod test { let mut selection = Selection::simple(location, Side::Right); selection.update(location, Side::Left); - assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span { + assert_eq!(selection.to_range(&term(1, 1)).unwrap(), SelectionRange { start: location, end: location, is_block: false }); } - /// Test adjacent cell selection from left to right + /// Test adjacent cell selection from left to right. /// /// 1. [ ][ ] /// 2. [ B][ ] @@ -510,10 +511,10 @@ mod test { let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); selection.update(Point::new(0, Column(1)), Side::Left); - assert_eq!(selection.to_span(&term(2, 1)), None); + assert_eq!(selection.to_range(&term(2, 1)), None); } - /// Test adjacent cell selection from right to left + /// Test adjacent cell selection from right to left. /// /// 1. [ ][ ] /// 2. [ ][B ] @@ -523,11 +524,10 @@ mod test { let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left); selection.update(Point::new(0, Column(0)), Side::Right); - assert_eq!(selection.to_span(&term(2, 1)), None); + assert_eq!(selection.to_range(&term(2, 1)), None); } - /// Test selection across adjacent lines - /// + /// Test selection across adjacent lines. /// /// 1. [ ][ ][ ][ ][ ] /// [ ][ ][ ][ ][ ] @@ -540,15 +540,14 @@ mod test { let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right); selection.update(Point::new(0, Column(1)), Side::Right); - assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span { + assert_eq!(selection.to_range(&term(5, 2)).unwrap(), SelectionRange { start: Point::new(1, Column(2)), end: Point::new(0, Column(1)), is_block: false, }); } - /// Test selection across adjacent lines - /// + /// Test selection across adjacent lines. /// /// 1. [ ][ ][ ][ ][ ] /// [ ][ ][ ][ ][ ] @@ -564,7 +563,7 @@ mod test { selection.update(Point::new(1, Column(1)), Side::Right); selection.update(Point::new(1, Column(0)), Side::Right); - assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span { + assert_eq!(selection.to_range(&term(5, 2)).unwrap(), SelectionRange { start: Point::new(1, Column(1)), end: Point::new(0, Column(1)), is_block: false, @@ -573,52 +572,52 @@ mod test { #[test] fn line_selection() { - let mut selection = Selection::lines(Point::new(0, Column(0))); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); + let mut selection = Selection::lines(Point::new(0, Column(1))); + selection.update(Point::new(5, Column(1)), Side::Right); + selection.rotate(7); - assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span { - start: Point::new(2, Column(0)), - end: Point::new(0, Column(4)), + assert_eq!(selection.to_range(&term(5, 10)).unwrap(), SelectionRange { + start: Point::new(9, Column(0)), + end: Point::new(7, Column(4)), is_block: false, }); } #[test] fn semantic_selection() { - let mut selection = Selection::semantic(Point::new(0, Column(0))); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); + let mut selection = Selection::semantic(Point::new(0, Column(3))); + selection.update(Point::new(5, Column(1)), Side::Right); + selection.rotate(7); - assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span { - start: Point::new(2, Column(3)), - end: Point::new(0, Column(4)), + assert_eq!(selection.to_range(&term(5, 10)).unwrap(), SelectionRange { + start: Point::new(9, Column(0)), + end: Point::new(7, Column(3)), is_block: false, }); } #[test] fn simple_selection() { - let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); + let mut selection = Selection::simple(Point::new(0, Column(3)), Side::Right); + selection.update(Point::new(5, Column(1)), Side::Right); + selection.rotate(7); - assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span { - start: Point::new(2, Column(4)), - end: Point::new(0, Column(4)), + assert_eq!(selection.to_range(&term(5, 10)).unwrap(), SelectionRange { + start: Point::new(9, Column(0)), + end: Point::new(7, Column(3)), is_block: false, }); } #[test] fn block_selection() { - let mut selection = Selection::block(Point::new(0, Column(0)), Side::Right); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); + let mut selection = Selection::block(Point::new(0, Column(3)), Side::Right); + selection.update(Point::new(5, Column(1)), Side::Right); + selection.rotate(7); - assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span { - start: Point::new(2, Column(4)), - end: Point::new(0, Column(4)), + assert_eq!(selection.to_range(&term(5, 10)).unwrap(), SelectionRange { + start: Point::new(9, Column(2)), + end: Point::new(7, Column(3)), is_block: true }); } @@ -636,7 +635,7 @@ mod test { let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left); selection.update(Point::new(0, Column(8)), Side::Right); - assert_eq!(selection.to_span(&term).unwrap(), Span { + assert_eq!(selection.to_range(&term).unwrap(), SelectionRange { start: Point::new(0, Column(0)), end: Point::new(0, Column(9)), is_block: false, |