diff options
author | Joe Wilm <joe@jwilm.com> | 2018-03-06 20:57:40 -0800 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2018-06-02 09:34:28 -0700 |
commit | 8018dee1812ab88793c0f18e13335fa77c068000 (patch) | |
tree | 3d9a4a5435a1bdfe260ea78299f49d8934535b6b /src/selection.rs | |
parent | 8ef062efd94975885776e61b6df936088b018da0 (diff) | |
download | r-alacritty-8018dee1812ab88793c0f18e13335fa77c068000.tar.gz r-alacritty-8018dee1812ab88793c0f18e13335fa77c068000.tar.bz2 r-alacritty-8018dee1812ab88793c0f18e13335fa77c068000.zip |
Support selections with scrolling buffer
Selections now *mostly* work. They move as the buffer scrolls, copying
works as it should, and it looks like the different selection modes
behave properly as well.
The new Selection implementation uses buffer coordinates instead of
screen coordinates. This leads to doing a transform from mouse input to
update the selection, and back to screen coordinates when displaying the
selection. Scrolling the selection is fast because the grid is already
operating in buffer coordinates.
There are several bugs to address:
* A _partially_ visible selection will lead to a crash since the drawing
routine converts selection coordinates to screen coordinates. The
solution will be to clip the coordinates at draw time.
* A selection scrolling off the buffer in either direction leads to
indexing out-of-bounds. The solution again is to clip, but this needs
to be done within Selection::rotate by passing a max limit. It may
also need a return type to indicate that the selection is no longer
visible and should be discarded.
* A selection scrolling out of a logical scrolling region is not
clipped. A temporary and robust workaround is to simply discard the
selection in the case of scrolling in a region.
wip selections
fix issue with line selection
selection mostly working
need to support selection not being on the screen at draw time
Fix selection_to_string
Uncomment tests
Diffstat (limited to 'src/selection.rs')
-rw-r--r-- | src/selection.rs | 174 |
1 files changed, 89 insertions, 85 deletions
diff --git a/src/selection.rs b/src/selection.rs index b5bf5493..31787bbb 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -21,8 +21,7 @@ use std::cmp::{min, max}; use std::ops::Range; -use index::{Point, Column, RangeInclusive, Side, Linear, Line}; -use grid::ToRange; +use index::{Point, Column, Side}; /// Describes a region of a 2-dimensional area /// @@ -47,32 +46,32 @@ pub enum Selection { }, Semantic { /// The region representing start and end of cursor movement - region: Range<Point>, + region: Range<Point<usize>>, /// When beginning a semantic selection, the grid is searched around the /// initial point to find semantic escapes, and this initial expansion /// marks those points. - initial_expansion: Range<Point> + initial_expansion: Range<Point<usize>> }, Lines { /// The region representing start and end of cursor movement - region: Range<Point>, + region: Range<Point<usize>>, /// The line under the initial point. This is always selected regardless /// of which way the cursor is moved. - initial_line: Line + initial_line: usize } } /// A Point and side within that point. #[derive(Debug, Clone)] pub struct Anchor { - point: Point, + point: Point<usize>, side: Side, } impl Anchor { - fn new(point: Point, side: Side) -> Anchor { + fn new(point: Point<usize>, side: Side) -> Anchor { Anchor { point, side } } } @@ -83,9 +82,9 @@ impl Anchor { /// points are two dimensional indices. pub trait SemanticSearch { /// Find the nearest semantic boundary _to the left_ of provided point. - fn semantic_search_left(&self, _: Point) -> Point; + fn semantic_search_left(&self, _: Point<usize>) -> Point<usize>; /// Find the nearest semantic boundary _to the point_ of provided point. - fn semantic_search_right(&self, _: Point) -> Point; + fn semantic_search_right(&self, _: Point<usize>) -> Point<usize>; } /// A type that has 2-dimensional boundaries @@ -95,7 +94,7 @@ pub trait Dimensions { } impl Selection { - pub fn simple(location: Point, side: Side) -> Selection { + pub fn simple(location: Point<usize>, side: Side) -> Selection { Selection::Simple { region: Range { start: Anchor::new(location, side), @@ -104,7 +103,27 @@ impl Selection { } } - pub fn semantic<G: SemanticSearch>(point: Point, grid: &G) -> Selection { + pub fn rotate(&mut self, offset: isize) { + match *self { + Selection::Simple { ref mut region } => { + region.start.point.line = (region.start.point.line as isize + offset) as usize; + region.end.point.line = (region.end.point.line as isize + offset) as usize; + }, + Selection::Semantic { ref mut region, ref mut initial_expansion } => { + region.start.line = (region.start.line as isize + offset) as usize; + region.end.line = (region.end.line as isize + offset) as usize; + initial_expansion.start.line = (initial_expansion.start.line as isize + offset) as usize; + initial_expansion.end.line = (initial_expansion.end.line as isize + offset) as usize; + }, + Selection::Lines { ref mut region, ref mut initial_line } => { + region.start.line = (region.start.line as isize + offset) as usize; + region.end.line = (region.end.line as isize + offset) as usize; + *initial_line = (*initial_line as isize + offset) as usize; + } + } + } + + pub fn semantic<G: SemanticSearch>(point: Point<usize>, grid: &G) -> Selection { let (start, end) = (grid.semantic_search_left(point), grid.semantic_search_right(point)); Selection::Semantic { region: Range { @@ -118,7 +137,7 @@ impl Selection { } } - pub fn lines(point: Point) -> Selection { + pub fn lines(point: Point<usize>) -> Selection { Selection::Lines { region: Range { start: point, @@ -128,7 +147,7 @@ impl Selection { } } - pub fn update(&mut self, location: Point, side: Side) { + 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 } => { @@ -151,14 +170,14 @@ impl Selection { Selection::span_semantic(grid, region, initial_expansion) }, Selection::Lines { ref region, ref initial_line } => { - Selection::span_lines(grid, region, initial_line) + Selection::span_lines(grid, region, *initial_line) } } } fn span_semantic<G>( grid: &G, - region: &Range<Point>, - initial_expansion: &Range<Point> + region: &Range<Point<usize>>, + initial_expansion: &Range<Point<usize>> ) -> Option<Span> where G: SemanticSearch + Dimensions { @@ -172,14 +191,20 @@ impl Selection { (region.end, region.start) }; - // Update start of selection *if* front has moved beyond initial start - if front < start { + println!("BEFORE front={:?}, start={:?}, tail={:?}, end={:?}", front, start, tail, end); + + if front < tail && front.line == tail.line { start = grid.semantic_search_left(front); + end = grid.semantic_search_right(tail); + } else { + start = grid.semantic_search_right(front); + end = grid.semantic_search_left(tail); } - // Update end of selection *if* tail has moved beyond initial end. - if tail > end { - end = grid.semantic_search_right(tail); + println!("AFTER front={:?}, start={:?}, tail={:?}, end={:?}", front, start, tail, end); + + if start > end { + ::std::mem::swap(&mut start, &mut end); } Some(Span { @@ -190,27 +215,27 @@ impl Selection { }) } - fn span_lines<G>(grid: &G, region: &Range<Point>, initial_line: &Line) -> Option<Span> + fn span_lines<G>(grid: &G, region: &Range<Point<usize>>, initial_line: usize) -> Option<Span> where G: Dimensions { // First, create start and end points based on initial line and the grid // dimensions. let mut start = Point { - col: Column(0), - line: *initial_line + col: grid.dimensions().col - 1, + line: initial_line }; let mut end = Point { - col: grid.dimensions().col - 1, - line: *initial_line + col: Column(0), + line: initial_line }; // Now, expand lines based on where cursor started and ended. if region.start.line < region.end.line { - // Start is above end + // Start is below end start.line = min(start.line, region.start.line); end.line = max(end.line, region.end.line); } else { - // Start is below end + // Start is above end start.line = min(start.line, region.end.line); end.line = max(end.line, region.start.line); } @@ -313,27 +338,37 @@ pub enum SpanType { /// Represents a span of selected cells #[derive(Debug, Eq, PartialEq)] pub struct Span { - front: Point, - tail: Point, + front: Point<usize>, + tail: Point<usize>, cols: Column, /// The type says whether ends are included or not. ty: SpanType, } +#[derive(Debug)] +pub struct Locations { + /// Start point from bottom of buffer + pub start: Point<usize>, + /// End point towards top of buffer + pub end: Point<usize>, +} + impl Span { - pub fn to_locations(&self) -> (Point, Point) { - match self.ty { + pub fn to_locations(&self) -> Locations { + let (start, end) = match self.ty { SpanType::Inclusive => (self.front, self.tail), SpanType::Exclusive => { (Span::wrap_start(self.front, self.cols), Span::wrap_end(self.tail, self.cols)) }, SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail), SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)) - } + }; + + Locations { start, end } } - fn wrap_start(mut start: Point, cols: Column) -> Point { + fn wrap_start(mut start: Point<usize>, cols: Column) -> Point<usize> { if start.col == cols - 1 { Point { line: start.line + 1, @@ -345,8 +380,8 @@ impl Span { } } - fn wrap_end(end: Point, cols: Column) -> Point { - if end.col == Column(0) && end.line != Line(0) { + fn wrap_end(end: Point<usize>, cols: Column) -> Point<usize> { + if end.col == Column(0) && end.line != 0 { Point { line: end.line - 1, col: cols @@ -358,37 +393,6 @@ impl Span { } } } - - #[inline] - fn exclude_start(start: Linear) -> Linear { - start + 1 - } - - #[inline] - fn exclude_end(end: Linear) -> Linear { - if end > Linear(0) { - end - 1 - } else { - end - } - } -} - -impl ToRange for Span { - fn to_range(&self) -> RangeInclusive<Linear> { - let cols = self.cols; - let start = Linear(self.front.line.0 * cols.0 + self.front.col.0); - let end = Linear(self.tail.line.0 * cols.0 + self.tail.col.0); - - let (start, end) = match self.ty { - SpanType::Inclusive => (start, end), - SpanType::Exclusive => (Span::exclude_start(start), Span::exclude_end(end)), - SpanType::ExcludeFront => (Span::exclude_start(start), end), - SpanType::ExcludeTail => (start, Span::exclude_end(end)) - }; - - RangeInclusive::new(start, end) - } } /// Tests for selection @@ -422,8 +426,8 @@ mod test { } impl super::SemanticSearch for Dimensions { - fn semantic_search_left(&self, _: Point) -> Point { unimplemented!(); } - fn semantic_search_right(&self, _: Point) -> Point { unimplemented!(); } + fn semantic_search_left(&self, _: Point<usize>) -> Point<usize> { unimplemented!(); } + fn semantic_search_right(&self, _: Point<usize>) -> Point<usize> { unimplemented!(); } } /// Test case of single cell selection @@ -433,7 +437,7 @@ mod test { /// 3. [BE] #[test] fn single_cell_left_to_right() { - let location = Point { line: Line(0), col: Column(0) }; + let location = Point { line: 0, col: Column(0) }; let mut selection = Selection::simple(location, Side::Left); selection.update(location, Side::Right); @@ -452,7 +456,7 @@ mod test { /// 3. [EB] #[test] fn single_cell_right_to_left() { - let location = Point { line: Line(0), col: Column(0) }; + let location = Point { line: 0, col: Column(0) }; let mut selection = Selection::simple(location, Side::Right); selection.update(location, Side::Left); @@ -471,8 +475,8 @@ mod test { /// 3. [ B][E ] #[test] fn between_adjacent_cells_left_to_right() { - let mut selection = Selection::simple(Point::new(Line(0), Column(0)), Side::Right); - selection.update(Point::new(Line(0), Column(1)), Side::Left); + 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(&Dimensions::new(1, 2)), None); } @@ -484,8 +488,8 @@ mod test { /// 3. [ E][B ] #[test] fn between_adjacent_cells_right_to_left() { - let mut selection = Selection::simple(Point::new(Line(0), Column(1)), Side::Left); - selection.update(Point::new(Line(0), Column(0)), Side::Right); + 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(&Dimensions::new(1, 2)), None); } @@ -501,13 +505,13 @@ mod test { /// [XX][XB][ ][ ][ ] #[test] fn across_adjacent_lines_upward_final_cell_exclusive() { - let mut selection = Selection::simple(Point::new(Line(1), Column(1)), Side::Right); - selection.update(Point::new(Line(0), Column(1)), Side::Right); + 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(&Dimensions::new(2, 5)).unwrap(), Span { cols: Column(5), - front: Point::new(Line(0), Column(1)), - tail: Point::new(Line(1), Column(1)), + front: Point::new(0, Column(1)), + tail: Point::new(1, Column(1)), ty: SpanType::ExcludeFront }); } @@ -525,14 +529,14 @@ mod test { /// [XE][ ][ ][ ][ ] #[test] fn selection_bigger_then_smaller() { - let mut selection = Selection::simple(Point::new(Line(0), Column(1)), Side::Right); - selection.update(Point::new(Line(1), Column(1)), Side::Right); - selection.update(Point::new(Line(1), Column(0)), Side::Right); + let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right); + selection.update(Point::new(1, Column(1)), Side::Right); + selection.update(Point::new(1, Column(0)), Side::Right); assert_eq!(selection.to_span(&Dimensions::new(2, 5)).unwrap(), Span { cols: Column(5), - front: Point::new(Line(0), Column(1)), - tail: Point::new(Line(1), Column(0)), + front: Point::new(0, Column(1)), + tail: Point::new(1, Column(0)), ty: SpanType::ExcludeFront }); } |