From a88961bbf01299cef3b675a30575a7d23c5c485a Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 12 Oct 2017 20:59:21 -0700 Subject: Remove redundant selection::Region type The type selection::Region was defined identially to std::ops::Range. Using something other than range just served to confuse. --- src/selection.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index cd905164..64cee8c2 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -19,6 +19,7 @@ //! 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::cmp::{min, max}; +use std::ops::Range; use index::{Point, Column, RangeInclusive, Side, Linear, Line}; use grid::ToRange; @@ -41,20 +42,20 @@ use grid::ToRange; pub enum Selection { Simple { /// The region representing start and end of cursor movement - region: Region, + region: Range, }, Semantic { /// The region representing start and end of cursor movement - region: Region, + region: Range, /// 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: Region + initial_expansion: Range }, Lines { /// The region representing start and end of cursor movement - region: Region, + region: Range, /// The line under the initial point. This is always selected regardless /// of which way the cursor is moved. @@ -62,11 +63,6 @@ pub enum Selection { } } -pub struct Region { - start: T, - end: T -} - /// A Point and side within that point. pub struct Anchor { point: Point, @@ -99,7 +95,7 @@ pub trait Dimensions { impl Selection { pub fn simple(location: Point, side: Side) -> Selection { Selection::Simple { - region: Region { + region: Range { start: Anchor::new(location, side), end: Anchor::new(location, side) } @@ -109,20 +105,20 @@ impl Selection { pub fn semantic(point: Point, grid: &G) -> Selection { let (start, end) = (grid.semantic_search_left(point), grid.semantic_search_right(point)); Selection::Semantic { - region: Region { + region: Range { start: point, end: point, }, - initial_expansion: Region { - start, - end, + initial_expansion: Range { + start: start, + end: end } } } pub fn lines(point: Point) -> Selection { Selection::Lines { - region: Region { + region: Range { start: point, end: point }, @@ -159,8 +155,8 @@ impl Selection { } fn span_semantic( grid: &G, - region: &Region, - initial_expansion: &Region + region: &Range, + initial_expansion: &Range ) -> Option where G: SemanticSearch + Dimensions { @@ -192,7 +188,7 @@ impl Selection { }) } - fn span_lines(grid: &G, region: &Region, initial_line: &Line) -> Option + fn span_lines(grid: &G, region: &Range, initial_line: &Line) -> Option where G: Dimensions { // First, create start and end points based on initial line and the grid @@ -225,7 +221,7 @@ impl Selection { }) } - fn span_simple(grid: &G, region: &Region) -> Option { + fn span_simple(grid: &G, region: &Range) -> Option { let start = region.start.point; let start_side = region.start.side; let end = region.end.point; -- cgit From 8ef062efd94975885776e61b6df936088b018da0 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 5 Mar 2018 09:57:34 -0800 Subject: Move selection into Grid Supporting selections with scrollback has two major components: 1. Grid needs access to Selection so that it may update the scroll position as the terminal text changes. 2. Selection needs to be implemented in terms of buffer offsets -- NOT lines -- and be updated when Storage is rotated. This commit implements the first part. --- src/selection.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 64cee8c2..b5bf5493 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -39,6 +39,7 @@ use grid::ToRange; /// [`simple`]: enum.Selection.html#method.simple /// [`semantic`]: enum.Selection.html#method.semantic /// [`lines`]: enum.Selection.html#method.lines +#[derive(Debug, Clone)] pub enum Selection { Simple { /// The region representing start and end of cursor movement @@ -64,6 +65,7 @@ pub enum Selection { } /// A Point and side within that point. +#[derive(Debug, Clone)] pub struct Anchor { point: Point, side: Side, -- cgit From 8018dee1812ab88793c0f18e13335fa77c068000 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Tue, 6 Mar 2018 20:57:40 -0800 Subject: 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 --- src/selection.rs | 174 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 85 deletions(-) (limited to 'src/selection.rs') 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, + region: Range>, /// 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 + initial_expansion: Range> }, Lines { /// The region representing start and end of cursor movement - region: Range, + region: Range>, /// 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, side: Side, } impl Anchor { - fn new(point: Point, side: Side) -> Anchor { + fn new(point: Point, 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) -> Point; /// Find the nearest semantic boundary _to the point_ of provided point. - fn semantic_search_right(&self, _: Point) -> Point; + fn semantic_search_right(&self, _: Point) -> Point; } /// 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, side: Side) -> Selection { Selection::Simple { region: Range { start: Anchor::new(location, side), @@ -104,7 +103,27 @@ impl Selection { } } - pub fn semantic(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(point: Point, 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) -> 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, 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( grid: &G, - region: &Range, - initial_expansion: &Range + region: &Range>, + initial_expansion: &Range> ) -> Option 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(grid: &G, region: &Range, initial_line: &Line) -> Option + fn span_lines(grid: &G, region: &Range>, initial_line: usize) -> Option 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, + tail: Point, 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, + /// End point towards top of buffer + pub end: Point, +} + 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, cols: Column) -> Point { 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, cols: Column) -> Point { + 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 { - 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) -> Point { unimplemented!(); } + fn semantic_search_right(&self, _: Point) -> Point { 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 }); } -- cgit From d9bd21d33f7f35d1362a581cefb1c897a821fcad Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 10 Mar 2018 12:14:58 +0100 Subject: Fix selection in scrollback There were a few issues with selection in scrollback that were mainly off-by-one errors. This aims at fixing these issues. This also fixes a bug that currently exists in master where the last cell is not selected when the mouse leaves the window to the right. --- src/selection.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 31787bbb..8e7fa29b 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -191,8 +191,6 @@ impl Selection { (region.end, region.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); @@ -201,8 +199,6 @@ impl Selection { end = grid.semantic_search_left(tail); } - println!("AFTER front={:?}, start={:?}, tail={:?}, end={:?}", front, start, tail, end); - if start > end { ::std::mem::swap(&mut start, &mut end); } @@ -249,12 +245,21 @@ impl Selection { } fn span_simple(grid: &G, region: &Range) -> Option { - let start = region.start.point; + let mut start = region.start.point; let start_side = region.start.side; - let end = region.end.point; + let mut end = region.end.point; let end_side = region.end.side; let cols = grid.dimensions().col; + // Handle some edge cases + if start.line > end.line { + start.col += 1; + end.col -= 1; + } else if start.line < end.line { + start.col -= 1; + end.col += 1; + } + let (front, tail, front_side, tail_side) = if start > end { // Selected upward; start/end are swapped (end, start, end_side, start_side) -- cgit From 58c69cafad3b1dafa3631d911c6bfc21f5e5dec5 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 13 Mar 2018 19:00:14 +0100 Subject: Fix multi-line selection with single cell end When the user selected multiple lines, dragging the selection downwards, and then leaves the cursor to the left side of the first cell, the first cell was still incorrectly selected. This has been fixed. The selection also did not update if the mouse was outside of the window, now all movement events are accpeted even when the mouse is outside of the window. This allows updating the selection when the user is dragging the cursor too far. Mouse movement and click events outside of the window are not propagated, these are only used for updating the selection. --- src/selection.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 8e7fa29b..d49236a4 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -253,8 +253,31 @@ impl Selection { // Handle some edge cases if start.line > end.line { - start.col += 1; - end.col -= 1; + if end.col > Column(0) { + start.col += 1; + end.col -= 1; + } + // Special case for when a multi-line selection to the + // bottom ends on a new line with just one cell selected + // and the first cell should not be selected + else { + if start_side == Side::Right { + start.col += 1; + } + + // Remove the single selected cell if mouse left window + if end_side == Side::Left { + end.line += 1; + end.col = cols - 1; + } + + return Some(Span { + cols, + front: end, + tail: start, + ty: SpanType::Inclusive, + }); + } } else if start.line < end.line { start.col -= 1; end.col += 1; -- cgit From e20aa550cbcf7b3f7be6757c007960b3f9b1ac08 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 13 Mar 2018 22:48:08 +0100 Subject: Fix selection starting in first cell When selecting to the top and starting in the first cell, alacritty would crash. These cases have been fixed and now selection should be completely working. --- src/selection.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index d49236a4..f775f1f6 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -279,8 +279,31 @@ impl Selection { }); } } else if start.line < end.line { - start.col -= 1; end.col += 1; + if start.col > Column(0) { + start.col -= 1; + } + // Special case for when a selection is started + // in the first cell of a line + else { + let ty = match end_side { + Side::Left => SpanType::ExcludeTail, + Side::Right => SpanType::Inclusive, + }; + + // Switch start cell to last cell of previous line + if start_side == Side::Left { + start.line += 1; + start.col = cols - 1; + } + + return Some(Span { + ty, + cols, + front: start, + tail: end, + }); + } } let (front, tail, front_side, tail_side) = if start > end { -- cgit From f46381616272ba18a1a8a3f441b836c79bd1a029 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Thu, 15 Mar 2018 20:13:00 +0100 Subject: Refactor `span_simple` selection The current `span_simple` selection is everything but simple. This version should have the same functionality as the current `span_simple` with the difference that a lot of complexity has been removed. Not only is this code shorter, it should also be significantly easier to understand with no "magic" to it. This will hopefully prevent us from having an unmaintainable blob of off-by-one guessing in the repo. Also removed the `out` file which I used in the original PR because scrollback is not implemented yet. :) --- src/selection.rs | 143 +++++++++++++------------------------------------------ 1 file changed, 34 insertions(+), 109 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index f775f1f6..20b1d601 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -245,127 +245,52 @@ impl Selection { } fn span_simple(grid: &G, region: &Range) -> Option { - let mut start = region.start.point; + let start = region.start.point; let start_side = region.start.side; - let mut end = region.end.point; + let end = region.end.point; let end_side = region.end.side; let cols = grid.dimensions().col; - // Handle some edge cases - if start.line > end.line { - if end.col > Column(0) { - start.col += 1; - end.col -= 1; - } - // Special case for when a multi-line selection to the - // bottom ends on a new line with just one cell selected - // and the first cell should not be selected - else { - if start_side == Side::Right { - start.col += 1; - } - - // Remove the single selected cell if mouse left window - if end_side == Side::Left { - end.line += 1; - end.col = cols - 1; - } - - return Some(Span { - cols, - front: end, - tail: start, - ty: SpanType::Inclusive, - }); - } - } else if start.line < end.line { - end.col += 1; - if start.col > Column(0) { - start.col -= 1; - } - // Special case for when a selection is started - // in the first cell of a line - else { - let ty = match end_side { - Side::Left => SpanType::ExcludeTail, - Side::Right => SpanType::Inclusive, - }; - - // Switch start cell to last cell of previous line - if start_side == Side::Left { - start.line += 1; - start.col = cols - 1; - } - - return Some(Span { - ty, - cols, - front: start, - tail: end, - }); - } + // No selection for single cell with identical sides or two cell with right+left sides + if (start == end && start_side == end_side) + || (start_side == Side::Right && end_side == Side::Left && end.col == start.col + 1) + { + return None; } - let (front, tail, front_side, tail_side) = if start > end { - // Selected upward; start/end are swapped - (end, start, end_side, start_side) - } else { - // Selected downward; no swapping - (start, end, start_side, end_side) - }; - - debug_assert!(!(tail < front)); - - // Single-cell selections are a special case - if start == end { - if start_side == end_side { - return None; + // Make sure front is always the "bottom" and tail is always the "top" + let (mut front, mut tail, front_side, tail_side) = + if start.line > end.line || start.line == end.line && start.col <= end.col { + // Selected upward; start/end are swapped + (end, start, end_side, start_side) } else { - return Some(Span { - cols, - ty: SpanType::Inclusive, - front, - tail, - }); + // Selected downward; no swapping + (start, end, start_side, end_side) + }; + + // Remove last cell if selection ends to the left of a cell + if front_side == Side::Left && start != end { + if front.col != Column(0) { + front.col -= 1; + } + // Special case when selection starts to left of first cell + else { + front.col = cols - 1; + front.line += 1; } } - // The other special case is two adjacent cells with no - // selection: [ B][E ] or [ E][B ] - let adjacent = tail.line == front.line && tail.col - front.col == Column(1); - if adjacent && front_side == Side::Right && tail_side == Side::Left { - return None; + // Remove first cell if selection starts at the right of a cell + if tail_side == Side::Right && front != tail { + tail.col += 1; } - Some(match (front_side, tail_side) { - // [FX][XX][XT] - (Side::Left, Side::Right) => Span { - cols, - front, - tail, - ty: SpanType::Inclusive - }, - // [ F][XX][T ] - (Side::Right, Side::Left) => Span { - cols, - front, - tail, - ty: SpanType::Exclusive - }, - // [FX][XX][T ] - (Side::Left, Side::Left) => Span { - cols, - front, - tail, - ty: SpanType::ExcludeTail - }, - // [ F][XX][XT] - (Side::Right, Side::Right) => Span { - cols, - front, - tail, - ty: SpanType::ExcludeFront - }, + // Return the selection with all cells inclusive + Some(Span { + cols, + front, + tail, + ty: SpanType::Inclusive, }) } } -- cgit From f66e3e457bdda808cc3f994a02fd6f7ce5ba381e Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sun, 25 Mar 2018 21:09:18 +0200 Subject: Fix selection tests The latest selection changes broke a few tests, these have been corrected. Two of these tests were broken because they assumed different span types, the test have been changed here because the result was correct. One test did actually catch a bug where selection of two cells from right to left would incorrectly mark the cells as selected even though they should not have been, this has been fixed in the `simple_span` method. --- src/selection.rs | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 20b1d601..188df348 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -251,13 +251,6 @@ impl Selection { let end_side = region.end.side; let cols = grid.dimensions().col; - // No selection for single cell with identical sides or two cell with right+left sides - if (start == end && start_side == end_side) - || (start_side == Side::Right && end_side == Side::Left && end.col == start.col + 1) - { - return None; - } - // Make sure front is always the "bottom" and tail is always the "top" let (mut front, mut tail, front_side, tail_side) = if start.line > end.line || start.line == end.line && start.col <= end.col { @@ -268,6 +261,14 @@ impl Selection { (start, end, start_side, end_side) }; + // No selection for single cell with identical sides or two cell with right+left sides + if (front == tail && front_side == tail_side) + || (tail_side == Side::Right && front_side == Side::Left && front.line == tail.line + && front.col == tail.col + 1) + { + return None; + } + // Remove last cell if selection ends to the left of a cell if front_side == Side::Left && start != end { if front.col != Column(0) { @@ -475,10 +476,10 @@ mod test { /// /// 1. [ ][ ][ ][ ][ ] /// [ ][ ][ ][ ][ ] - /// 2. [ ][ ][ ][ ][ ] - /// [ ][ B][ ][ ][ ] - /// 3. [ ][ E][XX][XX][XX] - /// [XX][XB][ ][ ][ ] + /// 2. [ ][ B][ ][ ][ ] + /// [ ][ ][ ][ ][ ] + /// 3. [ ][ B][XX][XX][XX] + /// [XX][XE][ ][ ][ ] #[test] fn across_adjacent_lines_upward_final_cell_exclusive() { let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right); @@ -487,8 +488,8 @@ mod test { assert_eq!(selection.to_span(&Dimensions::new(2, 5)).unwrap(), Span { cols: Column(5), front: Point::new(0, Column(1)), - tail: Point::new(1, Column(1)), - ty: SpanType::ExcludeFront + tail: Point::new(1, Column(2)), + ty: SpanType::Inclusive, }); } @@ -497,12 +498,12 @@ mod test { /// /// 1. [ ][ ][ ][ ][ ] /// [ ][ ][ ][ ][ ] - /// 2. [ ][ B][ ][ ][ ] - /// [ ][ ][ ][ ][ ] - /// 3. [ ][ B][XX][XX][XX] - /// [XX][XE][ ][ ][ ] - /// 4. [ ][ B][XX][XX][XX] - /// [XE][ ][ ][ ][ ] + /// 2. [ ][ ][ ][ ][ ] + /// [ ][ B][ ][ ][ ] + /// 3. [ ][ E][XX][XX][XX] + /// [XX][XB][ ][ ][ ] + /// 4. [ E][XX][XX][XX][XX] + /// [XX][XB][ ][ ][ ] #[test] fn selection_bigger_then_smaller() { let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right); @@ -512,8 +513,8 @@ mod test { assert_eq!(selection.to_span(&Dimensions::new(2, 5)).unwrap(), Span { cols: Column(5), front: Point::new(0, Column(1)), - tail: Point::new(1, Column(0)), - ty: SpanType::ExcludeFront + tail: Point::new(1, Column(1)), + ty: SpanType::Inclusive, }); } } -- cgit From b19045da66899999856c6b2cc6707b60c607660a Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 2 Apr 2018 08:44:54 -0700 Subject: Fix BCE ref tests BCE was broken in attempt to optimize row clearing. The fix is to revert to passing in the current cursor state when clearing. --- src/selection.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 188df348..6f910bce 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -181,9 +181,6 @@ impl Selection { ) -> Option where G: SemanticSearch + Dimensions { - let mut start = initial_expansion.start; - let mut end = initial_expansion.end; - // Normalize ordering of selected cells let (front, tail) = if region.start < region.end { (region.start, region.end) @@ -191,13 +188,11 @@ impl Selection { (region.end, region.start) }; - if front < tail && front.line == tail.line { - start = grid.semantic_search_left(front); - end = grid.semantic_search_right(tail); + let (mut start, mut end) = if front < tail && front.line == tail.line { + (grid.semantic_search_left(front), grid.semantic_search_right(tail)) } else { - start = grid.semantic_search_right(front); - end = grid.semantic_search_left(tail); - } + (grid.semantic_search_right(front), grid.semantic_search_left(tail)) + }; if start > end { ::std::mem::swap(&mut start, &mut end); -- cgit From 8168d85a21b1a67b9cf25808c4e3e01f7437b50d Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 28 Apr 2018 16:15:21 +0200 Subject: Improve storage comparison algorithm Instead of iterating over the raw storage vector because the offsets don't allow direct comparison, the comparison is now done in chunks. Based on benchmarking this is a lot more efficient than using split_off + append or iterating over the elements of the buffer. The `history_size` field has also been removed from the storage structure because it can be easily calculated by substracting the number of visible lines from the length of the raw storage vector. --- src/selection.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index 6f910bce..a54bd49d 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -38,7 +38,7 @@ use index::{Point, Column, Side}; /// [`simple`]: enum.Selection.html#method.simple /// [`semantic`]: enum.Selection.html#method.semantic /// [`lines`]: enum.Selection.html#method.lines -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Selection { Simple { /// The region representing start and end of cursor movement @@ -64,7 +64,7 @@ pub enum Selection { } /// A Point and side within that point. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Anchor { point: Point, side: Side, -- cgit From 24c50fa0a793cd8c6127b5cf8ba3ea59f47cc1ca Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Tue, 29 May 2018 21:45:28 -0700 Subject: Remove dead code --- src/selection.rs | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index a54bd49d..dca16e26 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -47,11 +47,6 @@ pub enum Selection { Semantic { /// The region representing start and end of cursor movement region: Range>, - - /// 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> }, Lines { /// The region representing start and end of cursor movement @@ -109,11 +104,9 @@ impl Selection { 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 } => { + Selection::Semantic { ref mut region } => { 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; @@ -123,16 +116,11 @@ impl Selection { } } - pub fn semantic(point: Point, grid: &G) -> Selection { - let (start, end) = (grid.semantic_search_left(point), grid.semantic_search_right(point)); + pub fn semantic(point: Point) -> Selection { Selection::Semantic { region: Range { start: point, end: point, - }, - initial_expansion: Range { - start: start, - end: end } } } @@ -153,7 +141,7 @@ impl Selection { Selection::Simple { ref mut region } => { region.end = Anchor::new(location, side); }, - Selection::Semantic { ref mut region, .. } | + Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => { region.end = location; @@ -166,8 +154,8 @@ impl Selection { Selection::Simple { ref region } => { Selection::span_simple(grid, region) }, - Selection::Semantic { ref region, ref initial_expansion } => { - Selection::span_semantic(grid, region, initial_expansion) + Selection::Semantic { ref region } => { + Selection::span_semantic(grid, region) }, Selection::Lines { ref region, ref initial_line } => { Selection::span_lines(grid, region, *initial_line) @@ -177,7 +165,6 @@ impl Selection { fn span_semantic( grid: &G, region: &Range>, - initial_expansion: &Range> ) -> Option where G: SemanticSearch + Dimensions { -- cgit From f50ca1a54c94fe324d22d985c1acae1ff7c16a80 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 21 Jul 2018 17:17:41 +0000 Subject: Scrollback cleanup There were some unneeded codeblocks and TODO/XXX comments in the code that have been removed. All issues marked with TODO/XXX have either been already resolved or tracking issues exist. --- src/selection.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/selection.rs') diff --git a/src/selection.rs b/src/selection.rs index dca16e26..702599e3 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -157,8 +157,8 @@ impl Selection { Selection::Semantic { ref region } => { Selection::span_semantic(grid, region) }, - Selection::Lines { ref region, ref initial_line } => { - Selection::span_lines(grid, region, *initial_line) + Selection::Lines { ref region, initial_line } => { + Selection::span_lines(grid, region, initial_line) } } } @@ -253,13 +253,12 @@ impl Selection { // Remove last cell if selection ends to the left of a cell if front_side == Side::Left && start != end { - if front.col != Column(0) { - front.col -= 1; - } // Special case when selection starts to left of first cell - else { + if front.col == Column(0) { front.col = cols - 1; front.line += 1; + } else { + front.col -= 1; } } -- cgit