aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/index.rs12
-rw-r--r--src/selection.rs201
-rw-r--r--src/term/mod.rs6
4 files changed, 171 insertions, 49 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac8c0c06..c0aa9fd0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed erroneous results when using the `indexed_colors` config option
- Fixed rendering cursors other than rectangular with the RustType backend
+- Selection memory leak and glitches in the alternate screen buffer
## Version 0.2.1
diff --git a/src/index.rs b/src/index.rs
index ab8e7416..149fd458 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -52,6 +52,18 @@ impl Ord for Point {
}
}
+impl From<Point<usize>> for Point<isize> {
+ fn from(point: Point<usize>) -> Self {
+ Point::new(point.line as isize, point.col)
+ }
+}
+
+impl From<Point<isize>> for Point<usize> {
+ fn from(point: Point<isize>) -> Self {
+ Point::new(point.line as usize, point.col)
+ }
+}
+
/// A line
///
/// Newtype to avoid passing values incorrectly
diff --git a/src/selection.rs b/src/selection.rs
index 702599e3..932a61ee 100644
--- a/src/selection.rs
+++ b/src/selection.rs
@@ -46,27 +46,27 @@ pub enum Selection {
},
Semantic {
/// The region representing start and end of cursor movement
- region: Range<Point<usize>>,
+ region: Range<Point<isize>>,
},
Lines {
/// The region representing start and end of cursor movement
- region: Range<Point<usize>>,
+ region: Range<Point<isize>>,
/// The line under the initial point. This is always selected regardless
/// of which way the cursor is moved.
- initial_line: usize
+ initial_line: isize
}
}
/// A Point and side within that point.
#[derive(Debug, Clone, PartialEq)]
pub struct Anchor {
- point: Point<usize>,
+ point: Point<isize>,
side: Side,
}
impl Anchor {
- fn new(point: Point<usize>, side: Side) -> Anchor {
+ fn new(point: Point<isize>, side: Side) -> Anchor {
Anchor { point, side }
}
}
@@ -92,8 +92,8 @@ impl Selection {
pub fn simple(location: Point<usize>, side: Side) -> Selection {
Selection::Simple {
region: Range {
- start: Anchor::new(location, side),
- end: Anchor::new(location, side)
+ start: Anchor::new(location.into(), side),
+ end: Anchor::new(location.into(), side)
}
}
}
@@ -101,17 +101,17 @@ impl 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;
+ region.start.point.line += offset;
+ region.end.point.line += offset;
},
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;
+ region.start.line += offset;
+ region.end.line += offset;
},
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;
+ region.start.line += offset;
+ region.end.line += offset;
+ *initial_line += offset;
}
}
}
@@ -119,8 +119,8 @@ impl Selection {
pub fn semantic(point: Point<usize>) -> Selection {
Selection::Semantic {
region: Range {
- start: point,
- end: point,
+ start: point.into(),
+ end: point.into(),
}
}
}
@@ -128,10 +128,10 @@ impl Selection {
pub fn lines(point: Point<usize>) -> Selection {
Selection::Lines {
region: Range {
- start: point,
- end: point
+ start: point.into(),
+ end: point.into(),
},
- initial_line: point.line
+ initial_line: point.line as isize,
}
}
@@ -139,46 +139,58 @@ impl Selection {
// Always update the `end`; can normalize later during span generation.
match *self {
Selection::Simple { ref mut region } => {
- region.end = Anchor::new(location, side);
+ region.end = Anchor::new(location.into(), side);
},
Selection::Semantic { ref mut region } |
Selection::Lines { ref mut region, .. } =>
{
- region.end = location;
+ region.end = location.into();
},
}
}
- pub fn to_span<G: SemanticSearch + Dimensions>(&self, grid: &G) -> Option<Span> {
+ pub fn to_span<G>(&self, grid: &G, alt_screen: bool) -> Option<Span>
+ where
+ G: SemanticSearch + Dimensions,
+ {
match *self {
Selection::Simple { ref region } => {
- Selection::span_simple(grid, region)
+ Selection::span_simple(grid, region, alt_screen)
},
Selection::Semantic { ref region } => {
- Selection::span_semantic(grid, region)
+ Selection::span_semantic(grid, region, alt_screen)
},
Selection::Lines { ref region, initial_line } => {
- Selection::span_lines(grid, region, initial_line)
+ Selection::span_lines(grid, region, initial_line, alt_screen)
}
}
}
+
fn span_semantic<G>(
grid: &G,
- region: &Range<Point<usize>>,
+ region: &Range<Point<isize>>,
+ alt_screen: bool,
) -> Option<Span>
where G: SemanticSearch + Dimensions
{
+ let cols = grid.dimensions().col;
+ let lines = grid.dimensions().line.0 as isize;
+
// Normalize ordering of selected cells
- let (front, tail) = if region.start < region.end {
+ let (mut front, mut tail) = if region.start < region.end {
(region.start, region.end)
} else {
(region.end, region.start)
};
+ if alt_screen {
+ Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?;
+ }
+
let (mut start, mut end) = if front < tail && front.line == tail.line {
- (grid.semantic_search_left(front), grid.semantic_search_right(tail))
+ (grid.semantic_search_left(front.into()), grid.semantic_search_right(tail.into()))
} else {
- (grid.semantic_search_right(front), grid.semantic_search_left(tail))
+ (grid.semantic_search_right(front.into()), grid.semantic_search_left(tail.into()))
};
if start > end {
@@ -186,20 +198,29 @@ impl Selection {
}
Some(Span {
- cols: grid.dimensions().col,
+ cols,
front: start,
tail: end,
ty: SpanType::Inclusive,
})
}
- fn span_lines<G>(grid: &G, region: &Range<Point<usize>>, initial_line: usize) -> Option<Span>
- where G: Dimensions
+ fn span_lines<G>(
+ grid: &G,
+ region: &Range<Point<isize>>,
+ initial_line: isize,
+ alt_screen: bool,
+ ) -> Option<Span>
+ where
+ G: Dimensions
{
+ let cols = grid.dimensions().col;
+ let lines = grid.dimensions().line.0 as isize;
+
// First, create start and end points based on initial line and the grid
// dimensions.
let mut start = Point {
- col: grid.dimensions().col - 1,
+ col: cols - 1,
line: initial_line
};
let mut end = Point {
@@ -218,20 +239,28 @@ impl Selection {
end.line = max(end.line, region.start.line);
}
+ if alt_screen {
+ Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?;
+ }
+
Some(Span {
- cols: grid.dimensions().col,
- front: start,
- tail: end,
+ cols,
+ front: start.into(),
+ tail: end.into(),
ty: SpanType::Inclusive
})
}
- fn span_simple<G: Dimensions>(grid: &G, region: &Range<Anchor>) -> Option<Span> {
+ fn span_simple<G>(grid: &G, region: &Range<Anchor>, alt_screen: bool) -> Option<Span>
+ where
+ G: Dimensions
+ {
let start = region.start.point;
let start_side = region.start.side;
let end = region.end.point;
let end_side = region.end.side;
let cols = grid.dimensions().col;
+ let lines = grid.dimensions().line.0 as isize;
// Make sure front is always the "bottom" and tail is always the "top"
let (mut front, mut tail, front_side, tail_side) =
@@ -267,14 +296,50 @@ impl Selection {
tail.col += 1;
}
+ if alt_screen {
+ Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?;
+ }
+
// Return the selection with all cells inclusive
Some(Span {
cols,
- front,
- tail,
+ front: front.into(),
+ tail: tail.into(),
ty: SpanType::Inclusive,
})
}
+
+ // Clamp selection in the alternate screen to the visible region
+ fn alt_screen_clamp(
+ front: &mut Point<isize>,
+ tail: &mut Point<isize>,
+ lines: isize,
+ cols: Column,
+ ) -> Option<()> {
+ if tail.line >= lines {
+ // Don't show selection above visible region
+ if front.line >= lines {
+ return None;
+ }
+
+ // Clamp selection above viewport to visible region
+ tail.line = lines - 1;
+ tail.col = Column(0);
+ }
+
+ if front.line < 0 {
+ // Don't show selection below visible region
+ if tail.line < 0 {
+ return None;
+ }
+
+ // Clamp selection below viewport to visible region
+ front.line = 0;
+ front.col = cols - 1;
+ }
+
+ Some(())
+ }
}
/// How to interpret the locations of a Span.
@@ -384,8 +449,8 @@ mod test {
}
impl super::SemanticSearch for Dimensions {
- fn semantic_search_left(&self, _: Point<usize>) -> Point<usize> { unimplemented!(); }
- fn semantic_search_right(&self, _: Point<usize>) -> Point<usize> { unimplemented!(); }
+ fn semantic_search_left(&self, point: Point<usize>) -> Point<usize> { point }
+ fn semantic_search_right(&self, point: Point<usize>) -> Point<usize> { point }
}
/// Test case of single cell selection
@@ -399,7 +464,7 @@ mod test {
let mut selection = Selection::simple(location, Side::Left);
selection.update(location, Side::Right);
- assert_eq!(selection.to_span(&Dimensions::new(1, 1)).unwrap(), Span {
+ assert_eq!(selection.to_span(&Dimensions::new(1, 1), false).unwrap(), Span {
cols: Column(1),
ty: SpanType::Inclusive,
front: location,
@@ -418,7 +483,7 @@ mod test {
let mut selection = Selection::simple(location, Side::Right);
selection.update(location, Side::Left);
- assert_eq!(selection.to_span(&Dimensions::new(1, 1)).unwrap(), Span {
+ assert_eq!(selection.to_span(&Dimensions::new(1, 1), false).unwrap(), Span {
cols: Column(1),
ty: SpanType::Inclusive,
front: location,
@@ -436,7 +501,7 @@ 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(&Dimensions::new(1, 2)), None);
+ assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None);
}
/// Test adjacent cell selection from right to left
@@ -449,7 +514,7 @@ 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(&Dimensions::new(1, 2)), None);
+ assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None);
}
/// Test selection across adjacent lines
@@ -466,7 +531,7 @@ 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(&Dimensions::new(2, 5)).unwrap(), Span {
+ assert_eq!(selection.to_span(&Dimensions::new(2, 5), false).unwrap(), Span {
cols: Column(5),
front: Point::new(0, Column(1)),
tail: Point::new(1, Column(2)),
@@ -491,11 +556,53 @@ 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(&Dimensions::new(2, 5)).unwrap(), Span {
+ assert_eq!(selection.to_span(&Dimensions::new(2, 5), false).unwrap(), Span {
cols: Column(5),
front: Point::new(0, Column(1)),
tail: Point::new(1, Column(1)),
ty: SpanType::Inclusive,
});
}
+
+ #[test]
+ fn alt_scren_lines() {
+ let mut selection = Selection::lines(Point::new(0, Column(0)));
+ selection.update(Point::new(5, Column(3)), Side::Right);
+ selection.rotate(-3);
+
+ assert_eq!(selection.to_span(&Dimensions::new(10, 5), true).unwrap(), Span {
+ cols: Column(5),
+ front: Point::new(0, Column(4)),
+ tail: Point::new(2, Column(0)),
+ ty: SpanType::Inclusive,
+ });
+ }
+
+ #[test]
+ fn alt_screen_semantic() {
+ let mut selection = Selection::semantic(Point::new(0, Column(0)));
+ selection.update(Point::new(5, Column(3)), Side::Right);
+ selection.rotate(-3);
+
+ assert_eq!(selection.to_span(&Dimensions::new(10, 5), true).unwrap(), Span {
+ cols: Column(5),
+ front: Point::new(0, Column(4)),
+ tail: Point::new(2, Column(3)),
+ ty: SpanType::Inclusive,
+ });
+ }
+
+ #[test]
+ fn alt_screen_simple() {
+ let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
+ selection.update(Point::new(5, Column(3)), Side::Right);
+ selection.rotate(-3);
+
+ assert_eq!(selection.to_span(&Dimensions::new(10, 5), true).unwrap(), Span {
+ cols: Column(5),
+ front: Point::new(0, Column(4)),
+ tail: Point::new(2, Column(4)),
+ ty: SpanType::Inclusive,
+ });
+ }
}
diff --git a/src/term/mod.rs b/src/term/mod.rs
index 495a7567..f4563922 100644
--- a/src/term/mod.rs
+++ b/src/term/mod.rs
@@ -973,8 +973,9 @@ impl Term {
}
}
+ let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
let selection = self.grid.selection.clone()?;
- let span = selection.to_span(self)?;
+ let span = selection.to_span(self, alt_screen)?;
let mut res = String::new();
@@ -1058,8 +1059,9 @@ impl Term {
config: &'b Config,
window_focused: bool,
) -> RenderableCellsIter {
+ let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
let selection = self.grid.selection.as_ref()
- .and_then(|s| s.to_span(self))
+ .and_then(|s| s.to_span(self, alt_screen))
.map(|span| {
span.to_locations()
});