summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Duerr <chrisduerr@users.noreply.github.com>2019-06-20 15:56:09 +0000
committerGitHub <noreply@github.com>2019-06-20 15:56:09 +0000
commite0a286515f12c6ceed53c74df1c10123cb0b550d (patch)
treee24c9fc28984cdc577fd87b47c36d72c82ab3368
parenta1c70b1d68f192c3e6901095f646e17a93774746 (diff)
downloadalacritty-e0a286515f12c6ceed53c74df1c10123cb0b550d.tar.gz
alacritty-e0a286515f12c6ceed53c74df1c10123cb0b550d.zip
Add block selection
This implements a block selection mode which can be triggered by holding Control before starting a selection. If text is copied using this block selection, newlines will be automatically added to the end of the lines. This fixes #526.
-rw-r--r--CHANGELOG.md4
-rw-r--r--alacritty_terminal/src/event.rs6
-rw-r--r--alacritty_terminal/src/input.rs9
-rw-r--r--alacritty_terminal/src/selection.rs255
-rw-r--r--alacritty_terminal/src/term/mod.rs94
5 files changed, 236 insertions, 132 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08c7e774..cceb5d00 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- Block selection mode when Control is held while starting a selection
+
### Fixed
- GUI programs launched by Alacritty starting in the background on X11
diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs
index f844bf68..171f3ce7 100644
--- a/alacritty_terminal/src/event.rs
+++ b/alacritty_terminal/src/event.rs
@@ -102,6 +102,12 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
self.terminal.dirty = true;
}
+ fn block_selection(&mut self, point: Point, side: Side) {
+ let point = self.terminal.visible_to_buffer(point);
+ *self.terminal.selection_mut() = Some(Selection::block(point, side));
+ self.terminal.dirty = true;
+ }
+
fn semantic_selection(&mut self, point: Point) {
let point = self.terminal.visible_to_buffer(point);
*self.terminal.selection_mut() = Some(Selection::semantic(point));
diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs
index 17d427cd..b4ea5910 100644
--- a/alacritty_terminal/src/input.rs
+++ b/alacritty_terminal/src/input.rs
@@ -66,6 +66,7 @@ pub trait ActionContext {
fn clear_selection(&mut self);
fn update_selection(&mut self, point: Point, side: Side);
fn simple_selection(&mut self, point: Point, side: Side);
+ fn block_selection(&mut self, point: Point, side: Side);
fn semantic_selection(&mut self, point: Point);
fn line_selection(&mut self, point: Point);
fn selection_is_empty(&self) -> bool;
@@ -612,7 +613,11 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
// Start new empty selection
let side = self.ctx.mouse().cell_side;
if let Some(point) = point {
- self.ctx.simple_selection(point, side);
+ if modifiers.ctrl {
+ self.ctx.block_selection(point, side);
+ } else {
+ self.ctx.simple_selection(point, side);
+ }
}
let report_modes =
@@ -991,6 +996,8 @@ mod tests {
fn simple_selection(&mut self, _point: Point, _side: Side) {}
+ fn block_selection(&mut self, _point: Point, _side: Side) {}
+
fn copy_selection(&mut self, _: ClipboardType) {}
fn clear_selection(&mut self) {}
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,
});
}
}
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index f936b080..378b3156 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -35,7 +35,7 @@ use crate::grid::{
use crate::index::{self, Column, Contains, IndexRange, Line, Linear, Point};
use crate::input::FONT_SIZE_STEP;
use crate::message_bar::MessageBuffer;
-use crate::selection::{self, Selection, Span};
+use crate::selection::{self, Selection, SelectionRange, Span};
use crate::term::cell::{Cell, Flags, LineLength};
use crate::term::color::Rgb;
use crate::url::{Url, UrlParser};
@@ -215,7 +215,7 @@ pub struct RenderableCellsIter<'a> {
cursor_style: CursorStyle,
config: &'a Config,
colors: &'a color::List,
- selection: Option<RangeInclusive<index::Linear>>,
+ selection: Option<SelectionRange>,
url_highlight: &'a Option<RangeInclusive<index::Linear>>,
}
@@ -240,19 +240,23 @@ impl<'a> RenderableCellsIter<'a> {
let start_line = grid.buffer_line_to_visible(span.start.line);
let end_line = grid.buffer_line_to_visible(span.end.line);
+ // Limit block selection columns to within start/end points
+ let (limit_start, limit_end) =
+ if span.is_block { (span.start.col, span.end.col) } else { (Column(0), Column(0)) };
+
// Get start/end locations based on what part of selection is on screen
let locations = match (start_line, end_line) {
(ViewportPosition::Visible(start_line), ViewportPosition::Visible(end_line)) => {
Some((start_line, span.start.col, end_line, span.end.col))
},
(ViewportPosition::Visible(start_line), ViewportPosition::Above) => {
- Some((start_line, span.start.col, Line(0), Column(0)))
+ Some((start_line, span.start.col, Line(0), limit_end))
},
(ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
- Some((grid.num_lines(), Column(0), end_line, span.end.col))
+ Some((grid.num_lines(), limit_start, end_line, span.end.col))
},
(ViewportPosition::Below, ViewportPosition::Above) => {
- Some((grid.num_lines(), Column(0), Line(0), Column(0)))
+ Some((grid.num_lines(), limit_start, Line(0), limit_end))
},
_ => None,
};
@@ -267,11 +271,7 @@ impl<'a> RenderableCellsIter<'a> {
::std::mem::swap(&mut start, &mut end);
}
- let cols = grid.num_cols();
- let start = Linear::from_point(cols, start.into());
- let end = Linear::from_point(cols, end.into());
-
- RangeInclusive::new(start, end)
+ SelectionRange::new(start, end, span.is_block)
})
});
@@ -425,9 +425,11 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col {
- let index = Linear::new(self.grid.num_cols(), self.cursor.col, self.cursor.line);
- let selected =
- self.selection.as_ref().map(|range| range.contains_(index)).unwrap_or(false);
+ let selected = self
+ .selection
+ .as_ref()
+ .map(|range| range.contains(self.cursor.col, self.cursor.line))
+ .unwrap_or(false);
// Handle cursor
if let Some(cursor_key) = self.cursor_key.take() {
@@ -464,12 +466,14 @@ impl<'a> Iterator for RenderableCellsIter<'a> {
} else {
let mut cell = self.inner.next()?;
- let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
-
- let selected =
- self.selection.as_ref().map(|range| range.contains_(index)).unwrap_or(false);
+ let selected = self
+ .selection
+ .as_ref()
+ .map(|range| range.contains(cell.column, cell.line))
+ .unwrap_or(false);
// Underline URL highlights
+ let index = Linear::new(self.grid.num_cols(), cell.column, cell.line);
if self.url_highlight.as_ref().map(|range| range.contains_(index)).unwrap_or(false)
{
cell.inner.flags.insert(Flags::UNDERLINE);
@@ -987,28 +991,10 @@ impl Term {
}
pub fn selection_to_string(&self) -> Option<String> {
- /// Need a generic push() for the Append trait
- trait PushChar {
- fn push_char(&mut self, c: char);
- fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: Column) {
- if ending != Column(0)
- && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE)
- {
- self.push_char('\n');
- }
- }
- }
-
- impl PushChar for String {
- #[inline]
- fn push_char(&mut self, c: char) {
- self.push(c);
- }
- }
-
- trait Append: PushChar {
+ trait Append {
fn append(
&mut self,
+ append_newline: bool,
grid: &Grid<Cell>,
tabs: &TabStops,
line: usize,
@@ -1019,6 +1005,7 @@ impl Term {
impl Append for String {
fn append(
&mut self,
+ append_newline: bool,
grid: &Grid<Cell>,
tabs: &TabStops,
mut line: usize,
@@ -1031,9 +1018,7 @@ impl Term {
let line_length = grid_line.line_length();
let line_end = min(line_length, cols.end + 1);
- if line_end.0 == 0 && cols.end >= grid.num_cols() - 1 {
- self.push('\n');
- } else if cols.start < line_end {
+ if cols.start < line_end {
let mut tab_mode = false;
for col in IndexRange::from(cols.start..line_end) {
@@ -1059,16 +1044,20 @@ impl Term {
tab_mode = true;
}
}
+ }
- if cols.end >= grid.num_cols() - 1 {
- self.maybe_newline(grid, line, line_end);
- }
+ if append_newline
+ || (cols.end >= grid.num_cols() - 1
+ && (line_end == Column(0)
+ || !grid[line][line_end - 1].flags.contains(cell::Flags::WRAPLINE)))
+ {
+ self.push('\n');
}
}
}
let selection = self.grid.selection.clone()?;
- let Span { mut start, mut end } = selection.to_span(self)?;
+ let Span { mut start, mut end, is_block } = selection.to_span(self)?;
let mut res = String::new();
@@ -1077,35 +1066,38 @@ impl Term {
}
let line_count = end.line - start.line;
- let max_col = Column(usize::max_value() - 1);
+
+ // Setup block selection start/end point limits
+ let (limit_start, limit_end) =
+ if is_block { (end.col, start.col) } else { (Column(0), self.grid.num_cols()) };
match line_count {
// Selection within single line
0 => {
- res.append(&self.grid, &self.tabs, start.line, start.col..end.col);
+ res.append(false, &self.grid, &self.tabs, start.line, start.col..end.col);
},
// Selection ends on line following start
1 => {
// Ending line
- res.append(&self.grid, &self.tabs, end.line, end.col..max_col);
+ res.append(is_block, &self.grid, &self.tabs, end.line, end.col..limit_end);
// Starting line
- res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
+ res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col);
},
// Multi line selection
_ => {
// Ending line
- res.append(&self.grid, &self.tabs, end.line, end.col..max_col);
+ res.append(is_block, &self.grid, &self.tabs, end.line, end.col..limit_end);
let middle_range = (start.line + 1)..(end.line);
for line in middle_range.rev() {
- res.append(&self.grid, &self.tabs, line, Column(0)..max_col);
+ res.append(is_block, &self.grid, &self.tabs, line, limit_start..limit_end);
}
// Starting line
- res.append(&self.grid, &self.tabs, start.line, Column(0)..start.col);
+ res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col);
},
}