summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--alacritty/src/display.rs4
-rw-r--r--alacritty/src/event.rs3
-rw-r--r--alacritty/src/renderer/mod.rs68
-rw-r--r--alacritty/src/renderer/rects.rs10
-rw-r--r--alacritty/src/url.rs22
-rw-r--r--alacritty_terminal/src/grid/mod.rs95
-rw-r--r--alacritty_terminal/src/grid/resize.rs47
-rw-r--r--alacritty_terminal/src/grid/row.rs54
-rw-r--r--alacritty_terminal/src/grid/storage.rs415
-rw-r--r--alacritty_terminal/src/grid/tests.rs42
-rw-r--r--alacritty_terminal/src/index.rs4
-rw-r--r--alacritty_terminal/src/term/cell.rs151
-rw-r--r--alacritty_terminal/src/term/mod.rs169
-rw-r--r--alacritty_terminal/src/term/search.rs39
-rw-r--r--alacritty_terminal/src/vi_mode.rs8
-rw-r--r--alacritty_terminal/tests/ref.rs6
-rw-r--r--alacritty_terminal/tests/ref/alt_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/clear_underline/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/colored_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/csi_rep/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/decaln_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/deccolm_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/delete_chars_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/delete_lines/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/erase_chars_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/fish_cc/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/grid_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/history/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/indexed_256_colors/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/insert_blank_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/issue_855/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/ll/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/newline_with_cursor_beyond_scroll_region/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/region_scroll_down/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/row_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/saved_cursor/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/saved_cursor_alt/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/scroll_up_reset/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/selective_erasure/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/sgr/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/tab_rendering/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/tmux_git_log/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/tmux_htop/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/underline/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vim_24bitcolors_bce/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vim_large_window_scroll/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vim_simple_edit/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_cursor_movement_1/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_insert/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_origin_mode_1/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_origin_mode_2/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_scroll/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/vttest_tab_clear_set/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/wrapline_alt_toggle/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/zerowidth/alacritty.recording4
-rw-r--r--alacritty_terminal/tests/ref/zerowidth/config.json2
-rw-r--r--alacritty_terminal/tests/ref/zerowidth/grid.json2
-rw-r--r--alacritty_terminal/tests/ref/zerowidth/size.json2
-rw-r--r--alacritty_terminal/tests/ref/zsh_tab_completion/grid.json2
60 files changed, 629 insertions, 598 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35d3b92f..9b0b17a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Use yellow/red from the config for error and warning messages instead of fixed colors
- Existing CLI parameters are now passed to instances spawned using `SpawnNewInstance`
- Wayland's Client side decorations now use the search bar colors
+- Reduce memory usage by up to at least 30% with a full scrollback buffer
+- The number of zerowidth characters per cell is no longer limited to 5
### Fixed
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
index 600ce2a4..6d683336 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display.rs
@@ -473,10 +473,10 @@ impl Display {
// Iterate over all non-empty cells in the grid.
for cell in grid_cells {
// Update URL underlines.
- urls.update(size_info.cols(), cell);
+ urls.update(size_info.cols(), &cell);
// Update underline/strikeout.
- lines.update(cell);
+ lines.update(&cell);
// Draw the cell.
api.render_cell(cell, glyph_cache);
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 6c5318f6..0f9c24a5 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -34,7 +34,6 @@ use alacritty_terminal::grid::{Dimensions, Scroll};
use alacritty_terminal::index::{Boundary, Column, Direction, Line, Point, Side};
use alacritty_terminal::selection::{Selection, SelectionType};
use alacritty_terminal::sync::FairMutex;
-use alacritty_terminal::term::cell::Cell;
use alacritty_terminal::term::{ClipboardType, SizeInfo, Term, TermMode};
#[cfg(not(windows))]
use alacritty_terminal::tty;
@@ -1174,7 +1173,7 @@ impl<N: Notify + OnResize> Processor<N> {
fn write_ref_test_results<T>(&self, terminal: &Term<T>) {
// Dump grid state.
let mut grid = terminal.grid().clone();
- grid.initialize_all(Cell::default());
+ grid.initialize_all();
grid.truncate();
let serialized_grid = json::to_string(&grid).expect("serialize grid");
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index e97ac025..f628d24f 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -19,7 +19,7 @@ use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use alacritty_terminal::config::Cursor;
use alacritty_terminal::index::{Column, Line};
-use alacritty_terminal::term::cell::{self, Flags};
+use alacritty_terminal::term::cell::Flags;
use alacritty_terminal::term::color::Rgb;
use alacritty_terminal::term::{CursorKey, RenderableCell, RenderableCellContent, SizeInfo};
use alacritty_terminal::thread;
@@ -436,7 +436,7 @@ impl Batch {
Self { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
}
- pub fn add_item(&mut self, cell: RenderableCell, glyph: &Glyph) {
+ pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
if self.is_empty() {
self.tex = glyph.tex_id;
}
@@ -953,11 +953,7 @@ impl<'a> RenderApi<'a> {
.map(|(i, c)| RenderableCell {
line,
column: Column(i),
- inner: RenderableCellContent::Chars({
- let mut chars = [' '; cell::MAX_ZEROWIDTH_CHARS + 1];
- chars[0] = c;
- chars
- }),
+ inner: RenderableCellContent::Chars((c, None)),
flags: Flags::empty(),
bg_alpha,
fg,
@@ -971,7 +967,7 @@ impl<'a> RenderApi<'a> {
}
#[inline]
- fn add_render_item(&mut self, cell: RenderableCell, glyph: &Glyph) {
+ fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
// Flush batch if tex changing.
if !self.batch.is_empty() && self.batch.tex != glyph.tex_id {
self.render_batch();
@@ -985,8 +981,8 @@ impl<'a> RenderApi<'a> {
}
}
- pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) {
- let chars = match cell.inner {
+ pub fn render_cell(&mut self, mut cell: RenderableCell, glyph_cache: &mut GlyphCache) {
+ let (mut c, zerowidth) = match cell.inner {
RenderableCellContent::Cursor(cursor_key) => {
// Raw cell pixel buffers like cursors don't need to go through font lookup.
let metrics = glyph_cache.metrics;
@@ -1000,10 +996,10 @@ impl<'a> RenderApi<'a> {
self.cursor_config.thickness(),
))
});
- self.add_render_item(cell, glyph);
+ self.add_render_item(&cell, glyph);
return;
},
- RenderableCellContent::Chars(chars) => chars,
+ RenderableCellContent::Chars((c, ref mut zerowidth)) => (c, zerowidth.take()),
};
// Get font key for cell.
@@ -1014,37 +1010,33 @@ impl<'a> RenderApi<'a> {
_ => glyph_cache.font_key,
};
- // Don't render text of HIDDEN cells.
- let mut chars = if cell.flags.contains(Flags::HIDDEN) {
- [' '; cell::MAX_ZEROWIDTH_CHARS + 1]
- } else {
- chars
- };
-
- // Render tabs as spaces in case the font doesn't support it.
- if chars[0] == '\t' {
- chars[0] = ' ';
+ // Ignore hidden cells and render tabs as spaces to prevent font issues.
+ let hidden = cell.flags.contains(Flags::HIDDEN);
+ if c == '\t' || hidden {
+ c = ' ';
}
- let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
+ let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c };
// Add cell to batch.
let glyph = glyph_cache.get(glyph_key, self);
- self.add_render_item(cell, glyph);
-
- // Render zero-width characters.
- for c in (&chars[1..]).iter().filter(|c| **c != ' ') {
- glyph_key.c = *c;
- let mut glyph = *glyph_cache.get(glyph_key, self);
-
- // The metrics of zero-width characters are based on rendering
- // the character after the current cell, with the anchor at the
- // right side of the preceding character. Since we render the
- // zero-width characters inside the preceding character, the
- // anchor has been moved to the right by one cell.
- glyph.left += glyph_cache.metrics.average_advance as i16;
-
- self.add_render_item(cell, &glyph);
+ self.add_render_item(&cell, glyph);
+
+ // Render visible zero-width characters.
+ if let Some(zerowidth) = zerowidth.filter(|_| !hidden) {
+ for c in zerowidth {
+ glyph_key.c = c;
+ let mut glyph = *glyph_cache.get(glyph_key, self);
+
+ // The metrics of zero-width characters are based on rendering
+ // the character after the current cell, with the anchor at the
+ // right side of the preceding character. Since we render the
+ // zero-width characters inside the preceding character, the
+ // anchor has been moved to the right by one cell.
+ glyph.left += glyph_cache.metrics.average_advance as i16;
+
+ self.add_render_item(&cell, &glyph);
+ }
}
}
}
diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs
index 31e8b82a..fcd8c82e 100644
--- a/alacritty/src/renderer/rects.rs
+++ b/alacritty/src/renderer/rects.rs
@@ -150,14 +150,14 @@ impl RenderLines {
/// Update the stored lines with the next cell info.
#[inline]
- pub fn update(&mut self, cell: RenderableCell) {
- self.update_flag(cell, Flags::UNDERLINE);
- self.update_flag(cell, Flags::DOUBLE_UNDERLINE);
- self.update_flag(cell, Flags::STRIKEOUT);
+ pub fn update(&mut self, cell: &RenderableCell) {
+ self.update_flag(&cell, Flags::UNDERLINE);
+ self.update_flag(&cell, Flags::DOUBLE_UNDERLINE);
+ self.update_flag(&cell, Flags::STRIKEOUT);
}
/// Update the lines for a specific flag.
- fn update_flag(&mut self, cell: RenderableCell, flag: Flags) {
+ fn update_flag(&mut self, cell: &RenderableCell, flag: Flags) {
if !cell.flags.contains(flag) {
return;
}
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
index f969c7af..f3f60dd3 100644
--- a/alacritty/src/url.rs
+++ b/alacritty/src/url.rs
@@ -48,7 +48,7 @@ impl Url {
pub struct Urls {
locator: UrlLocator,
urls: Vec<Url>,
- scheme_buffer: Vec<RenderableCell>,
+ scheme_buffer: Vec<(Point, Rgb)>,
last_point: Option<Point>,
state: UrlLocation,
}
@@ -71,10 +71,10 @@ impl Urls {
}
// Update tracked URLs.
- pub fn update(&mut self, num_cols: Column, cell: RenderableCell) {
+ pub fn update(&mut self, num_cols: Column, cell: &RenderableCell) {
// Convert cell to character.
- let c = match cell.inner {
- RenderableCellContent::Chars(chars) => chars[0],
+ let c = match &cell.inner {
+ RenderableCellContent::Chars((c, _zerowidth)) => *c,
RenderableCellContent::Cursor(_) => return,
};
@@ -109,9 +109,8 @@ impl Urls {
self.urls.push(Url { lines: Vec::new(), end_offset, num_cols });
// Push schemes into URL.
- for scheme_cell in self.scheme_buffer.split_off(0) {
- let point = scheme_cell.into();
- self.extend_url(point, point, scheme_cell.fg, end_offset);
+ for (scheme_point, scheme_fg) in self.scheme_buffer.split_off(0) {
+ self.extend_url(scheme_point, scheme_point, scheme_fg, end_offset);
}
// Push the new cell into URL.
@@ -120,7 +119,7 @@ impl Urls {
(UrlLocation::Url(_length, end_offset), UrlLocation::Url(..)) => {
self.extend_url(point, end, cell.fg, end_offset);
},
- (UrlLocation::Scheme, _) => self.scheme_buffer.push(cell),
+ (UrlLocation::Scheme, _) => self.scheme_buffer.push((cell.into(), cell.fg)),
(UrlLocation::Reset, _) => self.reset(),
_ => (),
}
@@ -196,13 +195,12 @@ mod tests {
use super::*;
use alacritty_terminal::index::{Column, Line};
- use alacritty_terminal::term::cell::MAX_ZEROWIDTH_CHARS;
fn text_to_cells(text: &str) -> Vec<RenderableCell> {
text.chars()
.enumerate()
.map(|(i, c)| RenderableCell {
- inner: RenderableCellContent::Chars([c; MAX_ZEROWIDTH_CHARS + 1]),
+ inner: RenderableCellContent::Chars((c, None)),
line: Line(0),
column: Column(i),
fg: Default::default(),
@@ -223,7 +221,7 @@ mod tests {
let mut urls = Urls::new();
for cell in input {
- urls.update(Column(num_cols), cell);
+ urls.update(Column(num_cols), &cell);
}
let url = urls.urls.first().unwrap();
@@ -239,7 +237,7 @@ mod tests {
let mut urls = Urls::new();
for cell in input {
- urls.update(Column(num_cols), cell);
+ urls.update(Column(num_cols), &cell);
}
assert_eq!(urls.urls.len(), 3);
diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs
index 5ab25c78..70dbc936 100644
--- a/alacritty_terminal/src/grid/mod.rs
+++ b/alacritty_terminal/src/grid/mod.rs
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use crate::ansi::{CharsetIndex, StandardCharset};
use crate::index::{Column, IndexRange, Line, Point};
-use crate::term::cell::{Cell, Flags};
+use crate::term::cell::{Flags, ResetDiscriminant};
pub mod resize;
mod row;
@@ -49,25 +49,24 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
}
}
-pub trait GridCell {
+pub trait GridCell: Sized {
+ /// Check if the cell contains any content.
fn is_empty(&self) -> bool;
+
+ /// Perform an opinionated cell reset based on a template cell.
+ fn reset(&mut self, template: &Self);
+
fn flags(&self) -> &Flags;
fn flags_mut(&mut self) -> &mut Flags;
-
- /// Fast equality approximation.
- ///
- /// This is a faster alternative to [`PartialEq`],
- /// but might report unequal cells as equal.
- fn fast_eq(&self, other: Self) -> bool;
}
-#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-pub struct Cursor {
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct Cursor<T> {
/// The location of this cursor.
pub point: Point,
/// Template cell when using this cursor.
- pub template: Cell,
+ pub template: T,
/// Currently configured graphic character sets.
pub charsets: Charsets,
@@ -131,11 +130,11 @@ impl IndexMut<CharsetIndex> for Charsets {
pub struct Grid<T> {
/// Current cursor for writing data.
#[serde(skip)]
- pub cursor: Cursor,
+ pub cursor: Cursor<T>,
/// Last saved cursor.
#[serde(skip)]
- pub saved_cursor: Cursor,
+ pub saved_cursor: Cursor<T>,
/// Lines in the grid. Each row holds a list of cells corresponding to the
/// columns in that row.
@@ -167,10 +166,10 @@ pub enum Scroll {
Bottom,
}
-impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
- pub fn new(lines: Line, cols: Column, max_scroll_limit: usize, template: T) -> Grid<T> {
+impl<T: GridCell + Default + PartialEq + Clone> Grid<T> {
+ pub fn new(lines: Line, cols: Column, max_scroll_limit: usize) -> Grid<T> {
Grid {
- raw: Storage::with_capacity(lines, Row::new(cols, template)),
+ raw: Storage::with_capacity(lines, cols),
max_scroll_limit,
display_offset: 0,
saved_cursor: Cursor::default(),
@@ -203,10 +202,10 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
};
}
- fn increase_scroll_limit(&mut self, count: usize, template: T) {
+ fn increase_scroll_limit(&mut self, count: usize) {
let count = min(count, self.max_scroll_limit - self.history_size());
if count != 0 {
- self.raw.initialize(count, template, self.cols);
+ self.raw.initialize(count, self.cols);
}
}
@@ -219,7 +218,11 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
}
#[inline]
- pub fn scroll_down(&mut self, region: &Range<Line>, positions: Line, template: T) {
+ pub fn scroll_down<D>(&mut self, region: &Range<Line>, positions: Line)
+ where
+ T: ResetDiscriminant<D>,
+ D: PartialEq,
+ {
// Whether or not there is a scrolling region active, as long as it
// starts at the top, we can do a full rotation which just involves
// changing the start index.
@@ -238,7 +241,7 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
// Finally, reset recycled lines.
for i in IndexRange(Line(0)..positions) {
- self.raw[i].reset(template);
+ self.raw[i].reset(&self.cursor.template);
}
} else {
// Subregion rotation.
@@ -247,7 +250,7 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
}
for line in IndexRange(region.start..(region.start + positions)) {
- self.raw[line].reset(template);
+ self.raw[line].reset(&self.cursor.template);
}
}
}
@@ -255,7 +258,11 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
/// Move lines at the bottom toward the top.
///
/// This is the performance-sensitive part of scrolling.
- pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: T) {
+ pub fn scroll_up<D>(&mut self, region: &Range<Line>, positions: Line)
+ where
+ T: ResetDiscriminant<D>,
+ D: PartialEq,
+ {
let num_lines = self.screen_lines().0;
if region.start == Line(0) {
@@ -264,7 +271,7 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
self.display_offset = min(self.display_offset + *positions, self.max_scroll_limit);
}
- self.increase_scroll_limit(*positions, template);
+ self.increase_scroll_limit(*positions);
// Rotate the entire line buffer. If there's a scrolling region
// active, the bottom lines are restored in the next step.
@@ -284,7 +291,7 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
//
// Recycled lines are just above the end of the scrolling region.
for i in 0..*positions {
- self.raw[i + fixed_lines].reset(template);
+ self.raw[i + fixed_lines].reset(&self.cursor.template);
}
} else {
// Subregion rotation.
@@ -294,12 +301,16 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
// Clear reused lines.
for line in IndexRange((region.end - positions)..region.end) {
- self.raw[line].reset(template);
+ self.raw[line].reset(&self.cursor.template);
}
}
}
- pub fn clear_viewport(&mut self, template: T) {
+ pub fn clear_viewport<D>(&mut self)
+ where
+ T: ResetDiscriminant<D>,
+ D: PartialEq,
+ {
// Determine how many lines to scroll up by.
let end = Point { line: 0, col: self.cols() };
let mut iter = self.iter_from(end);
@@ -316,26 +327,30 @@ impl<T: GridCell + Default + PartialEq + Copy> Grid<T> {
self.display_offset = 0;
// Clear the viewport.
- self.scroll_up(&region, positions, template);
+ self.scroll_up(&region, positions);
// Reset rotated lines.
for i in positions.0..self.lines.0 {
- self.raw[i].reset(template);
+ self.raw[i].reset(&self.cursor.template);
}
}
/// Completely reset the grid state.
- pub fn reset(&mut self, template: T) {
+ pub fn reset<D>(&mut self)
+ where
+ T: ResetDiscriminant<D>,
+ D: PartialEq,
+ {