aboutsummaryrefslogtreecommitdiff
path: root/alacritty_terminal/src/selection.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty_terminal/src/selection.rs')
-rw-r--r--alacritty_terminal/src/selection.rs255
1 files changed, 175 insertions, 80 deletions
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs
index 7dd0be74..132c6919 100644
--- a/alacritty_terminal/src/selection.rs
+++ b/alacritty_terminal/src/selection.rs
@@ -20,7 +20,7 @@
//! also be cleared if the user clicks off of the selection.
use std::ops::Range;
-use crate::index::{Column, Point, Side};
+use crate::index::{Column, Line, Point, Side};
use crate::term::cell::Flags;
use crate::term::{Search, Term};
@@ -45,6 +45,10 @@ pub enum Selection {
/// The region representing start and end of cursor movement
region: Range<Anchor>,
},
+ Block {
+ /// 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>>,
@@ -52,10 +56,6 @@ pub enum Selection {
Lines {
/// The region representing start and end of cursor movement
region: Range<Point<isize>>,
-
- /// The line under the initial point. This is always selected regardless
- /// of which way the cursor is moved.
- initial_line: isize,
},
}
@@ -79,6 +79,19 @@ pub trait Dimensions {
}
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 {
@@ -88,20 +101,11 @@ impl Selection {
}
}
- pub fn rotate(&mut self, offset: isize) {
- match *self {
- Selection::Simple { ref mut region } => {
- region.start.point.line += offset;
- region.end.point.line += offset;
- },
- Selection::Semantic { ref mut region } => {
- region.start.line += offset;
- region.end.line += offset;
- },
- Selection::Lines { ref mut region, ref mut initial_line } => {
- region.start.line += offset;
- region.end.line += offset;
- *initial_line += offset;
+ 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),
},
}
}
@@ -111,29 +115,49 @@ impl Selection {
}
pub fn lines(point: Point<usize>) -> Selection {
- Selection::Lines {
- region: Range { start: point.into(), end: point.into() },
- initial_line: point.line as isize,
- }
+ Selection::Lines { region: Range { start: point.into(), end: point.into() } }
}
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::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, .. } => {
+ Selection::Semantic { ref mut region } | Selection::Lines { ref mut region } => {
region.end = location.into();
},
}
}
+ pub fn is_empty(&self) -> bool {
+ match *self {
+ Selection::Simple { ref region } | Selection::Block { ref region } => {
+ let (start, end) =
+ if Selection::points_need_swap(region.start.point, region.end.point) {
+ (&region.end, &region.start)
+ } else {
+ (&region.start, &region.end)
+ };
+
+ // Empty when single cell with identical sides or two cell with right+left sides
+ start == end
+ || (start.side == Side::Left
+ && end.side == Side::Right
+ && start.point.line == end.point.line
+ && start.point.col == end.point.col + 1)
+ },
+ Selection::Semantic { .. } | Selection::Lines { .. } => false,
+ }
+ }
+
pub fn to_span(&self, term: &Term) -> Option<Span> {
// Get both sides of the selection
let (mut start, mut end) = match *self {
- Selection::Simple { ref region } => (region.start.point, region.end.point),
- Selection::Semantic { ref region } | Selection::Lines { ref region, .. } => {
+ 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)
},
};
@@ -150,11 +174,23 @@ impl Selection {
let (start, end) = Selection::grid_clamp(start, end, lines, cols)?;
let span = match *self {
- Selection::Simple { ref region } if needs_swap => {
- Selection::span_simple(term, start, end, region.end.side, region.start.side)
- },
Selection::Simple { ref region } => {
- Selection::span_simple(term, start, end, region.start.side, region.end.side)
+ let (start_side, end_side) = if needs_swap {
+ (region.end.side, region.start.side)
+ } else {
+ (region.start.side, region.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)
+ };
+
+ 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),
@@ -180,13 +216,41 @@ impl Selection {
})
}
- pub fn is_empty(&self) -> bool {
- match *self {
- Selection::Simple { ref region } => {
- region.start == region.end && region.start.side == region.end.side
- },
- Selection::Semantic { .. } | Selection::Lines { .. } => false,
+ // Bring start and end points in the correct order
+ fn points_need_swap(start: Point<isize>, end: Point<isize>) -> bool {
+ start.line > end.line || start.line == end.line && start.col < end.col
+ }
+
+ // Clamp selection inside the grid to prevent out of bounds errors
+ fn grid_clamp(
+ mut start: Point<isize>,
+ mut end: Point<isize>,
+ lines: isize,
+ cols: Column,
+ ) -> Option<(Point<isize>, Point<isize>)> {
+ if end.line >= lines {
+ // Don't show selection above visible region
+ if start.line >= lines {
+ return None;
+ }
+
+ // Clamp selection above viewport to visible region
+ end.line = lines - 1;
+ end.col = Column(0);
}
+
+ if start.line < 0 {
+ // Don't show selection below visible region
+ if end.line < 0 {
+ return None;
+ }
+
+ // Clamp selection below viewport to visible region
+ start.line = 0;
+ start.col = cols - 1;
+ }
+
+ Some((start, end))
}
fn span_semantic<T>(term: &T, start: Point<isize>, end: Point<isize>) -> Option<Span>
@@ -203,7 +267,7 @@ impl Selection {
(term.semantic_search_right(start.into()), term.semantic_search_left(end.into()))
};
- Some(Span { start, end })
+ Some(Span { start, end, is_block: false })
}
fn span_lines<T>(term: &T, mut start: Point<isize>, mut end: Point<isize>) -> Option<Span>
@@ -213,10 +277,11 @@ impl Selection {
start.col = term.dimensions().col - 1;
end.col = Column(0);
- Some(Span { start: start.into(), end: end.into() })
+ Some(Span { start: start.into(), end: end.into(), is_block: false })
}
fn span_simple<T>(
+ &self,
term: &T,
mut start: Point<isize>,
mut end: Point<isize>,
@@ -226,13 +291,7 @@ impl Selection {
where
T: Dimensions,
{
- // No selection for single cell with identical sides or two cell with right+left sides
- if (start == end && start_side == end_side)
- || (end_side == Side::Right
- && start_side == Side::Left
- && start.line == end.line
- && start.col == end.col + 1)
- {
+ if self.is_empty() {
return None;
}
@@ -253,54 +312,69 @@ impl Selection {
}
// Return the selection with all cells inclusive
- Some(Span { start: start.into(), end: end.into() })
- }
-
- // Bring start and end points in the correct order
- fn points_need_swap(start: Point<isize>, end: Point<isize>) -> bool {
- start.line > end.line || start.line == end.line && start.col <= end.col
+ Some(Span { start: start.into(), end: end.into(), is_block: false })
}
- // Clamp selection inside the grid to prevent out of bounds errors
- fn grid_clamp(
+ fn span_block(
+ &self,
mut start: Point<isize>,
mut end: Point<isize>,
- lines: isize,
- cols: Column,
- ) -> Option<(Point<isize>, Point<isize>)> {
- if end.line >= lines {
- // Don't show selection above visible region
- if start.line >= lines {
- return None;
- }
+ mut start_side: Side,
+ mut end_side: Side,
+ ) -> Option<Span> {
+ if self.is_empty() {
+ return None;
+ }
- // Clamp selection above viewport to visible region
- end.line = lines - 1;
- end.col = Column(0);
+ // Always go bottom-right -> top-left
+ 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.line < 0 {
- // Don't show selection below visible region
- if end.line < 0 {
- return None;
- }
+ // Remove last cell if selection ends to the left of a cell
+ if start_side == Side::Left && start != end && start.col.0 > 0 {
+ start.col -= 1;
+ }
- // Clamp selection below viewport to visible region
- start.line = 0;
- start.col = cols - 1;
+ // Remove first cell if selection starts at the right of a cell
+ if end_side == Side::Right && start != end {
+ end.col += 1;
}
- Some((start, end))
+ // Return the selection with all cells inclusive
+ Some(Span { start: start.into(), end: end.into(), is_block: true })
}
}
/// Represents a span of selected cells
-#[derive(Debug, Eq, PartialEq)]
+#[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,
+}
+
+pub struct SelectionRange {
+ start: Point,
+ end: Point,
+ is_block: bool,
+}
+
+impl SelectionRange {
+ pub fn new(start: Point, end: Point, is_block: bool) -> Self {
+ Self { start, end, is_block }
+ }
+
+ 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))
+ }
}
/// Tests for selection
@@ -350,7 +424,8 @@ mod test {
assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span {
start: location,
- end: location
+ end: location,
+ is_block: false,
});
}
@@ -367,7 +442,8 @@ mod test {
assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span {
start: location,
- end: location
+ end: location,
+ is_block: false,
});
}
@@ -414,6 +490,7 @@ mod test {
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
start: Point::new(0, Column(1)),
end: Point::new(1, Column(2)),
+ is_block: false,
});
}
@@ -437,11 +514,12 @@ mod test {
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
start: Point::new(0, Column(1)),
end: Point::new(1, Column(1)),
+ is_block: false,
});
}
#[test]
- fn alt_screen_lines() {
+ 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);
@@ -449,11 +527,12 @@ mod test {
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
start: Point::new(0, Column(4)),
end: Point::new(2, Column(0)),
+ is_block: false,
});
}
#[test]
- fn alt_screen_semantic() {
+ 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);
@@ -461,11 +540,12 @@ mod test {
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
start: Point::new(0, Column(4)),
end: Point::new(2, Column(3)),
+ is_block: false,
});
}
#[test]
- fn alt_screen_simple() {
+ 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);
@@ -473,6 +553,20 @@ mod test {
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
start: Point::new(0, Column(4)),
end: Point::new(2, Column(4)),
+ 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);
+
+ assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
+ start: Point::new(0, Column(4)),
+ end: Point::new(2, Column(4)),
+ is_block: true,
});
}
@@ -492,6 +586,7 @@ mod test {
assert_eq!(selection.to_span(&term).unwrap(), Span {
start: Point::new(0, Column(9)),
end: Point::new(0, Column(0)),
+ is_block: false,
});
}
}