From bbe276e3a80571dd0d72a9b32fb7bed38c3be868 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 12 Oct 2017 19:07:49 -0700 Subject: Back Grid with VecDeque VecDeque offers improved performance beyond a plain Vec for common scrolling situations (full screen scroll). Additionally, VecDeque is necessary for performant scrollback since recycling old rows for a Vec would be expensive (push/pop front would shift entire vec). --- src/term/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 4f56e7fc..dd853368 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -24,7 +24,7 @@ use unicode_width::UnicodeWidthChar; use font::{self, Size}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; -use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange, Indexed}; +use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange, Indexed, IndexRegion}; use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive}; use selection::{self, Span, Selection}; use config::{Config, VisualBellAnimation}; @@ -1708,7 +1708,7 @@ impl ansi::Handler for Term { cell.reset(&template); } if self.cursor.point.line < self.grid.num_lines() - 1 { - for row in &mut self.grid[(self.cursor.point.line + 1)..] { + for row in self.grid.region_mut((self.cursor.point.line + 1)..) { for cell in row { cell.reset(&template); } @@ -1722,7 +1722,7 @@ impl ansi::Handler for Term { // If clearing more than one line if self.cursor.point.line > Line(1) { // Fully clear all lines before the current line - for row in &mut self.grid[..self.cursor.point.line] { + for row in self.grid.region_mut(..self.cursor.point.line) { for cell in row { cell.reset(&template); } -- cgit From 6fc0e1ec49561fd9783332b30632471336004aed Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 12 Oct 2017 20:12:29 -0700 Subject: Eliminate ClearRegion trait --- src/term/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index dd853368..fa6145f9 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -24,7 +24,7 @@ use unicode_width::UnicodeWidthChar; use font::{self, Size}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; -use grid::{BidirectionalIterator, Grid, ClearRegion, ToRange, Indexed, IndexRegion}; +use grid::{BidirectionalIterator, Grid, ToRange, Indexed, IndexRegion}; use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive}; use selection::{self, Span, Selection}; use config::{Config, VisualBellAnimation}; @@ -1089,8 +1089,12 @@ impl Term { if num_lines > old_lines { // Make sure bottom of terminal is clear let template = self.cursor.template; - self.grid.clear_region((self.cursor.point.line + 1).., |c| c.reset(&template)); - self.alt_grid.clear_region((self.cursor_save_alt.point.line + 1).., |c| c.reset(&template)); + self.grid + .region_mut((self.cursor.point.line + 1)..) + .each(|c| c.reset(&template)); + self.alt_grid + .region_mut((self.cursor_save_alt.point.line + 1)..) + .each(|c| c.reset(&template)); } } @@ -1113,7 +1117,7 @@ impl Term { pub fn swap_alt(&mut self) { if self.alt { let template = &self.cursor.template; - self.grid.clear(|c| c.reset(template)); + self.grid.region_mut(..).each(|c| c.reset(template)); } self.alt = !self.alt; @@ -1135,7 +1139,9 @@ impl Term { // Clear `lines` lines at bottom of area { let start = max(origin, Line(self.scroll_region.end.0.saturating_sub(lines.0))); - self.grid.clear_region(start..self.scroll_region.end, |c| c.reset(&template)); + self.grid + .region_mut(start..self.scroll_region.end) + .each(|c| c.reset(&template)); } // Scroll between origin and bottom @@ -1157,7 +1163,7 @@ impl Term { // Clear `lines` lines starting from origin to origin + lines { let end = min(origin + lines, self.scroll_region.end); - self.grid.clear_region(origin..end, |c| c.reset(&template)); + self.grid.region_mut(origin..end).each(|c| c.reset(&template)); } // Scroll from origin to bottom less number of lines @@ -1172,7 +1178,7 @@ impl Term { // Clear grid let template = self.cursor.template; - self.grid.clear(|c| c.reset(&template)); + self.grid.region_mut(..).each(|c| c.reset(&template)); } #[inline] @@ -1708,25 +1714,19 @@ impl ansi::Handler for Term { cell.reset(&template); } if self.cursor.point.line < self.grid.num_lines() - 1 { - for row in self.grid.region_mut((self.cursor.point.line + 1)..) { - for cell in row { - cell.reset(&template); - } - } + self.grid.region_mut((self.cursor.point.line + 1)..) + .each(|cell| cell.reset(&template)); } }, ansi::ClearMode::All => { - self.grid.clear(|c| c.reset(&template)); + self.grid.region_mut(..).each(|c| c.reset(&template)); }, ansi::ClearMode::Above => { // If clearing more than one line if self.cursor.point.line > Line(1) { // Fully clear all lines before the current line - for row in self.grid.region_mut(..self.cursor.point.line) { - for cell in row { - cell.reset(&template); - } - } + self.grid.region_mut(..self.cursor.point.line) + .each(|cell| cell.reset(&template)); } // Clear up to the current column in the current line let end = min(self.cursor.point.col + 1, self.grid.num_cols()); -- cgit From 4ed25009c49b5963d91f4e3c7ead0f4a5b678980 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 12 Oct 2017 20:29:35 -0700 Subject: Remove some unused methods and impls --- src/term/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index fa6145f9..c405490e 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1299,11 +1299,8 @@ impl ansi::Handler for Term { let mut template = self.cursor.template; template.c = 'E'; - for row in &mut self.grid.lines_mut() { - for cell in row { - cell.reset(&template); - } - } + self.grid.region_mut(..) + .each(|c| c.reset(&template)); } #[inline] -- cgit From 350bb8c800232ce0b27512420e99645ec8b95ef1 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Sun, 14 Jan 2018 10:17:08 -0800 Subject: Use memcpy for resetting row contents In addition to a marginal performance improvement, this simplifies some logic in the Term implementation since now the Grid fully handles row recycling. --- src/term/mod.rs | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index c405490e..efcafc25 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -800,7 +800,7 @@ impl Term { let num_cols = size.cols(); let num_lines = size.lines(); - let grid = Grid::new(num_lines, num_cols, &template); + let grid = Grid::new(num_lines, num_cols, template); let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) @@ -1066,9 +1066,8 @@ impl Term { debug!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size - let template = Cell::default(); - self.grid.resize(num_lines, num_cols, &template); - self.alt_grid.resize(num_lines, num_cols, &template); + self.grid.resize(num_lines, num_cols); + self.alt_grid.resize(num_lines, num_cols); // Reset scrolling region to new size self.scroll_region = Line(0)..self.grid.num_lines(); @@ -1133,17 +1132,6 @@ impl Term { trace!("scroll_down_relative: origin={}, lines={}", origin, lines); let lines = min(lines, self.scroll_region.end - self.scroll_region.start); - // Copy of cell template; can't have it borrowed when calling clear/scroll - let template = self.cursor.template; - - // Clear `lines` lines at bottom of area - { - let start = max(origin, Line(self.scroll_region.end.0.saturating_sub(lines.0))); - self.grid - .region_mut(start..self.scroll_region.end) - .each(|c| c.reset(&template)); - } - // Scroll between origin and bottom self.grid.scroll_down(&(origin..self.scroll_region.end), lines); } @@ -1157,15 +1145,6 @@ impl Term { trace!("scroll_up_relative: origin={}, lines={}", origin, lines); let lines = min(lines, self.scroll_region.end - self.scroll_region.start); - // Copy of cell template; can't have it borrowed when calling clear/scroll - let template = self.cursor.template; - - // Clear `lines` lines starting from origin to origin + lines - { - let end = min(origin + lines, self.scroll_region.end); - self.grid.region_mut(origin..end).each(|c| c.reset(&template)); - } - // Scroll from origin to bottom less number of lines self.grid.scroll_up(&(origin..self.scroll_region.end), lines); } -- cgit From 45c2b3fbf72fa6dfd36bee590e64314c6da7c6c2 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 15 Feb 2018 18:35:49 -0800 Subject: checkpoint: very basic scrolling works Things that do not work - Limiting how far back in the buffer it's possible to scroll - Selections (need to transform to buffer offsets) --- src/term/mod.rs | 183 ++++++++++++++++++++++++++------------------------------ 1 file changed, 86 insertions(+), 97 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index efcafc25..e2088413 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -24,8 +24,8 @@ use unicode_width::UnicodeWidthChar; use font::{self, Size}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; -use grid::{BidirectionalIterator, Grid, ToRange, Indexed, IndexRegion}; -use index::{self, Point, Column, Line, Linear, IndexRange, Contains, RangeInclusive}; +use grid::{BidirectionalIterator, Grid, ToRange, Indexed, IndexRegion, DisplayIter}; +use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive}; use selection::{self, Span, Selection}; use config::{Config, VisualBellAnimation}; use {MouseCursor, Rgb}; @@ -94,12 +94,11 @@ impl selection::Dimensions for Term { /// This manages the cursor during a render. The cursor location is inverted to /// draw it, and reverted after drawing to maintain state. pub struct RenderableCellsIter<'a> { + inner: DisplayIter<'a, Cell>, grid: &'a Grid, cursor: &'a Point, - cursor_index: index::Linear, + cursor_offset: usize, mode: TermMode, - line: Line, - column: Column, config: &'a Config, colors: &'a color::List, selection: Option>, @@ -120,18 +119,18 @@ impl<'a> RenderableCellsIter<'a> { selection: Option>, cursor_style: CursorStyle, ) -> RenderableCellsIter<'b> { - let cursor_index = Linear(cursor.line.0 * grid.num_cols().0 + cursor.col.0); + let cursor_offset = grid.line_to_offset(cursor.line); + let inner = grid.display_iter(); RenderableCellsIter { - grid, - cursor, - cursor_index, - mode, - line: Line(0), - column: Column(0), - selection, - config, - colors, + cursor: cursor, + cursor_offset: cursor_offset, + grid: grid, + inner: inner, + mode: mode, + selection: selection, + config: config, + colors: colors, cursor_cells: ArrayDeque::new(), }.initialize(cursor_style) } @@ -318,6 +317,7 @@ impl<'a> RenderableCellsIter<'a> { } pub struct RenderableCell { + /// A _Display_ line (not necessarily an _Active_ line) pub line: Line, pub column: Column, pub c: char, @@ -336,81 +336,70 @@ impl<'a> Iterator for RenderableCellsIter<'a> { /// (eg. invert fg and bg colors). #[inline] fn next(&mut self) -> Option { - while self.line < self.grid.num_lines() { - while self.column < self.grid.num_cols() { - // Grab current state for this iteration - let line = self.line; - let mut column = self.column; - let cell = &self.grid[line][column]; - - let index = Linear(line.0 * self.grid.num_cols().0 + column.0); - - let (cell, selected) = if index == self.cursor_index { - // Cursor cell - let cell = self.cursor_cells.pop_front().unwrap(); - column = cell.column; - - // Since there may be multiple cursor cells (for a wide - // char), only update iteration position after all cursor - // cells have been drawn. - if self.cursor_cells.is_empty() { - self.line = cell.line; - self.column = cell.column + 1; - } - (cell.inner, false) - } else { - // Normal cell - self.column += 1; + loop { + // Handle cursor + let (cell, selected) = if self.cursor_offset == self.inner.offset() && + self.inner.column() == self.cursor.col + { + // Cursor cell + let cell = self.cursor_cells.pop_front().unwrap(); + + // Since there may be multiple cursor cells (for a wide + // char), only update iteration position after all cursor + // cells have been drawn. + if self.cursor_cells.is_empty() { + self.inner.next(); + } + (cell, false) + } else { + let cell = self.inner.next()?; + + // XXX (jwilm) selection temp disabled + // + // let selected = self.selection.as_ref() + // .map(|range| range.contains_(index)) + // .unwrap_or(false); + let selected = false; + + // Skip empty cells + if cell.is_empty() && !selected { + continue; + } + (cell, selected) + }; - let selected = self.selection.as_ref() - .map(|range| range.contains_(index)) - .unwrap_or(false); + // Apply inversion and lookup RGB values + let mut bg_alpha = 1.0; + let fg_rgb; + let bg_rgb; - // Skip empty cells - if cell.is_empty() && !selected { - continue; - } - (*cell, selected) - }; + let invert = selected ^ cell.inverse(); - // Apply inversion and lookup RGB values - let mut bg_alpha = 1.0; - let fg_rgb; - let bg_rgb; - - let invert = selected ^ cell.inverse(); - - if invert { - if cell.fg == cell.bg { - bg_rgb = self.colors[NamedColor::Foreground]; - fg_rgb = self.colors[NamedColor::Background]; - bg_alpha = 1.0 - } else { - bg_rgb = self.compute_fg_rgb(&cell.fg, &cell); - fg_rgb = self.compute_bg_rgb(&cell.bg); - } + if invert { + if cell.fg == cell.bg { + bg_rgb = self.colors[NamedColor::Foreground]; + fg_rgb = self.colors[NamedColor::Background]; + bg_alpha = 1.0 } else { - fg_rgb = self.compute_fg_rgb(&cell.fg, &cell); - bg_rgb = self.compute_bg_rgb(&cell.bg); - bg_alpha = self.compute_bg_alpha(&cell.bg); + bg_rgb = self.compute_fg_rgb(&cell.fg, &cell); + fg_rgb = self.compute_bg_rgb(&cell.bg); } - - return Some(RenderableCell { - line, - column, - flags: cell.flags, - c: cell.c, - fg: fg_rgb, - bg: bg_rgb, - bg_alpha, - }) + } else { + fg_rgb = self.compute_fg_rgb(&cell.fg, &cell); + bg_rgb = self.compute_bg_rgb(&cell.bg); + bg_alpha = self.compute_bg_alpha(&cell.bg); } - self.column = Column(0); - self.line += 1; + return Some(RenderableCell { + line: cell.line, + column: cell.column, + flags: cell.flags, + c: cell.c, + fg: fg_rgb, + bg: bg_rgb, + bg_alpha: bg_alpha, + }) } - - None } } @@ -789,6 +778,11 @@ impl Term { self.next_title.take() } + pub fn scroll_display(&mut self, count: isize) { + self.grid.scroll_display(count); + self.dirty = true; + } + #[inline] pub fn get_next_mouse_cursor(&mut self) -> Option { self.next_mouse_cursor.take() @@ -1047,10 +1041,6 @@ impl Term { num_lines = Line(2); } - // Scroll up to keep cursor and as much context as possible in grid. - // This only runs when the lines decreases. - self.scroll_region = Line(0)..self.grid.num_lines(); - // Scroll up to keep cursor in terminal if self.cursor.point.line >= num_lines { let lines = self.cursor.point.line - num_lines + 1; @@ -1062,7 +1052,6 @@ impl Term { let lines = self.cursor_save_alt.point.line - num_lines + 1; self.alt_grid.scroll_up(&(Line(0)..old_lines), lines); } - debug!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size @@ -1085,16 +1074,16 @@ impl Term { .map(|i| (*i as usize) % self.tabspaces == 0) .collect::>(); - if num_lines > old_lines { - // Make sure bottom of terminal is clear - let template = self.cursor.template; - self.grid - .region_mut((self.cursor.point.line + 1)..) - .each(|c| c.reset(&template)); - self.alt_grid - .region_mut((self.cursor_save_alt.point.line + 1)..) - .each(|c| c.reset(&template)); - } + // if num_lines > old_lines { + // // Make sure bottom of terminal is clear + // let template = self.cursor.template; + // self.grid + // .region_mut((self.cursor.point.line + 1)..) + // .each(|c| c.reset(&template)); + // self.alt_grid + // .region_mut((self.cursor_save_alt.point.line + 1)..) + // .each(|c| c.reset(&template)); + // } } -- cgit From 9b9b138bac9353d2d95ce71ec155c3a9b2963491 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 15 Feb 2018 19:34:09 -0800 Subject: Fir cursor not scrolling --- src/term/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index e2088413..824577f7 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -342,7 +342,8 @@ impl<'a> Iterator for RenderableCellsIter<'a> { self.inner.column() == self.cursor.col { // Cursor cell - let cell = self.cursor_cells.pop_front().unwrap(); + let mut cell = self.cursor_cells.pop_front().unwrap(); + cell.line = self.inner.line(); // Since there may be multiple cursor cells (for a wide // char), only update iteration position after all cursor -- cgit From 7fe67743ebffd047532f6271bf28474f9d947f64 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Fri, 16 Feb 2018 17:33:32 -0800 Subject: Scroll to bottom on character received --- src/term/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 824577f7..3bce60cd 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -784,6 +784,10 @@ impl Term { self.dirty = true; } + pub fn reset_scroll(&mut self) { + self.grid.reset_scroll(); + } + #[inline] pub fn get_next_mouse_cursor(&mut self) -> Option { self.next_mouse_cursor.take() -- cgit From c49a7e88f64d1421474d492cc6f51bfd30e1e4d1 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Fri, 16 Feb 2018 17:54:32 -0800 Subject: Make number of scrollback lines configurable --- src/term/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 3bce60cd..220dd8e6 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -785,7 +785,7 @@ impl Term { } pub fn reset_scroll(&mut self) { - self.grid.reset_scroll(); + self.grid.reset_scroll_display(); } #[inline] @@ -799,7 +799,7 @@ impl Term { let num_cols = size.cols(); let num_lines = size.lines(); - let grid = Grid::new(num_lines, num_cols, template); + let grid = Grid::new(num_lines, num_cols, config.scroll_history(), template); let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) -- cgit From ef3c384540b31004a423ada778ed5c02d90e68c6 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 5 Mar 2018 09:26:36 -0800 Subject: Style cleanup --- src/term/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 220dd8e6..493086a7 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -135,33 +135,28 @@ impl<'a> RenderableCellsIter<'a> { }.initialize(cursor_style) } - fn push_cursor_cells( - &mut self, - original_cell: Cell, - cursor_cell: Cell, - wide_cell: Cell, - ) { + fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) { // Prints the char under the cell if cursor is situated on a non-empty cell self.cursor_cells.push_back(Indexed { line: self.cursor.line, column: self.cursor.col, - inner: original_cell, + inner: original, }).expect("won't exceed capacity"); // Prints the cursor self.cursor_cells.push_back(Indexed { line: self.cursor.line, column: self.cursor.col, - inner: cursor_cell, + inner: cursor, }).expect("won't exceed capacity"); // If cursor is over a wide (2 cell size) character, // print the second cursor cell - if self.is_wide_cursor(&cursor_cell) { + if self.is_wide_cursor(&cursor) { self.cursor_cells.push_back(Indexed { line: self.cursor.line, column: self.cursor.col + 1, - inner: wide_cell, + inner: wide, }).expect("won't exceed capacity"); } } -- cgit From 8ef062efd94975885776e61b6df936088b018da0 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 5 Mar 2018 09:57:34 -0800 Subject: Move selection into Grid Supporting selections with scrollback has two major components: 1. Grid needs access to Selection so that it may update the scroll position as the terminal text changes. 2. Selection needs to be implemented in terms of buffer offsets -- NOT lines -- and be updated when Storage is rotated. This commit implements the first part. --- src/term/mod.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 493086a7..9eeabc31 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -769,6 +769,14 @@ impl SizeInfo { impl Term { + pub fn selection(&self) -> &Option { + &self.grid.selection + } + + pub fn selection_mut(&mut self) -> &mut Option { + &mut self.grid.selection + } + #[inline] pub fn get_next_title(&mut self) -> Option { self.next_title.take() @@ -865,7 +873,7 @@ impl Term { self.dirty } - pub fn string_from_selection(&self, span: &Span) -> String { + pub fn selection_to_string(&self) -> Option { /// Need a generic push() for the Append trait trait PushChar { fn push_char(&mut self, c: char); @@ -921,6 +929,9 @@ impl Term { } } + let selection = self.grid.selection.clone()?; + let span = selection.to_span(self)?; + let mut res = String::new(); let (start, end) = span.to_locations(); @@ -957,7 +968,7 @@ impl Term { } } - res + Some(res) } /// Convert the given pixel values to a grid coordinate @@ -986,10 +997,9 @@ impl Term { pub fn renderable_cells<'b>( &'b self, config: &'b Config, - selection: Option<&'b Selection>, window_focused: bool, ) -> RenderableCellsIter { - let selection = selection.and_then(|s| s.to_span(self)) + let selection = self.grid.selection.as_ref().and_then(|s| s.to_span(self)) .map(|span| span.to_range()); let cursor = if window_focused { self.cursor_style.unwrap_or(self.default_cursor_style) @@ -1031,6 +1041,9 @@ impl Term { return; } + self.grid.selection = None; + self.alt_grid.selection = None; + // Should not allow less than 1 col, causes all sorts of checks to be required. if num_cols <= Column(1) { num_cols = Column(2); -- cgit From 8018dee1812ab88793c0f18e13335fa77c068000 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Tue, 6 Mar 2018 20:57:40 -0800 Subject: Support selections with scrolling buffer Selections now *mostly* work. They move as the buffer scrolls, copying works as it should, and it looks like the different selection modes behave properly as well. The new Selection implementation uses buffer coordinates instead of screen coordinates. This leads to doing a transform from mouse input to update the selection, and back to screen coordinates when displaying the selection. Scrolling the selection is fast because the grid is already operating in buffer coordinates. There are several bugs to address: * A _partially_ visible selection will lead to a crash since the drawing routine converts selection coordinates to screen coordinates. The solution will be to clip the coordinates at draw time. * A selection scrolling off the buffer in either direction leads to indexing out-of-bounds. The solution again is to clip, but this needs to be done within Selection::rotate by passing a max limit. It may also need a return type to indicate that the selection is no longer visible and should be discarded. * A selection scrolling out of a logical scrolling region is not clipped. A temporary and robust workaround is to simply discard the selection in the case of scrolling in a region. wip selections fix issue with line selection selection mostly working need to support selection not being on the screen at draw time Fix selection_to_string Uncomment tests --- src/term/mod.rs | 93 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 26 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 9eeabc31..d1ce1d58 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -24,9 +24,9 @@ use unicode_width::UnicodeWidthChar; use font::{self, Size}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; -use grid::{BidirectionalIterator, Grid, ToRange, Indexed, IndexRegion, DisplayIter}; -use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive}; -use selection::{self, Span, Selection}; +use grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter}; +use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive, Linear}; +use selection::{self, Selection, Locations}; use config::{Config, VisualBellAnimation}; use {MouseCursor, Rgb}; use copypasta::{Clipboard, Load, Store}; @@ -37,7 +37,7 @@ pub use self::cell::Cell; use self::cell::LineLength; impl selection::SemanticSearch for Term { - fn semantic_search_left(&self, mut point: Point) -> Point { + fn semantic_search_left(&self, mut point: Point) -> Point { let mut iter = self.grid.iter_from(point); let last_col = self.grid.num_cols() - Column(1); @@ -56,7 +56,7 @@ impl selection::SemanticSearch for Term { point } - fn semantic_search_right(&self, mut point: Point) -> Point { + fn semantic_search_right(&self, mut point: Point) -> Point { let mut iter = self.grid.iter_from(point); let last_col = self.grid.num_cols() - Column(1); @@ -116,12 +116,37 @@ impl<'a> RenderableCellsIter<'a> { colors: &'b color::List, mode: TermMode, config: &'b Config, - selection: Option>, + selection: Option, cursor_style: CursorStyle, ) -> RenderableCellsIter<'b> { let cursor_offset = grid.line_to_offset(cursor.line); let inner = grid.display_iter(); + let selection = selection.map(|loc| { + // start and end *lines* are swapped as we switch from buffer to + // Line coordinates. + let mut end = Point { + line: grid.buffer_line_to_visible(loc.start.line), + col: loc.start.col + }; + let mut start = Point { + line: grid.buffer_line_to_visible(loc.end.line), + col: loc.end.col + }; + + if start > end { + ::std::mem::swap(&mut start, &mut end); + } + + println!("start={:?}, end={:?}", start, end); + + let cols = grid.num_cols(); + let start = Linear(start.line.0 * cols.0 + start.col.0); + let end = Linear(end.line.0 * cols.0 + end.col.0); + + RangeInclusive::new(start, end) + }); + RenderableCellsIter { cursor: cursor, cursor_offset: cursor_offset, @@ -350,12 +375,13 @@ impl<'a> Iterator for RenderableCellsIter<'a> { } else { let cell = self.inner.next()?; + let index = Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0); + // XXX (jwilm) selection temp disabled // - // let selected = self.selection.as_ref() - // .map(|range| range.contains_(index)) - // .unwrap_or(false); - let selected = false; + let selected = self.selection.as_ref() + .map(|range| range.contains_(index)) + .unwrap_or(false); // Skip empty cells if cell.is_empty() && !selected { @@ -877,7 +903,7 @@ impl Term { /// Need a generic push() for the Append trait trait PushChar { fn push_char(&mut self, c: char); - fn maybe_newline(&mut self, grid: &Grid, line: Line, ending: Column) { + fn maybe_newline(&mut self, grid: &Grid, line: usize, ending: Column) { if ending != Column(0) && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE) { self.push_char('\n'); } @@ -894,14 +920,14 @@ impl Term { use std::ops::Range; trait Append : PushChar { - fn append(&mut self, grid: &Grid, line: Line, cols: Range) -> Option>; + fn append(&mut self, grid: &Grid, line: usize, cols: Range) -> Option>; } impl Append for String { fn append( &mut self, grid: &Grid, - line: Line, + line: usize, cols: Range ) -> Option> { let grid_line = &grid[line]; @@ -934,43 +960,54 @@ impl Term { let mut res = String::new(); - let (start, end) = span.to_locations(); + let Locations { mut start, mut end } = span.to_locations(); + + if start > end { + ::std::mem::swap(&mut start, &mut end); + } + let line_count = end.line - start.line; let max_col = Column(usize::max_value() - 1); match line_count { // Selection within single line - Line(0) => { + 0 => { res.append(&self.grid, start.line, start.col..end.col); }, // Selection ends on line following start - Line(1) => { + 1 => { + // Ending line + res.append(&self.grid, end.line, end.col..max_col); + // Starting line - res.append(&self.grid, start.line, start.col..max_col); + res.append(&self.grid, start.line, Column(0)..start.col); - // Ending line - res.append(&self.grid, end.line, Column(0)..end.col); }, // Multi line selection _ => { - // Starting line - res.append(&self.grid, start.line, start.col..max_col); + // Ending line + res.append(&self.grid, end.line, end.col..max_col); - let middle_range = IndexRange::from((start.line + 1)..(end.line)); + let middle_range = (start.line + 1)..(end.line); for line in middle_range { res.append(&self.grid, line, Column(0)..max_col); } - // Ending line - res.append(&self.grid, end.line, Column(0)..end.col); + // Starting line + res.append(&self.grid, start.line, Column(0)..(start.col + 1)); + } } Some(res) } + pub(crate) fn visible_to_buffer(&self, point: Point) -> Point { + self.grid.visible_to_buffer(point) + } + /// Convert the given pixel values to a grid coordinate /// /// The mouse coordinates are expected to be relative to the top left. The @@ -999,8 +1036,12 @@ impl Term { config: &'b Config, window_focused: bool, ) -> RenderableCellsIter { - let selection = self.grid.selection.as_ref().and_then(|s| s.to_span(self)) - .map(|span| span.to_range()); + let selection = self.grid.selection.as_ref() + .and_then(|s| s.to_span(self)) + .map(|span| { + // println!("span={:?}, locations={:?}", span, span.to_locations()); + span.to_locations() + }); let cursor = if window_focused { self.cursor_style.unwrap_or(self.default_cursor_style) } else { -- cgit From 0484a07fe1bdedd658e74907a7f2bdeb068688db Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Thu, 8 Mar 2018 20:12:48 -0800 Subject: Fix 4+ line copying --- src/term/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index d1ce1d58..c3c3a300 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -991,7 +991,7 @@ impl Term { res.append(&self.grid, end.line, end.col..max_col); let middle_range = (start.line + 1)..(end.line); - for line in middle_range { + for line in middle_range.rev() { res.append(&self.grid, line, Column(0)..max_col); } -- cgit From 231ef51365e3cb0de760d04a47a7d2b74809c41d Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 10 Mar 2018 14:53:54 +0100 Subject: Fix crash when selection leaves viewport There was an issue where alacritty tries to convert the lines in a selection to the on-screen lines even when the selection is not on the screen. This results in a crash. To prevent this from happening the selection now is not shown if it is off the screen. There currently still is a bug that when the selection is at the top of the screen but still half visible, it will not show the top line as selected but start in the second line. This bug should be resolved with https://github.com/jwilm/alacritty/pull/1171. This fixes #1148. --- src/term/mod.rs | 58 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 19 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index c3c3a300..54c8a7e2 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -122,29 +122,49 @@ impl<'a> RenderableCellsIter<'a> { let cursor_offset = grid.line_to_offset(cursor.line); let inner = grid.display_iter(); - let selection = selection.map(|loc| { - // start and end *lines* are swapped as we switch from buffer to - // Line coordinates. - let mut end = Point { - line: grid.buffer_line_to_visible(loc.start.line), - col: loc.start.col - }; - let mut start = Point { - line: grid.buffer_line_to_visible(loc.end.line), - col: loc.end.col + let mut selection_range = None; + selection.map(|loc| { + // Get on-screen lines of the selection's locations + let start_line = grid.buffer_line_to_visible(loc.start.line); + let end_line = grid.buffer_line_to_visible(loc.end.line); + + // Get start/end locations based on what part of selection is on screen + let locations = match (start_line, end_line) { + (Some(start_line), Some(end_line)) => { + Some((start_line, loc.start.col, end_line, loc.end.col)) + }, + (Some(start_line), None) => { + Some((start_line, loc.start.col, Line(0), grid.num_cols())) + }, + (None, Some(end_line)) => { + Some((grid.num_lines(), Column(0), end_line, loc.end.col)) + }, + (None, None) => None, }; - if start > end { - ::std::mem::swap(&mut start, &mut end); - } + if let Some((start_line, start_col, end_line, end_col)) = locations { + // start and end *lines* are swapped as we switch from buffer to + // Line coordinates. + let mut end = Point { + line: start_line, + col: start_col, + }; + let mut start = Point { + line: end_line, + col: end_col, + }; - println!("start={:?}, end={:?}", start, end); + if start > end { + ::std::mem::swap(&mut start, &mut end); + } - let cols = grid.num_cols(); - let start = Linear(start.line.0 * cols.0 + start.col.0); - let end = Linear(end.line.0 * cols.0 + end.col.0); + let cols = grid.num_cols(); + let start = Linear(start.line.0 * cols.0 + start.col.0); + let end = Linear(end.line.0 * cols.0 + end.col.0); - RangeInclusive::new(start, end) + // Update the selection + selection_range = Some(RangeInclusive::new(start, end)); + } }); RenderableCellsIter { @@ -153,7 +173,7 @@ impl<'a> RenderableCellsIter<'a> { grid: grid, inner: inner, mode: mode, - selection: selection, + selection: selection_range, config: config, colors: colors, cursor_cells: ArrayDeque::new(), -- cgit From d3f64072f3fe4d31f7a60eb0cb17a98096617b4b Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 9 Mar 2018 19:45:40 +0100 Subject: Merge branch #1095 Because there was some overlap with branch #1095, these two PRs have been added together and the config has been restructured to make use of a `scrolling` section. The default faux scrolling amount has also been changed to `3` because this simplifies the code and falls in line with what most other terminal emulators do. There should be no additional test failures due to this. --- src/term/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 54c8a7e2..fd321fc6 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -848,7 +848,8 @@ impl Term { let num_cols = size.cols(); let num_lines = size.lines(); - let grid = Grid::new(num_lines, num_cols, config.scroll_history(), template); + let history_size = config.scrolling().history as usize; + let grid = Grid::new(num_lines, num_cols, history_size, template); let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) -- cgit From d9bd21d33f7f35d1362a581cefb1c897a821fcad Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 10 Mar 2018 12:14:58 +0100 Subject: Fix selection in scrollback There were a few issues with selection in scrollback that were mainly off-by-one errors. This aims at fixing these issues. This also fixes a bug that currently exists in master where the last cell is not selected when the mouse leaves the window to the right. --- src/term/mod.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index fd321fc6..6318c680 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1060,7 +1060,6 @@ impl Term { let selection = self.grid.selection.as_ref() .and_then(|s| s.to_span(self)) .map(|span| { - // println!("span={:?}, locations={:?}", span, span.to_locations()); span.to_locations() }); let cursor = if window_focused { -- cgit From 58c69cafad3b1dafa3631d911c6bfc21f5e5dec5 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 13 Mar 2018 19:00:14 +0100 Subject: Fix multi-line selection with single cell end When the user selected multiple lines, dragging the selection downwards, and then leaves the cursor to the left side of the first cell, the first cell was still incorrectly selected. This has been fixed. The selection also did not update if the mouse was outside of the window, now all movement events are accpeted even when the mouse is outside of the window. This allows updating the selection when the user is dragging the cursor too far. Mouse movement and click events outside of the window are not propagated, these are only used for updating the selection. --- src/term/mod.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 6318c680..8db21402 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -791,25 +791,21 @@ impl SizeInfo { Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize) } - fn contains_point(&self, x: usize, y:usize) -> bool { + pub fn contains_point(&self, x: usize, y:usize) -> bool { x <= (self.width - self.padding_x) as usize && x >= self.padding_x as usize && y <= (self.height - self.padding_y) as usize && y >= self.padding_y as usize } - pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option { - if !self.contains_point(x, y) { - return None; - } + pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point { + let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize)); + let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize)); - let col = Column((x - self.padding_x as usize) / (self.cell_width as usize)); - let line = Line((y - self.padding_y as usize) / (self.cell_height as usize)); - - Some(Point { + Point { line: min(line, self.lines() - 1), col: min(col, self.cols() - 1) - }) + } } } @@ -1036,7 +1032,11 @@ impl Term { /// /// Returns None if the coordinates are outside the screen pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option { - self.size_info().pixels_to_coords(x, y) + if self.size_info.contains_point(x, y) { + Some(self.size_info.pixels_to_coords(x, y)) + } else { + None + } } /// Access to the raw grid data structure -- cgit From b0f272f41950558cfec36414d2430d524d7b8b55 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Tue, 13 Mar 2018 22:07:23 +0100 Subject: Fix buggy selection when scrolling down When scrolling down with a selection on screen the first line was not properly selected. This has been fixed by making sure the selection always starts in the first cell when it is only partially visible. --- src/term/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 8db21402..c8017262 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -134,7 +134,7 @@ impl<'a> RenderableCellsIter<'a> { Some((start_line, loc.start.col, end_line, loc.end.col)) }, (Some(start_line), None) => { - Some((start_line, loc.start.col, Line(0), grid.num_cols())) + Some((start_line, loc.start.col, Line(0), Column(0))) }, (None, Some(end_line)) => { Some((grid.num_lines(), Column(0), end_line, loc.end.col)) -- cgit From 2c7bb9a4d3ce3ead6de4ca6485ca67c44c0bd1c1 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 10 Mar 2018 20:24:10 +0100 Subject: Add scrollback hotkeys This offers a few additional hotkeys that can be used in combination with scrollback. None of these are used by default yet. This implements the following bindings: - ScrollPageUp: Scroll exactly one screen height up - ScrollPageDown: Scroll exactly one screen height down - ScrollToTop: Scroll as far up as possible - ScrollToBottom: Scroll as far down as possible This fixes #1151. --- src/term/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index c8017262..8e973b0b 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -831,6 +831,22 @@ impl Term { pub fn reset_scroll(&mut self) { self.grid.reset_scroll_display(); + self.dirty = true; + } + + pub fn scroll_to_top(&mut self) { + self.grid.scroll_to_top(); + self.dirty = true; + } + + pub fn scroll_page_up(&mut self) { + self.grid.scroll_page_up(); + self.dirty = true; + } + + pub fn scroll_page_down(&mut self) { + self.grid.scroll_page_down(); + self.dirty = true; } #[inline] -- cgit From 0dcb9ca6a871fd6d28787142a134c9190001ac43 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sun, 11 Mar 2018 13:01:06 +0100 Subject: Replace scrolling methods with enum The different scrolling methods added a bunch of boilerplate where the call was just forwarded to the next struct, this has been removed by making the scroll amount into a struct. Now everything is called through one method and the parameter decides how far the viewport should be scrolled. --- src/term/mod.rs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 8e973b0b..3c7ef87c 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -24,7 +24,7 @@ use unicode_width::UnicodeWidthChar; use font::{self, Size}; use ansi::{self, Color, NamedColor, Attr, Handler, CharsetIndex, StandardCharset, CursorStyle}; -use grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter}; +use grid::{BidirectionalIterator, Grid, Indexed, IndexRegion, DisplayIter, Scroll}; use index::{self, Point, Column, Line, IndexRange, Contains, RangeInclusive, Linear}; use selection::{self, Selection, Locations}; use config::{Config, VisualBellAnimation}; @@ -824,28 +824,8 @@ impl Term { self.next_title.take() } - pub fn scroll_display(&mut self, count: isize) { - self.grid.scroll_display(count); - self.dirty = true; - } - - pub fn reset_scroll(&mut self) { - self.grid.reset_scroll_display(); - self.dirty = true; - } - - pub fn scroll_to_top(&mut self) { - self.grid.scroll_to_top(); - self.dirty = true; - } - - pub fn scroll_page_up(&mut self) { - self.grid.scroll_page_up(); - self.dirty = true; - } - - pub fn scroll_page_down(&mut self) { - self.grid.scroll_page_down(); + pub fn scroll_display(&mut self, scroll: Scroll) { + self.grid.scroll_display(scroll); self.dirty = true; } -- cgit From 688cabefc0bfc3224189c10235668eae5e5b0101 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 23 Mar 2018 01:01:55 +0100 Subject: Rework auto-scrolling options This changes two things, the first thing it does is that now whenever a keybinding sends an escape sequence, the viewport is automatically scrolled to the bottom. This is enabled by default and fixes #1187. The second thing is automatic scrolling when a command writes to the terminal. So when running a command like `sleep 3; ls -lah`, alacritty will scroll to the bottom once the output is sent, even if the viewport is currently not at the bottom of the scrollback. Because this can have an impact on performance, and is not enabled by default in terminals like iTerm or Termite (VTE), it is an opt-in setting in the config. --- src/term/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 3c7ef87c..198f8cea 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -756,6 +756,9 @@ pub struct Term { /// Number of spaces in one tab tabspaces: usize, + + /// Automatically scroll to bottom when new lines are added + auto_scroll: bool, } /// Terminal size info @@ -879,6 +882,7 @@ impl Term { default_cursor_style: config.cursor_style(), dynamic_title: config.dynamic_title(), tabspaces, + auto_scroll: config.scrolling().auto_scroll, } } @@ -905,6 +909,7 @@ impl Term { self.visual_bell.update_config(config); self.default_cursor_style = config.cursor_style(); self.dynamic_title = config.dynamic_title(); + self.auto_scroll = config.scrolling().auto_scroll; } #[inline] @@ -1255,6 +1260,11 @@ impl ansi::Handler for Term { /// A character to be displayed #[inline] fn input(&mut self, c: char) { + // If enabled, scroll to bottom when character is received + if self.auto_scroll { + self.scroll_display(Scroll::Bottom); + } + if self.input_needs_wrap { if !self.mode.contains(mode::TermMode::LINE_WRAP) { return; -- cgit From b0f655ac85ab6d86e9e482cbb9035200c6f08d40 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Fri, 9 Mar 2018 13:49:47 -0800 Subject: Make tests compile again Some tests are still not passing, though. A migration script was added to migrate serialized grids from pre-scrollback to the current format. The script is included with this commit for completeness, posterity, and as an example to be used in the future. A few tests in grid/tests.rs were removed due to becoming irrelevant. --- src/term/mod.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 198f8cea..7a50810e 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -2004,7 +2004,7 @@ mod tests { padding_y: 0.0, }; let mut term = Term::new(&Default::default(), size); - let mut grid: Grid = Grid::new(Line(3), Column(5), &Cell::default()); + let mut grid: Grid = Grid::new(Line(3), Column(5), 0, Cell::default()); for i in 0..5 { for j in 0..2 { grid[Line(j)][Column(i)].c = 'a'; @@ -2021,18 +2021,18 @@ mod tests { mem::swap(&mut term.semantic_escape_chars, &mut escape_chars); { - let selection = Selection::semantic(Point { line: Line(0), col: Column(1) }, &term); - assert_eq!(term.string_from_selection(&selection.to_span(&term).unwrap()), "aa"); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }, &term)); + assert_eq!(term.selection_to_string(), Some(String::from("aa"))); } { - let selection = Selection::semantic(Point { line: Line(0), col: Column(4) }, &term); - assert_eq!(term.string_from_selection(&selection.to_span(&term).unwrap()), "aaa"); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }, &term)); + assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } { - let selection = Selection::semantic(Point { line: Line(1), col: Column(1) }, &term); - assert_eq!(term.string_from_selection(&selection.to_span(&term).unwrap()), "aaa"); + *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }, &term)); + assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } } @@ -2047,7 +2047,7 @@ mod tests { padding_y: 0.0, }; let mut term = Term::new(&Default::default(), size); - let mut grid: Grid = Grid::new(Line(1), Column(5), &Cell::default()); + let mut grid: Grid = Grid::new(Line(1), Column(5), 0, Cell::default()); for i in 0..5 { grid[Line(0)][Column(i)].c = 'a'; } @@ -2057,10 +2057,8 @@ mod tests { mem::swap(&mut term.grid, &mut grid); - let selection = Selection::lines(Point { line: Line(0), col: Column(3) }); - if let Some(span) = selection.to_span(&term) { - assert_eq!(term.string_from_selection(&span), "\"aa\"a\n"); - } + *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) })); + assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n"))); } /// Check that the grid can be serialized back and forth losslessly @@ -2071,7 +2069,7 @@ mod tests { fn grid_serde() { let template = Cell::default(); - let grid = Grid::new(Line(24), Column(80), &template); + let grid: Grid = Grid::new(Line(24), Column(80), 0, template); let serialized = serde_json::to_string(&grid).expect("ser"); let deserialized = serde_json::from_str::>(&serialized) .expect("de"); -- cgit From b19045da66899999856c6b2cc6707b60c607660a Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 2 Apr 2018 08:44:54 -0700 Subject: Fix BCE ref tests BCE was broken in attempt to optimize row clearing. The fix is to revert to passing in the current cursor state when clearing. --- src/term/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 7a50810e..f66753d9 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1119,19 +1119,19 @@ impl Term { // Scroll up to keep cursor in terminal if self.cursor.point.line >= num_lines { let lines = self.cursor.point.line - num_lines + 1; - self.grid.scroll_up(&(Line(0)..old_lines), lines); + self.grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template); } // Scroll up alt grid as well if self.cursor_save_alt.point.line >= num_lines { let lines = self.cursor_save_alt.point.line - num_lines + 1; - self.alt_grid.scroll_up(&(Line(0)..old_lines), lines); + self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); } debug!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size - self.grid.resize(num_lines, num_cols); - self.alt_grid.resize(num_lines, num_cols); + self.grid.resize(num_lines, num_cols, &self.cursor.template); + self.alt_grid.resize(num_lines, num_cols, &self.cursor_save_alt.template); // Reset scrolling region to new size self.scroll_region = Line(0)..self.grid.num_lines(); @@ -1197,7 +1197,7 @@ impl Term { let lines = min(lines, self.scroll_region.end - self.scroll_region.start); // Scroll between origin and bottom - self.grid.scroll_down(&(origin..self.scroll_region.end), lines); + self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &self.cursor.template); } /// Scroll screen up @@ -1210,7 +1210,7 @@ impl Term { let lines = min(lines, self.scroll_region.end - self.scroll_region.start); // Scroll from origin to bottom less number of lines - self.grid.scroll_up(&(origin..self.scroll_region.end), lines); + self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &self.cursor.template); } fn deccolm(&mut self) { -- cgit From d8bda60c3d8f906f1018012f4a55c7b894afb4b7 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 28 Apr 2018 14:14:45 +0000 Subject: Reset grid when running `reset` In the current scrollback PR the `reset` command does not affect the scrollback history. To make sure the terminal is properly reset, it should clear the scrollback history. To make resetting efficient, instead of resetting the history, the scrollback history is hidden by setting `grid.scroll_limit` to `0`. This will not clear the history but instead just make it inaccessible, which should have the same effect. The visible area is reset by the shell itself, so in combination this clears the complete terminal grid from a user perspective. This fixes #1242. --- src/term/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index f66753d9..fa204a55 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -838,13 +838,11 @@ impl Term { } pub fn new(config: &Config, size: SizeInfo) -> Term { - let template = Cell::default(); - let num_cols = size.cols(); let num_lines = size.lines(); let history_size = config.scrolling().history as usize; - let grid = Grid::new(num_lines, num_cols, history_size, template); + let grid = Grid::new(num_lines, num_cols, history_size, Cell::default()); let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) @@ -1820,6 +1818,7 @@ impl ansi::Handler for Term { self.colors = self.original_colors; self.color_modified = [false; color::COUNT]; self.cursor_style = None; + self.grid.reset(); } #[inline] -- cgit From 4631ca4db5faf10eb0276e3968814a68c86c81ee Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 30 May 2018 10:20:47 +0200 Subject: Allow changing scrollback history size at runtime Making use of the changes that have been introduced in #1234 and #1284, this allows changing the size of the scrollback buffer at runtime. This simply changes the size of the raw inner buffer making use of the optimized mutation algorithms introduced in #1284. As a result, shrinking the scrollback history size at runtime should be basically free and growing will only introduce a performance cost when there are no more buffered lines. However, as a result there will not be any memory freed when shrinking the scrollback history size at runtime. As discussed in #1234 a potential solution for this could be to truncate the raw buffer whenever more than X lines are deleted, however this issue should not be very significant PR and if a solution is desired a separate issue/PR should be opened. This fixes #1235. --- src/term/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index fa204a55..8635c818 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -908,6 +908,8 @@ impl Term { self.default_cursor_style = config.cursor_style(); self.dynamic_title = config.dynamic_title(); self.auto_scroll = config.scrolling().auto_scroll; + self.grid + .update_history(config.scrolling().history as usize, &self.cursor.template); } #[inline] @@ -2020,17 +2022,17 @@ mod tests { mem::swap(&mut term.semantic_escape_chars, &mut escape_chars); { - *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }, &term)); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) })); assert_eq!(term.selection_to_string(), Some(String::from("aa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }, &term)); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }, &term)); + *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } } -- cgit From bfd62ef45bedf02a4f61c08bba134a2eb06c0b47 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 16 Jun 2018 17:50:44 +0000 Subject: Optimize indexing of the grid's raw buffer The `compute_index` method in the `Storage` struct used to normalize indices was responsible for a significant amount of the CPU time spent while running the `alt-screen-random-write` benchmark (~50%). The issue with this relatively simple method was that due to how often the method is executed, the modulo operation was too expensive. Instead of the modulo, a more conservative branch has been put in place which has a very efficient best-case (which is hit most of the time). Until now the methods for growing/shrinking the storage buffer and compute_index have been written with the assumption that `self.zero` might be bigger than `self.inner.len()`. However there is no reason why `self.zero` wouldn't be constrained to always be within the size of the raw buffer, so this has been changed to make things a little simpler and more explicit. Instead of clamping the selection to be within the buffer inside the storage, this is now checked in the selection logic to remove all selection-specific logic from `storage.rs`. --- src/term/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 8635c818..6de8afac 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -945,9 +945,12 @@ impl Term { fn append( &mut self, grid: &Grid, - line: usize, + mut line: usize, cols: Range ) -> Option> { + // Select until last line still within the buffer + line = min(line, grid.len() - 1); + let grid_line = &grid[line]; let line_length = grid_line.line_length(); let line_end = min(line_length, cols.end + 1); -- cgit From 07aaf05f7463a971e56de87e3b6b24e4153e5a98 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Mon, 2 Jul 2018 22:03:04 +0000 Subject: Fix scrollback accessing indices out of bounds There have been two instances of the scrollback trying to access indices which were moved out of bounds due to new lines (`yes` command). These have both been fixed. The first instance was during semantic selection, since the logic of limiting the selection start point was moved outside of `compute_index`, it was necessary to add this to semantic selection too. Now semantic selection, line selection and normal selection should all work without crashing when new lines are shoving the selection out of bounds. The other error was with the viewport being outside of the scrollback history. Since the default is to keep the scrollback buffer at its current position when new lines are added, it is possible that the position the scrollback buffer is at is suddenly shoved out of the visible area. To fix this the `display_offset` is now limited to always be an allowed value. If a single line of the viewport is moved out of the history now, the viewport should move down a single line now, so only valid content is displayed, with multiple lines this process is repeated. This fixes #1400. There was another error where the iterator would attempt to iterate before the first line in the history buffer, this was because the bounds of the `prev` iterator weren't setup correctly. The iterator should now properly iterate from the first cell in the terminal until the last one. This also fixes #1406, since these semantic selection errors were partiall related to indexing. --- src/term/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 6de8afac..67d34be1 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -38,6 +38,9 @@ use self::cell::LineLength; impl selection::SemanticSearch for Term { fn semantic_search_left(&self, mut point: Point) -> Point { + // Limit the starting point to the last line in the history + point.line = min(point.line, self.grid.len() - 1); + let mut iter = self.grid.iter_from(point); let last_col = self.grid.num_cols() - Column(1); @@ -57,6 +60,9 @@ impl selection::SemanticSearch for Term { } fn semantic_search_right(&self, mut point: Point) -> Point { + // Limit the starting point to the last line in the history + point.line = min(point.line, self.grid.len() - 1); + let mut iter = self.grid.iter_from(point); let last_col = self.grid.num_cols() - Column(1); @@ -1017,7 +1023,7 @@ impl Term { } // Starting line - res.append(&self.grid, start.line, Column(0)..(start.col + 1)); + res.append(&self.grid, start.line, Column(0)..start.col); } } -- cgit From b05ad74fe6d42ce0f913e02ef633ca119fc0b43e Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Fri, 6 Jul 2018 07:45:10 -0700 Subject: Disable scroll buffer for "alt" Grid The scroll history size for the alternative grid (used by fullscreen apps such as vim and tmux) is now forced to zero. There are two motivations for this change: 1. According to the literature, the alt screen should not have scroll history. 2. Reduce memory consumption by only allocating the single scroll history. In the future, it may be desirable to support a configuration option to enable a scroll buffer for the alt screen. By launching without this feature, we can delay a decision about whether to officially support this or not. --- src/term/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 67d34be1..9f8506d1 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -849,13 +849,13 @@ impl Term { let history_size = config.scrolling().history as usize; let grid = Grid::new(num_lines, num_cols, history_size, Cell::default()); + let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default()); let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) .map(|i| (*i as usize) % tabspaces == 0) .collect::>(); - let alt = grid.clone(); let scroll_region = Line(0)..grid.num_lines(); Term { -- cgit From f50ca1a54c94fe324d22d985c1acae1ff7c16a80 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 21 Jul 2018 17:17:41 +0000 Subject: Scrollback cleanup There were some unneeded codeblocks and TODO/XXX comments in the code that have been removed. All issues marked with TODO/XXX have either been already resolved or tracking issues exist. --- src/term/mod.rs | 136 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 40 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 9f8506d1..3cdd5ff3 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -30,6 +30,7 @@ use selection::{self, Selection, Locations}; use config::{Config, VisualBellAnimation}; use {MouseCursor, Rgb}; use copypasta::{Clipboard, Load, Store}; +use input::FONT_SIZE_STEP; pub mod cell; pub mod color; @@ -129,7 +130,7 @@ impl<'a> RenderableCellsIter<'a> { let inner = grid.display_iter(); let mut selection_range = None; - selection.map(|loc| { + if let Some(loc) = selection { // Get on-screen lines of the selection's locations let start_line = grid.buffer_line_to_visible(loc.start.line); let end_line = grid.buffer_line_to_visible(loc.end.line); @@ -171,17 +172,17 @@ impl<'a> RenderableCellsIter<'a> { // Update the selection selection_range = Some(RangeInclusive::new(start, end)); } - }); + } RenderableCellsIter { - cursor: cursor, - cursor_offset: cursor_offset, - grid: grid, - inner: inner, - mode: mode, + cursor, + cursor_offset, + grid, + inner, + mode, selection: selection_range, - config: config, - colors: colors, + config, + colors, cursor_cells: ArrayDeque::new(), }.initialize(cursor_style) } @@ -313,9 +314,9 @@ impl<'a> RenderableCellsIter<'a> { self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor) } - fn compute_fg_rgb(&self, fg: &Color, cell: &Cell) -> Rgb { + fn compute_fg_rgb(&self, fg: Color, cell: &Cell) -> Rgb { use self::cell::Flags; - match *fg { + match fg { Color::Spec(rgb) => rgb, Color::Named(ansi) => { match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) { @@ -346,15 +347,15 @@ impl<'a> RenderableCellsIter<'a> { } #[inline] - fn compute_bg_alpha(&self, bg: &Color) -> f32 { - match *bg { + fn compute_bg_alpha(&self, bg: Color) -> f32 { + match bg { Color::Named(NamedColor::Background) => 0.0, _ => 1.0 } } - fn compute_bg_rgb(&self, bg: &Color) -> Rgb { - match *bg { + fn compute_bg_rgb(&self, bg: Color) -> Rgb { + match bg { Color::Spec(rgb) => rgb, Color::Named(ansi) => self.colors[ansi], Color::Indexed(idx) => self.colors[idx], @@ -403,8 +404,6 @@ impl<'a> Iterator for RenderableCellsIter<'a> { let index = Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0); - // XXX (jwilm) selection temp disabled - // let selected = self.selection.as_ref() .map(|range| range.contains_(index)) .unwrap_or(false); @@ -429,13 +428,13 @@ impl<'a> Iterator for RenderableCellsIter<'a> { fg_rgb = self.colors[NamedColor::Background]; bg_alpha = 1.0 } else { - bg_rgb = self.compute_fg_rgb(&cell.fg, &cell); - fg_rgb = self.compute_bg_rgb(&cell.bg); + bg_rgb = self.compute_fg_rgb(cell.fg, &cell); + fg_rgb = self.compute_bg_rgb(cell.bg); } } else { - fg_rgb = self.compute_fg_rgb(&cell.fg, &cell); - bg_rgb = self.compute_bg_rgb(&cell.bg); - bg_alpha = self.compute_bg_alpha(&cell.bg); + fg_rgb = self.compute_fg_rgb(cell.fg, &cell); + bg_rgb = self.compute_bg_rgb(cell.bg); + bg_alpha = self.compute_bg_alpha(cell.bg); } return Some(RenderableCell { @@ -445,7 +444,7 @@ impl<'a> Iterator for RenderableCellsIter<'a> { c: cell.c, fg: fg_rgb, bg: bg_rgb, - bg_alpha: bg_alpha, + bg_alpha, }) } } @@ -890,10 +889,10 @@ impl Term { } } - pub fn change_font_size(&mut self, delta: i8) { - // Saturating addition with minimum font size 1 - let new_size = self.font_size + Size::new(f32::from(delta)); - self.font_size = max(new_size, Size::new(1.)); + pub fn change_font_size(&mut self, delta: f32) { + // Saturating addition with minimum font size FONT_SIZE_STEP + let new_size = self.font_size + Size::new(delta); + self.font_size = max(new_size, Size::new(FONT_SIZE_STEP)); self.dirty = true; } @@ -1157,18 +1156,6 @@ impl Term { self.tabs = IndexRange::from(Column(0)..self.grid.num_cols()) .map(|i| (*i as usize) % self.tabspaces == 0) .collect::>(); - - // if num_lines > old_lines { - // // Make sure bottom of terminal is clear - // let template = self.cursor.template; - // self.grid - // .region_mut((self.cursor.point.line + 1)..) - // .each(|c| c.reset(&template)); - // self.alt_grid - // .region_mut((self.cursor_save_alt.point.line + 1)..) - // .each(|c| c.reset(&template)); - // } - } #[inline] @@ -2002,6 +1989,9 @@ mod tests { use ansi::{Handler, CharsetIndex, StandardCharset}; use selection::Selection; use std::mem; + use input::FONT_SIZE_STEP; + use font::Size; + use config::Config; #[test] fn semantic_selection_works() { @@ -2105,6 +2095,72 @@ mod tests { assert_eq!(term.grid()[&cursor].c, '▒'); } + + fn change_font_size_works(font_size: f32) { + let size = SizeInfo { + width: 21.0, + height: 51.0, + cell_width: 3.0, + cell_height: 3.0, + padding_x: 0.0, + padding_y: 0.0, + }; + let config: Config = Default::default(); + let mut term: Term = Term::new(&config, size); + term.change_font_size(font_size); + + let expected_font_size: Size = config.font().size() + Size::new(font_size); + assert_eq!(term.font_size, expected_font_size); + } + + #[test] + fn increase_font_size_works() { + change_font_size_works(10.0); + } + + #[test] + fn decrease_font_size_works() { + change_font_size_works(-10.0); + } + + #[test] + fn prevent_font_below_threshold_works() { + let size = SizeInfo { + width: 21.0, + height: 51.0, + cell_width: 3.0, + cell_height: 3.0, + padding_x: 0.0, + padding_y: 0.0, + }; + let config: Config = Default::default(); + let mut term: Term = Term::new(&config, size); + + term.change_font_size(-100.0); + + let expected_font_size: Size = Size::new(FONT_SIZE_STEP); + assert_eq!(term.font_size, expected_font_size); + } + + #[test] + fn reset_font_size_works() { + let size = SizeInfo { + width: 21.0, + height: 51.0, + cell_width: 3.0, + cell_height: 3.0, + padding_x: 0.0, + padding_y: 0.0, + }; + let config: Config = Default::default(); + let mut term: Term = Term::new(&config, size); + + term.change_font_size(10.0); + term.reset_font_size(); + + let expected_font_size: Size = config.font().size(); + assert_eq!(term.font_size, expected_font_size); + } } #[cfg(all(test, feature = "bench"))] @@ -2161,7 +2217,7 @@ mod benches { mem::swap(&mut terminal.grid, &mut grid); b.iter(|| { - let iter = terminal.renderable_cells(&config, None, false); + let iter = terminal.renderable_cells(&config, false); for cell in iter { test::black_box(cell); } -- cgit From 0ca5c7a6956d2af95f798b858e646e5ec8e63403 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Thu, 26 Jul 2018 20:47:33 +0000 Subject: Fix trailing colors when leaving vim after resize There is an issue where the terminal would use the template cell to fill new space after resizing the terminal. However this leads to issues since the template cell is not always empty and thus can create some blocks of color appearing out of nowhere. This should fix this problem by always initializing cells with the default cell after resizing. --- src/term/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 3cdd5ff3..ca3b5025 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1138,8 +1138,8 @@ impl Term { debug!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size - self.grid.resize(num_lines, num_cols, &self.cursor.template); - self.alt_grid.resize(num_lines, num_cols, &self.cursor_save_alt.template); + self.grid.resize(num_lines, num_cols, &Cell::default()); + self.alt_grid.resize(num_lines, num_cols, &Cell::default()); // Reset scrolling region to new size self.scroll_region = Line(0)..self.grid.num_lines(); -- cgit From c4a0f9c4cb7e8d73914800c4ba64413970f98540 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 28 Jul 2018 23:10:13 +0000 Subject: Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available. --- src/term/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index ca3b5025..97f8b779 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -320,11 +320,18 @@ impl<'a> RenderableCellsIter<'a> { Color::Spec(rgb) => rgb, Color::Named(ansi) => { match (self.config.draw_bold_text_with_bright_colors(), cell.flags & Flags::DIM_BOLD) { + // If no bright foreground is set, treat it like the BOLD flag doesn't exist + (_, self::cell::Flags::DIM_BOLD) + if ansi == NamedColor::Foreground + && self.config.colors().primary.bright_foreground.is_none() => + { + self.colors[NamedColor::DimForeground] + } // Draw bold text in bright colors *and* contains bold flag. - (true, self::cell::Flags::DIM_BOLD) | - (true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()], + (true, self::cell::Flags::BOLD) => self.colors[ansi.to_bright()], // Cell is marked as dim and not bold - (_, self::cell::Flags::DIM) => self.colors[ansi.to_dim()], + (_, self::cell::Flags::DIM) | + (false, self::cell::Flags::DIM_BOLD) => self.colors[ansi.to_dim()], // None of the above, keep original color. _ => self.colors[ansi] } @@ -412,6 +419,7 @@ impl<'a> Iterator for RenderableCellsIter<'a> { if cell.is_empty() && !selected { continue; } + (cell, selected) }; @@ -1071,7 +1079,7 @@ impl Term { .map(|span| { span.to_locations() }); - let cursor = if window_focused { + let cursor = if window_focused || !config.unfocused_hollow_cursor() { self.cursor_style.unwrap_or(self.default_cursor_style) } else { CursorStyle::HollowBlock -- cgit From facab5beef95c4b1808f7bcba92aa74bf792daf0 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 3 Aug 2018 22:12:23 +0000 Subject: Reset visible area when RIS is received When running bash and executing `echo -ne '\033c'`, the terminal should be cleared. However there was an issue with the visible area not being cleared, so all the text previously printed would still remain visible. To fix this, whenever a `reset` call is received now, the complete visible area is reset to `Cell::default()` (the default Cell) and the length of the available scrollback history is reset to `0`, which results in the scrollback history being cleared from the perspective of the user. This fixes #1483. --- src/term/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 2cc60e30..1b78c39c 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1825,7 +1825,8 @@ impl ansi::Handler for Term { self.colors = self.original_colors; self.color_modified = [false; color::COUNT]; self.cursor_style = None; - self.grid.reset(); + self.grid.clear_history(); + self.grid.region_mut(..).each(|c| c.reset(&Cell::default())); } #[inline] -- cgit From 8e8ecdd0f98dd8005cd940d19dc0a922661d64fc Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 15 Aug 2018 20:09:59 +0000 Subject: Scroll visible area when growing window Since Alacritty never had any scrollback history, the behavior when the window height was increased was to just keep the prompt on the same line it has been before the resize. However the usual behavior of terminal emulators is to keep the distance from the prompt to the bottom of the screen consistent whenever possible. This fixes this behavior by loading lines from the scrollback buffer when the window height is increased. This is only done when scrollback is available, so there are only N lines available, the maximum amount of lines which will be loaded when growing the height is N. Since the number of lines available in the alternate screen buffer is 0, it still behaves the same way it did before this patch. Different terminal emulators have different behaviors when this is done in the alt screen buffer, XTerm for example loads history from the normal screen buffer when growing the height of the window from the alternate screen buffer. Since this seems wrong (the alt screen is not supposed to have any scrollback), the behavior of Termite (VTE) has been chosen instead. In Termite the alt screen buffer never loads any scrollback history itself, however when the terminal height is grown while the alternate screen is active, the normal screen's scrollback history lines are loaded. This fixes #1502. --- src/term/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 1b78c39c..ff7a53e9 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1144,6 +1144,18 @@ impl Term { let lines = self.cursor_save_alt.point.line - num_lines + 1; self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); } + + // Move prompt down when growing if scrollback lines are available + if num_lines > old_lines { + if self.mode.contains(TermMode::ALT_SCREEN) { + let growage = min(num_lines - old_lines, Line(self.alt_grid.scroll_limit())); + self.cursor_save.point.line += growage; + } else { + let growage = min(num_lines - old_lines, Line(self.grid.scroll_limit())); + self.cursor.point.line += growage; + } + } + debug!("num_cols, num_lines = {}, {}", num_cols, num_lines); // Resize grids to new size -- cgit From 72495172c25e00799f29eb2e79fe40ddfa189866 Mon Sep 17 00:00:00 2001 From: Nathan Lilienthal Date: Sat, 1 Sep 2018 20:30:03 -0400 Subject: Implement `ansi::ClearMode::Saved` The clearing the screen for the `ansi::ClearMode::Saved` enum value has been implemented. This is used to clear all lines which are currently outside of the visible region but still inside the scrollback buffer. The specifications of XTerm indicate that the clearing of saved lines should only clear the saved lines and not the saved lines plus the currently visible part of the grid. Applications like `clear` send both the escape for clearing history plus the escape for clearing history when requested, so all sources seem to agree here. To allow both clearing the screen and the saved lines when a key is pressed the `process_key_bindings` method has been altered so multiple bindings can be specified. So it is now possible to execute both `^L` and `ClearHistory` with just a single binding. The `process_mouse_bindings` method has also been changed for consistency. To make sure everything works properly a test has been added which clears the history and then attempts to scroll. Since scrolling is the only way for a user to check if scrollback is available, this seems like a nice abstraction to check if there is a scrollback. --- src/term/mod.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index ff7a53e9..21c97671 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1797,7 +1797,9 @@ impl ansi::Handler for Term { } }, // If scrollback is implemented, this should clear it - ansi::ClearMode::Saved => return + ansi::ClearMode::Saved => { + self.grid.clear_history(); + } } } @@ -2006,9 +2008,9 @@ mod tests { use super::{Cell, Term, SizeInfo}; use term::cell; - use grid::Grid; + use grid::{Grid, Scroll}; use index::{Point, Line, Column}; - use ansi::{Handler, CharsetIndex, StandardCharset}; + use ansi::{self, Handler, CharsetIndex, StandardCharset}; use selection::Selection; use std::mem; use input::FONT_SIZE_STEP; @@ -2183,6 +2185,31 @@ mod tests { let expected_font_size: Size = config.font().size(); assert_eq!(term.font_size, expected_font_size); } + + #[test] + fn clear_saved_lines() { + let size = SizeInfo { + width: 21.0, + height: 51.0, + cell_width: 3.0, + cell_height: 3.0, + padding_x: 0.0, + padding_y: 0.0, + }; + let config: Config = Default::default(); + let mut term: Term = Term::new(&config, size); + + // Add one line of scrollback + term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); + + // Clear the history + term.clear_screen(ansi::ClearMode::Saved); + + // Make sure that scrolling does not change the grid + let mut scrolled_grid = term.grid.clone(); + scrolled_grid.scroll_display(Scroll::Top); + assert_eq!(term.grid, scrolled_grid); + } } #[cfg(all(test, feature = "bench"))] -- cgit From 43882ade33d4c14ee7248e489a2d33395faaa0b1 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 5 Sep 2018 21:15:16 +0000 Subject: Fix substraction underflow with IL sequence The IL escape sequence (CSI Ps L) allows inserting blank, uninitialized lines. `Ps` is a placeholder for the number of lines that should be inserted. Before this change Alacritty would crash when a large number of lines was passed as `Ps` parameter. The issue was caused whenever the current line of the cursor plus the lines that should be inserted would leave the bottom of the terminal, since this makes indexing impossible. This patch makes sure that the biggest amount of lines inserted does never exceed the end of the visible region minus the current line of the curser, which fixes the underflow issue. This fixes #1515. --- src/term/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/term/mod.rs') diff --git a/src/term/mod.rs b/src/term/mod.rs index 21c97671..04d110af 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -1209,9 +1209,10 @@ impl Term { /// Text moves down; clear at bottom /// Expects origin to be in scroll range. #[inline] - fn scroll_down_relative(&mut self, origin: Line, lines: Line) { + fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) { trace!("scroll_down_relative: origin={}, lines={}", origin, lines); - let lines = min(lines, self.scroll_region.end - self.scroll_region.start); + lines = min(lines, self.scroll_region.end - self.scroll_region.start); + lines = min(lines, self.scroll_region.end - origin); // Scroll between origin and bottom self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &self.cursor.template); -- cgit