2016-12-22 13:43:06 -05:00
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
//! State management for a selection in the grid
|
|
|
|
//!
|
|
|
|
//! A selection should start when the mouse is clicked, and it should be
|
|
|
|
//! 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.
|
2017-10-12 23:59:21 -04:00
|
|
|
use std::ops::Range;
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2019-03-30 12:48:36 -04:00
|
|
|
use crate::index::{Column, Point, Side};
|
2019-04-29 10:33:25 -04:00
|
|
|
use crate::term::cell::Flags;
|
|
|
|
use crate::term::{Search, Term};
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2017-06-16 00:43:28 -04:00
|
|
|
/// Describes a region of a 2-dimensional area
|
2016-12-22 13:43:06 -05:00
|
|
|
///
|
2017-06-16 00:43:28 -04:00
|
|
|
/// 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
|
|
|
|
/// selected without any expansion. [`semantic`] mode expands the initial selection to the nearest
|
|
|
|
/// semantic escape char in either direction. [`lines`] will always select entire lines.
|
|
|
|
///
|
|
|
|
/// Calls to [`update`] operate different based on the selection kind. The [`simple`] mode does
|
|
|
|
/// nothing special, simply tracks points and sides. [`semantic`] will continue to expand out to
|
|
|
|
/// semantic boundaries as the selection point changes. Similarly, [`lines`] will always expand the
|
|
|
|
/// new point to encompass entire lines.
|
|
|
|
///
|
|
|
|
/// [`simple`]: enum.Selection.html#method.simple
|
|
|
|
/// [`semantic`]: enum.Selection.html#method.semantic
|
|
|
|
/// [`lines`]: enum.Selection.html#method.lines
|
2018-04-28 10:15:21 -04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2016-12-22 13:43:06 -05:00
|
|
|
pub enum Selection {
|
2017-06-16 00:43:28 -04:00
|
|
|
Simple {
|
|
|
|
/// The region representing start and end of cursor movement
|
2017-10-12 23:59:21 -04:00
|
|
|
region: Range<Anchor>,
|
2016-12-22 13:43:06 -05:00
|
|
|
},
|
2017-06-16 00:43:28 -04:00
|
|
|
Semantic {
|
|
|
|
/// The region representing start and end of cursor movement
|
2018-10-20 18:30:59 -04:00
|
|
|
region: Range<Point<isize>>,
|
2017-06-16 00:43:28 -04:00
|
|
|
},
|
|
|
|
Lines {
|
|
|
|
/// The region representing start and end of cursor movement
|
2018-10-20 18:30:59 -04:00
|
|
|
region: Range<Point<isize>>,
|
2017-06-16 00:43:28 -04:00
|
|
|
|
|
|
|
/// The line under the initial point. This is always selected regardless
|
|
|
|
/// of which way the cursor is moved.
|
2019-03-30 12:48:36 -04:00
|
|
|
initial_line: isize,
|
|
|
|
},
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A Point and side within that point.
|
2018-04-28 10:15:21 -04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2017-06-16 00:43:28 -04:00
|
|
|
pub struct Anchor {
|
2018-10-20 18:30:59 -04:00
|
|
|
point: Point<isize>,
|
2017-06-16 00:43:28 -04:00
|
|
|
side: Side,
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
2017-06-16 00:43:28 -04:00
|
|
|
impl Anchor {
|
2018-10-20 18:30:59 -04:00
|
|
|
fn new(point: Point<isize>, side: Side) -> Anchor {
|
2018-03-04 17:40:15 -05:00
|
|
|
Anchor { point, side }
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 00:43:28 -04:00
|
|
|
/// A type that has 2-dimensional boundaries
|
|
|
|
pub trait Dimensions {
|
|
|
|
/// Get the size of the area
|
|
|
|
fn dimensions(&self) -> Point;
|
|
|
|
}
|
|
|
|
|
2016-12-22 13:43:06 -05:00
|
|
|
impl Selection {
|
2018-03-06 23:57:40 -05:00
|
|
|
pub fn simple(location: Point<usize>, side: Side) -> Selection {
|
2017-06-16 00:43:28 -04:00
|
|
|
Selection::Simple {
|
2017-10-12 23:59:21 -04:00
|
|
|
region: Range {
|
2018-10-20 18:30:59 -04:00
|
|
|
start: Anchor::new(location.into(), side),
|
2019-03-30 12:48:36 -04:00
|
|
|
end: Anchor::new(location.into(), side),
|
|
|
|
},
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
2018-03-06 23:57:40 -05:00
|
|
|
pub fn rotate(&mut self, offset: isize) {
|
|
|
|
match *self {
|
|
|
|
Selection::Simple { ref mut region } => {
|
2018-10-20 18:30:59 -04:00
|
|
|
region.start.point.line += offset;
|
|
|
|
region.end.point.line += offset;
|
2018-03-06 23:57:40 -05:00
|
|
|
},
|
2018-05-30 00:45:28 -04:00
|
|
|
Selection::Semantic { ref mut region } => {
|
2018-10-20 18:30:59 -04:00
|
|
|
region.start.line += offset;
|
|
|
|
region.end.line += offset;
|
2018-03-06 23:57:40 -05:00
|
|
|
},
|
|
|
|
Selection::Lines { ref mut region, ref mut initial_line } => {
|
2018-10-20 18:30:59 -04:00
|
|
|
region.start.line += offset;
|
|
|
|
region.end.line += offset;
|
|
|
|
*initial_line += offset;
|
2019-03-30 12:48:36 -04:00
|
|
|
},
|
2018-03-06 23:57:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-30 00:45:28 -04:00
|
|
|
pub fn semantic(point: Point<usize>) -> Selection {
|
2019-03-30 12:48:36 -04:00
|
|
|
Selection::Semantic { region: Range { start: point.into(), end: point.into() } }
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
2018-03-06 23:57:40 -05:00
|
|
|
pub fn lines(point: Point<usize>) -> Selection {
|
2017-06-16 00:43:28 -04:00
|
|
|
Selection::Lines {
|
2019-03-30 12:48:36 -04:00
|
|
|
region: Range { start: point.into(), end: point.into() },
|
2018-10-20 18:30:59 -04:00
|
|
|
initial_line: point.line as isize,
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-06 23:57:40 -05:00
|
|
|
pub fn update(&mut self, location: Point<usize>, side: Side) {
|
2017-06-16 00:43:28 -04:00
|
|
|
// Always update the `end`; can normalize later during span generation.
|
|
|
|
match *self {
|
|
|
|
Selection::Simple { ref mut region } => {
|
2018-10-20 18:30:59 -04:00
|
|
|
region.end = Anchor::new(location.into(), side);
|
2017-06-16 00:43:28 -04:00
|
|
|
},
|
2019-03-30 12:48:36 -04:00
|
|
|
Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => {
|
2018-10-20 18:30:59 -04:00
|
|
|
region.end = location.into();
|
2016-12-22 13:43:06 -05:00
|
|
|
},
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
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, .. } => {
|
|
|
|
(region.start, region.end)
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Order the start/end
|
|
|
|
let needs_swap = Selection::points_need_swap(start, end);
|
|
|
|
if needs_swap {
|
|
|
|
std::mem::swap(&mut start, &mut end);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clamp to visible region in grid/normal
|
|
|
|
let cols = term.dimensions().col;
|
|
|
|
let lines = term.dimensions().line.0 as isize;
|
|
|
|
let (start, end) = Selection::grid_clamp(start, end, lines, cols)?;
|
|
|
|
|
2019-04-29 10:33:25 -04:00
|
|
|
let span = match *self {
|
2019-06-06 09:04:12 -04:00
|
|
|
Selection::Simple { ref region } if needs_swap => {
|
|
|
|
Selection::span_simple(term, start, end, region.end.side, region.start.side)
|
2016-12-22 13:43:06 -05:00
|
|
|
},
|
2019-06-06 09:04:12 -04:00
|
|
|
Selection::Simple { ref region } => {
|
|
|
|
Selection::span_simple(term, start, end, region.start.side, region.end.side)
|
2019-03-30 12:48:36 -04:00
|
|
|
},
|
2019-06-06 09:04:12 -04:00
|
|
|
Selection::Semantic { .. } => Selection::span_semantic(term, start, end),
|
|
|
|
Selection::Lines { .. } => Selection::span_lines(term, start, end),
|
2019-04-29 10:33:25 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Expand selection across double-width cells
|
|
|
|
span.map(|mut span| {
|
|
|
|
let grid = term.grid();
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
if span.end.col < cols
|
2019-04-29 10:33:25 -04:00
|
|
|
&& grid[span.end.line][span.end.col].flags.contains(Flags::WIDE_CHAR_SPACER)
|
|
|
|
{
|
|
|
|
span.end.col = Column(span.end.col.saturating_sub(1));
|
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
if span.start.col.0 < cols.saturating_sub(1)
|
2019-04-29 10:33:25 -04:00
|
|
|
&& grid[span.start.line][span.start.col].flags.contains(Flags::WIDE_CHAR)
|
|
|
|
{
|
|
|
|
span.start.col += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
span
|
|
|
|
})
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
2018-10-20 18:30:59 -04:00
|
|
|
|
2019-03-30 12:48:36 -04:00
|
|
|
pub fn is_empty(&self) -> bool {
|
2018-10-22 15:39:26 -04:00
|
|
|
match *self {
|
|
|
|
Selection::Simple { ref region } => {
|
|
|
|
region.start == region.end && region.start.side == region.end.side
|
|
|
|
},
|
2019-03-30 12:48:36 -04:00
|
|
|
Selection::Semantic { .. } | Selection::Lines { .. } => false,
|
2018-10-22 15:39:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
fn span_semantic<T>(term: &T, start: Point<isize>, end: Point<isize>) -> Option<Span>
|
2019-03-30 12:48:36 -04:00
|
|
|
where
|
2019-06-06 09:04:12 -04:00
|
|
|
T: Search + Dimensions,
|
2017-06-16 00:43:28 -04:00
|
|
|
{
|
2019-06-06 09:04:12 -04:00
|
|
|
let (start, end) = if start == end {
|
|
|
|
if let Some(end) = term.bracket_search(start.into()) {
|
2019-05-11 12:15:32 -04:00
|
|
|
(start.into(), end)
|
|
|
|
} else {
|
2019-06-06 09:04:12 -04:00
|
|
|
(term.semantic_search_right(start.into()), term.semantic_search_left(end.into()))
|
2019-05-11 12:15:32 -04:00
|
|
|
}
|
2018-03-06 23:57:40 -05:00
|
|
|
} else {
|
2019-06-06 09:04:12 -04:00
|
|
|
(term.semantic_search_right(start.into()), term.semantic_search_left(end.into()))
|
2018-04-02 11:44:54 -04:00
|
|
|
};
|
2017-06-16 00:43:28 -04:00
|
|
|
|
2019-04-29 10:33:25 -04:00
|
|
|
Some(Span { start, end })
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
fn span_lines<T>(term: &T, mut start: Point<isize>, mut end: Point<isize>) -> Option<Span>
|
2018-10-20 18:30:59 -04:00
|
|
|
where
|
2019-06-06 09:04:12 -04:00
|
|
|
T: Dimensions,
|
2017-06-16 00:43:28 -04:00
|
|
|
{
|
2019-06-06 09:04:12 -04:00
|
|
|
start.col = term.dimensions().col - 1;
|
|
|
|
end.col = Column(0);
|
2018-10-20 18:30:59 -04:00
|
|
|
|
2019-04-29 10:33:25 -04:00
|
|
|
Some(Span { start: start.into(), end: end.into() })
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
fn span_simple<T>(
|
|
|
|
term: &T,
|
|
|
|
mut start: Point<isize>,
|
|
|
|
mut end: Point<isize>,
|
|
|
|
start_side: Side,
|
|
|
|
end_side: Side,
|
|
|
|
) -> Option<Span>
|
2018-10-20 18:30:59 -04:00
|
|
|
where
|
2019-06-06 09:04:12 -04:00
|
|
|
T: Dimensions,
|
2018-10-20 18:30:59 -04:00
|
|
|
{
|
2018-03-25 15:09:18 -04:00
|
|
|
// No selection for single cell with identical sides or two cell with right+left sides
|
2019-04-29 10:33:25 -04:00
|
|
|
if (start == end && start_side == end_side)
|
|
|
|
|| (end_side == Side::Right
|
|
|
|
&& start_side == Side::Left
|
|
|
|
&& start.line == end.line
|
|
|
|
&& start.col == end.col + 1)
|
2018-03-25 15:09:18 -04:00
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2018-03-15 15:13:00 -04:00
|
|
|
// Remove last cell if selection ends to the left of a cell
|
2019-04-29 10:33:25 -04:00
|
|
|
if start_side == Side::Left && start != end {
|
2018-03-15 15:13:00 -04:00
|
|
|
// Special case when selection starts to left of first cell
|
2019-04-29 10:33:25 -04:00
|
|
|
if start.col == Column(0) {
|
2019-06-06 09:04:12 -04:00
|
|
|
start.col = term.dimensions().col - 1;
|
2019-04-29 10:33:25 -04:00
|
|
|
start.line += 1;
|
2018-07-21 13:17:41 -04:00
|
|
|
} else {
|
2019-04-29 10:33:25 -04:00
|
|
|
start.col -= 1;
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-15 15:13:00 -04:00
|
|
|
// Remove first cell if selection starts at the right of a cell
|
2019-04-29 10:33:25 -04:00
|
|
|
if end_side == Side::Right && start != end {
|
|
|
|
end.col += 1;
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
|
|
|
|
2018-03-15 15:13:00 -04:00
|
|
|
// Return the selection with all cells inclusive
|
2019-04-29 10:33:25 -04:00
|
|
|
Some(Span { start: start.into(), end: end.into() })
|
2017-06-16 00:43:28 -04:00
|
|
|
}
|
2018-10-20 18:30:59 -04:00
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
// 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>,
|
2018-10-20 18:30:59 -04:00
|
|
|
lines: isize,
|
|
|
|
cols: Column,
|
2019-06-06 09:04:12 -04:00
|
|
|
) -> Option<(Point<isize>, Point<isize>)> {
|
2019-04-29 10:33:25 -04:00
|
|
|
if end.line >= lines {
|
2018-10-20 18:30:59 -04:00
|
|
|
// Don't show selection above visible region
|
2019-04-29 10:33:25 -04:00
|
|
|
if start.line >= lines {
|
2018-10-20 18:30:59 -04:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clamp selection above viewport to visible region
|
2019-04-29 10:33:25 -04:00
|
|
|
end.line = lines - 1;
|
|
|
|
end.col = Column(0);
|
2018-10-20 18:30:59 -04:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:33:25 -04:00
|
|
|
if start.line < 0 {
|
2018-10-20 18:30:59 -04:00
|
|
|
// Don't show selection below visible region
|
2019-04-29 10:33:25 -04:00
|
|
|
if end.line < 0 {
|
2018-10-20 18:30:59 -04:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clamp selection below viewport to visible region
|
2019-04-29 10:33:25 -04:00
|
|
|
start.line = 0;
|
|
|
|
start.col = cols - 1;
|
2018-10-20 18:30:59 -04:00
|
|
|
}
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
Some((start, end))
|
2018-10-20 18:30:59 -04:00
|
|
|
}
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents a span of selected cells
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub struct Span {
|
2018-03-06 23:57:40 -05:00
|
|
|
/// Start point from bottom of buffer
|
|
|
|
pub start: Point<usize>,
|
|
|
|
/// End point towards top of buffer
|
|
|
|
pub end: Point<usize>,
|
|
|
|
}
|
|
|
|
|
2016-12-22 13:43:06 -05:00
|
|
|
/// 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
|
2017-10-30 11:03:58 -04:00
|
|
|
/// cells that are completely covered are counted in a selection. Ends are
|
2016-12-22 13:43:06 -05:00
|
|
|
/// represented by `B` and `E` for begin and end, respectively. A selected cell
|
|
|
|
/// looks like [XX], [BX] (at the start), [XB] (at the end), [XE] (at the end),
|
|
|
|
/// and [EX] (at the start), or [BE] for a single cell. Partially selected cells
|
|
|
|
/// look like [ B] and [E ].
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2019-04-29 10:33:25 -04:00
|
|
|
use std::mem;
|
2017-06-17 13:29:59 -04:00
|
|
|
|
2019-04-29 10:33:25 -04:00
|
|
|
use super::{Selection, Span};
|
|
|
|
use crate::clipboard::Clipboard;
|
|
|
|
use crate::grid::Grid;
|
|
|
|
use crate::index::{Column, Line, Point, Side};
|
|
|
|
use crate::message_bar::MessageBuffer;
|
|
|
|
use crate::term::cell::{Cell, Flags};
|
|
|
|
use crate::term::{SizeInfo, Term};
|
|
|
|
|
|
|
|
fn term(width: usize, height: usize) -> Term {
|
|
|
|
let size = SizeInfo {
|
|
|
|
width: width as f32,
|
|
|
|
height: height as f32,
|
|
|
|
cell_width: 1.0,
|
|
|
|
cell_height: 1.0,
|
|
|
|
padding_x: 0.0,
|
|
|
|
padding_y: 0.0,
|
|
|
|
dpr: 1.0,
|
|
|
|
};
|
|
|
|
Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop())
|
2017-06-17 13:29:59 -04:00
|
|
|
}
|
|
|
|
|
2016-12-22 13:43:06 -05:00
|
|
|
/// Test case of single cell selection
|
|
|
|
///
|
|
|
|
/// 1. [ ]
|
|
|
|
/// 2. [B ]
|
|
|
|
/// 3. [BE]
|
|
|
|
#[test]
|
|
|
|
fn single_cell_left_to_right() {
|
2018-03-06 23:57:40 -05:00
|
|
|
let location = Point { line: 0, col: Column(0) };
|
2017-06-17 13:29:59 -04:00
|
|
|
let mut selection = Selection::simple(location, Side::Left);
|
2016-12-22 13:43:06 -05:00
|
|
|
selection.update(location, Side::Right);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: location,
|
|
|
|
end: location
|
2016-12-22 13:43:06 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test case of single cell selection
|
|
|
|
///
|
|
|
|
/// 1. [ ]
|
|
|
|
/// 2. [ B]
|
|
|
|
/// 3. [EB]
|
|
|
|
#[test]
|
|
|
|
fn single_cell_right_to_left() {
|
2018-03-06 23:57:40 -05:00
|
|
|
let location = Point { line: 0, col: Column(0) };
|
2017-06-17 13:29:59 -04:00
|
|
|
let mut selection = Selection::simple(location, Side::Right);
|
2016-12-22 13:43:06 -05:00
|
|
|
selection.update(location, Side::Left);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(1, 1)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: location,
|
|
|
|
end: location
|
2016-12-22 13:43:06 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test adjacent cell selection from left to right
|
|
|
|
///
|
|
|
|
/// 1. [ ][ ]
|
|
|
|
/// 2. [ B][ ]
|
|
|
|
/// 3. [ B][E ]
|
|
|
|
#[test]
|
|
|
|
fn between_adjacent_cells_left_to_right() {
|
2018-03-06 23:57:40 -05:00
|
|
|
let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right);
|
|
|
|
selection.update(Point::new(0, Column(1)), Side::Left);
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(2, 1)), None);
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Test adjacent cell selection from right to left
|
|
|
|
///
|
|
|
|
/// 1. [ ][ ]
|
|
|
|
/// 2. [ ][B ]
|
|
|
|
/// 3. [ E][B ]
|
|
|
|
#[test]
|
|
|
|
fn between_adjacent_cells_right_to_left() {
|
2018-03-06 23:57:40 -05:00
|
|
|
let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left);
|
|
|
|
selection.update(Point::new(0, Column(0)), Side::Right);
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(2, 1)), None);
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Test selection across adjacent lines
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// 1. [ ][ ][ ][ ][ ]
|
|
|
|
/// [ ][ ][ ][ ][ ]
|
2018-03-25 15:09:18 -04:00
|
|
|
/// 2. [ ][ B][ ][ ][ ]
|
|
|
|
/// [ ][ ][ ][ ][ ]
|
|
|
|
/// 3. [ ][ B][XX][XX][XX]
|
|
|
|
/// [XX][XE][ ][ ][ ]
|
2016-12-22 13:43:06 -05:00
|
|
|
#[test]
|
|
|
|
fn across_adjacent_lines_upward_final_cell_exclusive() {
|
2018-03-06 23:57:40 -05:00
|
|
|
let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right);
|
|
|
|
selection.update(Point::new(0, Column(1)), Side::Right);
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(1)),
|
|
|
|
end: Point::new(1, Column(2)),
|
2016-12-22 13:43:06 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test selection across adjacent lines
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// 1. [ ][ ][ ][ ][ ]
|
|
|
|
/// [ ][ ][ ][ ][ ]
|
2018-03-25 15:09:18 -04:00
|
|
|
/// 2. [ ][ ][ ][ ][ ]
|
|
|
|
/// [ ][ B][ ][ ][ ]
|
|
|
|
/// 3. [ ][ E][XX][XX][XX]
|
|
|
|
/// [XX][XB][ ][ ][ ]
|
|
|
|
/// 4. [ E][XX][XX][XX][XX]
|
|
|
|
/// [XX][XB][ ][ ][ ]
|
2016-12-22 13:43:06 -05:00
|
|
|
#[test]
|
|
|
|
fn selection_bigger_then_smaller() {
|
2018-03-06 23:57:40 -05:00
|
|
|
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);
|
2016-12-22 13:43:06 -05:00
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(5, 2)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(1)),
|
|
|
|
end: Point::new(1, Column(1)),
|
2016-12-22 13:43:06 -05:00
|
|
|
});
|
|
|
|
}
|
2018-10-20 18:30:59 -04:00
|
|
|
|
|
|
|
#[test]
|
2019-06-06 09:04:12 -04:00
|
|
|
fn alt_screen_lines() {
|
2018-10-20 18:30:59 -04:00
|
|
|
let mut selection = Selection::lines(Point::new(0, Column(0)));
|
|
|
|
selection.update(Point::new(5, Column(3)), Side::Right);
|
|
|
|
selection.rotate(-3);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(4)),
|
|
|
|
end: Point::new(2, Column(0)),
|
2018-10-20 18:30:59 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(4)),
|
|
|
|
end: Point::new(2, Column(3)),
|
2018-10-20 18:30:59 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term(5, 10)).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(4)),
|
|
|
|
end: Point::new(2, Column(4)),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn double_width_expansion() {
|
|
|
|
let mut term = term(10, 1);
|
|
|
|
let mut grid = Grid::new(Line(1), Column(10), 0, Cell::default());
|
|
|
|
grid[Line(0)][Column(0)].flags.insert(Flags::WIDE_CHAR);
|
|
|
|
grid[Line(0)][Column(1)].flags.insert(Flags::WIDE_CHAR_SPACER);
|
|
|
|
grid[Line(0)][Column(8)].flags.insert(Flags::WIDE_CHAR);
|
|
|
|
grid[Line(0)][Column(9)].flags.insert(Flags::WIDE_CHAR_SPACER);
|
|
|
|
mem::swap(term.grid_mut(), &mut grid);
|
|
|
|
|
|
|
|
let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left);
|
|
|
|
selection.update(Point::new(0, Column(8)), Side::Right);
|
|
|
|
|
2019-06-06 09:04:12 -04:00
|
|
|
assert_eq!(selection.to_span(&term).unwrap(), Span {
|
2019-04-29 10:33:25 -04:00
|
|
|
start: Point::new(0, Column(9)),
|
|
|
|
end: Point::new(0, Column(0)),
|
2018-10-20 18:30:59 -04:00
|
|
|
});
|
|
|
|
}
|
2016-12-22 13:43:06 -05:00
|
|
|
}
|