Fix performance issues with text reflow

Fixes #2567.
Fixes #2414.
This commit is contained in:
Christian Duerr 2019-06-23 23:29:01 +00:00 committed by GitHub
parent 743d5d9c66
commit f002171c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 617 additions and 724 deletions

View File

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- GUI programs launched by Alacritty starting in the background on X11
- Text Cursor position when scrolling
- Performance issues while resizing Alacritty
## 0.3.3

38
Cargo.lock generated
View File

@ -64,8 +64,8 @@ dependencies = [
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
"objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
"signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -465,7 +465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -508,8 +508,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -586,7 +586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -614,7 +614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -674,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1248,7 +1248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1786,17 +1786,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.92"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.92"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1806,7 +1806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1816,7 +1816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1971,7 +1971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.36"
version = "0.15.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1986,7 +1986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2671,8 +2671,8 @@ dependencies = [
"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be"
"checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e"
"checksum serde 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)" = "960e29cf7004b3b6e65fc5002981400eb3ccc017a08a2406940823e58e7179a9"
"checksum serde_derive 1.0.93 (registry+https://github.com/rust-lang/crates.io-index)" = "c4cce6663696bd38272e90bf34a0267e1226156c33f52d3f3915a2dd5d802085"
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
"checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9"
@ -2692,7 +2692,7 @@ dependencies = [
"checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241"
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 0.15.36 (registry+https://github.com/rust-lang/crates.io-index)" = "8b4f551a91e2e3848aeef8751d0d4eec9489b6474c720fd4c55958d8d31a430c"
"checksum syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e11410033fd5cf69a1cf2084604e011190c56f11e08ffc53df880f5f65f1c6e4"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
"checksum tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef"
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"

View File

@ -189,6 +189,7 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
pub fn resize(
&mut self,
reflow: bool,
lines: index::Line,
cols: index::Column,
cursor_pos: &mut Point,
@ -206,8 +207,8 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
}
match self.cols.cmp(&cols) {
Ordering::Less => self.grow_cols(cols, cursor_pos, template),
Ordering::Greater => self.shrink_cols(cols, template),
Ordering::Less => self.grow_cols(reflow, cols, cursor_pos, template),
Ordering::Greater => self.shrink_cols(reflow, cols, template),
Ordering::Equal => (),
}
}
@ -252,93 +253,107 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
self.display_offset = self.display_offset.saturating_sub(*lines_added);
}
fn grow_cols(&mut self, cols: index::Column, cursor_pos: &mut Point, template: &T) {
// Truncate all buffered lines
self.raw.grow_hidden(cols, template);
fn grow_cols(
&mut self,
reflow: bool,
cols: index::Column,
cursor_pos: &mut Point,
template: &T,
) {
let mut new_empty_lines = 0;
let mut new_raw: Vec<Row<T>> = Vec::with_capacity(self.raw.len());
for (i, mut row) in self.raw.drain().enumerate().rev() {
if let Some(last_row) = new_raw.last_mut() {
// Grow the current line if there's wrapped content available
if reflow
&& last_row.len() < cols.0
&& last_row.last().map(GridCell::is_wrap) == Some(true)
{
// Remove wrap flag before appending additional cells
if let Some(cell) = last_row.last_mut() {
cell.set_wrap(false);
}
let max_lines = self.lines.0 + self.max_scroll_limit;
// Append as many cells from the next line as possible
let len = min(row.len(), cols.0 - last_row.len());
let mut cells = row.front_split_off(len);
last_row.append(&mut cells);
// Iterate backwards with indices for mutation during iteration
let mut i = self.raw.len();
while i > 0 {
i -= 1;
if row.is_empty() {
let raw_len = i + 1 + new_raw.len();;
if raw_len < self.lines.0 || self.scroll_limit == 0 {
// Add new line and move lines up if we can't pull from history
cursor_pos.line = Line(cursor_pos.line.saturating_sub(1));
new_empty_lines += 1;
} else {
// Make sure viewport doesn't move if line is outside of the visible
// area
if i < self.display_offset {
self.display_offset = self.display_offset.saturating_sub(1);
}
// Grow the current line if there's wrapped content available
while i >= 1
&& self.raw[i].len() < cols.0
&& self.raw[i].last().map(GridCell::is_wrap) == Some(true)
{
// Remove wrap flag before appending additional cells
if let Some(cell) = self.raw[i].last_mut() {
cell.set_wrap(false);
}
// Append as many cells from the next line as possible
let len = min(self.raw[i - 1].len(), cols.0 - self.raw[i].len());
let mut cells = self.raw[i - 1].front_split_off(len);
self.raw[i].append(&mut cells);
if self.raw[i - 1].is_empty() {
// Remove following line if all cells have been drained
self.raw.remove(i - 1);
if self.raw.len() < self.lines.0 || self.scroll_limit == 0 {
// Add new line and move lines up if we can't pull from history
self.raw.insert(0, Row::new(cols, template), max_lines);
cursor_pos.line = Line(cursor_pos.line.saturating_sub(1));
} else {
// Make sure viewport doesn't move if line is outside of the visible area
if i < self.display_offset {
self.display_offset = self.display_offset.saturating_sub(1);
// Remove one line from scrollback, since we just moved it to the
// viewport
self.scroll_limit = self.scroll_limit.saturating_sub(1);
self.display_offset = min(self.display_offset, self.scroll_limit);
}
// Remove one line from scrollback, since we just moved it to the viewport
self.scroll_limit = self.scroll_limit.saturating_sub(1);
self.display_offset = min(self.display_offset, self.scroll_limit);
i -= 1;
// Don't push line into the new buffer
continue;
} else if let Some(cell) = last_row.last_mut() {
// Set wrap flag if next line still has cells
cell.set_wrap(true);
}
} else if let Some(cell) = self.raw[i].last_mut() {
// Set wrap flag if next line still has cells
cell.set_wrap(true);
}
}
// Fill remaining cells
if self.raw[i].len() < cols.0 {
self.raw[i].grow(cols, template);
}
new_raw.push(row);
}
// Add padding lines
new_raw.append(&mut vec![Row::new(cols, template); new_empty_lines]);
// Fill remaining cells and reverse iterator
let mut reversed = Vec::with_capacity(new_raw.len());
for mut row in new_raw.drain(..).rev() {
if row.len() < cols.0 {
row.grow(cols, template);
}
reversed.push(row);
}
self.raw.replace_inner(reversed);
self.cols = cols;
}
fn shrink_cols(&mut self, cols: index::Column, template: &T) {
// Truncate all buffered lines
self.raw.shrink_hidden(cols);
fn shrink_cols(&mut self, reflow: bool, cols: index::Column, template: &T) {
let mut new_raw = Vec::with_capacity(self.raw.len());
let mut buffered = None;
for (i, mut row) in self.raw.drain().enumerate().rev() {
if let Some(buffered) = buffered.take() {
row.append_front(buffered);
}
let max_lines = self.lines.0 + self.max_scroll_limit;
let mut wrapped = row.shrink(cols);
new_raw.push(row);
// Iterate backwards with indices for mutation during iteration
let mut i = self.raw.len();
while i > 0 {
i -= 1;
if let Some(mut new_row) = self.raw[i].shrink(cols) {
while let (Some(mut wrapped_cells), true) = (wrapped.take(), reflow) {
// Set line as wrapped if cells got removed
if let Some(cell) = self.raw[i].last_mut() {
if let Some(cell) = new_raw.last_mut().and_then(|r| r.last_mut()) {
cell.set_wrap(true);
}
if Some(true) == new_row.last().map(|c| c.is_wrap() && i >= 1)
&& new_row.len() < cols.0
if Some(true) == wrapped_cells.last().map(|c| c.is_wrap() && i >= 1)
&& wrapped_cells.len() < cols.0
{
// Make sure previous wrap flag doesn't linger around
if let Some(cell) = new_row.last_mut() {
if let Some(cell) = wrapped_cells.last_mut() {
cell.set_wrap(false);
}
// Add removed cells to start of next row
self.raw[i - 1].append_front(new_row);
buffered = Some(wrapped_cells);
} else {
// Make sure viewport doesn't move if line is outside of the visible area
if i < self.display_offset {
@ -346,24 +361,27 @@ impl<T: GridCell + Copy + Clone> Grid<T> {
}
// Make sure new row is at least as long as new width
let occ = new_row.len();
let occ = wrapped_cells.len();
if occ < cols.0 {
new_row.append(&mut vec![*template; cols.0 - occ]);
wrapped_cells.append(&mut vec![*template; cols.0 - occ]);
}
let row = Row::from_vec(new_row, occ);
let mut row = Row::from_vec(wrapped_cells, occ);
// Since inserted might exceed cols, we need to check it again
wrapped = row.shrink(cols);
// Add new row with all removed cells
self.raw.insert(i, row, max_lines);
new_raw.push(row);
// Increase scrollback history
self.scroll_limit = min(self.scroll_limit + 1, self.max_scroll_limit);
// Since inserted might exceed cols, we need to check the same line again
i += 1;
}
}
}
let mut reversed: Vec<Row<T>> = new_raw.drain(..).rev().collect();
reversed.truncate(self.max_scroll_limit + self.lines.0);
self.raw.replace_inner(reversed);
self.cols = cols;
}

File diff suppressed because it is too large Load Diff

View File

@ -140,7 +140,7 @@ fn shrink_reflow() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 3);
@ -166,8 +166,8 @@ fn shrink_reflow_twice() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
grid.resize(Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 3);
@ -193,7 +193,7 @@ fn shrink_reflow_empty_cell_inside_line() {
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = Cell::default();
grid.resize(Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 2);
@ -205,7 +205,7 @@ fn shrink_reflow_empty_cell_inside_line() {
assert_eq!(grid[0][Column(0)], cell('3'));
assert_eq!(grid[0][Column(1)], cell('4'));
grid.resize(Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 4);
@ -230,7 +230,7 @@ fn grow_reflow() {
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
grid.resize(Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 2);
@ -256,7 +256,7 @@ fn grow_reflow_multiline() {
grid[Line(2)][Column(0)] = cell('5');
grid[Line(2)][Column(1)] = cell('6');
grid.resize(Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default());
grid.resize(true, Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 3);
@ -279,6 +279,47 @@ fn grow_reflow_multiline() {
}
}
#[test]
fn grow_reflow_disabled() {
let mut grid = Grid::new(Line(2), Column(2), 0, cell('x'));
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = wrap_cell('2');
grid[Line(1)][Column(0)] = cell('3');
grid[Line(1)][Column(1)] = Cell::default();
grid.resize(false, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 2);
assert_eq!(grid[1].len(), 3);
assert_eq!(grid[1][Column(0)], cell('1'));
assert_eq!(grid[1][Column(1)], wrap_cell('2'));
assert_eq!(grid[1][Column(2)], Cell::default());
assert_eq!(grid[0].len(), 3);
assert_eq!(grid[0][Column(0)], cell('3'));
assert_eq!(grid[0][Column(1)], Cell::default());
assert_eq!(grid[0][Column(2)], Cell::default());
}
#[test]
fn shrink_reflow_disabled() {
let mut grid = Grid::new(Line(1), Column(5), 2, cell('x'));
grid[Line(0)][Column(0)] = cell('1');
grid[Line(0)][Column(1)] = cell('2');
grid[Line(0)][Column(2)] = cell('3');
grid[Line(0)][Column(3)] = cell('4');
grid[Line(0)][Column(4)] = cell('5');
grid.resize(false, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default());
assert_eq!(grid.len(), 1);
assert_eq!(grid[0].len(), 2);
assert_eq!(grid[0][Column(0)], cell('1'));
assert_eq!(grid[0][Column(1)], cell('2'));
}
fn cell(c: char) -> Cell {
let mut cell = Cell::default();
cell.c = c;

View File

@ -1228,13 +1228,11 @@ impl Term {
debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
// Resize grids to new size
let alt_cursor_point = if self.mode.contains(TermMode::ALT_SCREEN) {
&mut self.cursor_save.point
} else {
&mut self.cursor_save_alt.point
};
self.grid.resize(num_lines, num_cols, &mut self.cursor.point, &Cell::default());
self.alt_grid.resize(num_lines, num_cols, alt_cursor_point, &Cell::default());
let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
let alt_cursor_point =
if is_alt { &mut self.cursor_save.point } else { &mut self.cursor_save_alt.point };
self.grid.resize(!is_alt, num_lines, num_cols, &mut self.cursor.point, &Cell::default());
self.alt_grid.resize(is_alt, num_lines, num_cols, alt_cursor_point, &Cell::default());
// Reset scrolling region to new size
self.scroll_region = Line(0)..self.grid.num_lines();

View File

@ -2,6 +2,7 @@ extern crate copypasta;
#[cfg(target_os = "linux")]
use copypasta::x11_clipboard::{Primary, X11ClipboardContext};
#[cfg(target_os = "linux")]
use copypasta::ClipboardProvider;
#[cfg(target_os = "linux")]