1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

termwiz: avoid cluster -> vec conversions in a few more cases

This reduces the resident memory by another ~10% because it avoids
keeping as many runs of whitespace.

Runtime for `time cat enwiki8.wiki` is still ~11-12s, resident: 530K

refs: https://github.com/wez/wezterm/issues/1626
This commit is contained in:
Wez Furlong 2022-07-24 07:57:33 -07:00
parent 8002a17242
commit 7be01110ca
3 changed files with 69 additions and 38 deletions

View File

@ -509,39 +509,24 @@ fn basic_output() {
term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay); term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay);
term.cup(1, 1); term.cup(1, 1);
term.print("hello, world!"); term.print("hello, world!");
assert_visible_contents( assert_visible_contents(&term, file!(), line!(), &["", " hello, wo", "rld!", "", ""]);
&term,
file!(),
line!(),
&[" ", " hello, wo", "rld!", "", ""],
);
term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay); term.erase_in_display(EraseInDisplay::EraseToStartOfDisplay);
assert_visible_contents( assert_visible_contents(
&term, &term,
file!(), file!(),
line!(), line!(),
&[" ", " ", " ", "", ""], &["", " ", " ", "", ""],
); );
term.cup(0, 2); term.cup(0, 2);
term.print("woot"); term.print("woot");
term.cup(2, 2); term.cup(2, 2);
term.erase_in_line(EraseInLine::EraseToEndOfLine); term.erase_in_line(EraseInLine::EraseToEndOfLine);
assert_visible_contents( assert_visible_contents(&term, file!(), line!(), &["", " ", "wo", "", ""]);
&term,
file!(),
line!(),
&[" ", " ", "wo", "", ""],
);
term.erase_in_line(EraseInLine::EraseToStartOfLine); term.erase_in_line(EraseInLine::EraseToStartOfLine);
assert_visible_contents( assert_visible_contents(&term, file!(), line!(), &["", " ", " ", "", ""]);
&term,
file!(),
line!(),
&[" ", " ", " ", "", ""],
);
} }
/// Ensure that we dirty lines as the cursor is moved around, otherwise /// Ensure that we dirty lines as the cursor is moved around, otherwise

View File

@ -11,6 +11,7 @@ keywords = ["terminal", "readline", "console", "curses"]
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
# backtrace = "0.3"
base64 = "0.13" base64 = "0.13"
bitflags = "1.3" bitflags = "1.3"
cassowary = {version="0.3", optional=true} cassowary = {version="0.3", optional=true}

View File

@ -682,22 +682,20 @@ impl Line {
} }
if let CellStorage::C(cl) = &mut self.cells { if let CellStorage::C(cl) = &mut self.cells {
if idx == cl.len { if idx >= cl.len && cell == Cell::blank() {
cl.append(cell);
return;
}
if idx > cl.len
&& cell.str() == " "
&& cl
.clusters
.last()
.map(|c| c.attrs == *cell.attrs())
.unwrap_or_else(|| *cell.attrs() == CellAttributes::default())
{
// Appending blank beyond end of line; is already // Appending blank beyond end of line; is already
// implicitly blank // implicitly blank
return; return;
} }
while cl.len < idx {
// Fill out any implied blanks until we can append
// their intended cell content
cl.append(Cell::blank());
}
if idx == cl.len {
cl.append(cell);
return;
}
/* /*
log::info!( log::info!(
"cannot append {cell:?} to {:?} as idx={idx} and cl.len is {}", "cannot append {cell:?} to {:?} as idx={idx} and cl.len is {}",
@ -837,11 +835,12 @@ impl Line {
} }
pub fn prune_trailing_blanks(&mut self, seqno: SequenceNo) { pub fn prune_trailing_blanks(&mut self, seqno: SequenceNo) {
if let CellStorage::C(cl) = &self.cells { if let CellStorage::C(cl) = &mut self.cells {
if !cl.text.ends_with(' ') { if cl.prune_trailing_blanks() {
// There are no trailing blanks self.update_last_change_seqno(seqno);
return; self.invalidate_zones();
} }
return;
} }
let def_attr = CellAttributes::blank(); let def_attr = CellAttributes::blank();
@ -857,6 +856,11 @@ impl Line {
} }
pub fn fill_range(&mut self, cols: Range<usize>, cell: &Cell, seqno: SequenceNo) { pub fn fill_range(&mut self, cols: Range<usize>, cell: &Cell, seqno: SequenceNo) {
if self.len() == 0 && *cell == Cell::blank() {
// We would be filling it with blanks only to prune
// them all away again before we return; NOP
return;
}
for x in cols { for x in cols {
// FIXME: we can skip the look-back for second and subsequent iterations // FIXME: we can skip the look-back for second and subsequent iterations
self.set_cell_impl(x, cell.clone(), true, seqno); self.set_cell_impl(x, cell.clone(), true, seqno);
@ -904,6 +908,7 @@ impl Line {
CellStorage::V(_) => return, CellStorage::V(_) => return,
CellStorage::C(cl) => cl.to_cell_vec(), CellStorage::C(cl) => cl.to_cell_vec(),
}; };
// log::info!("make_cells\n{:?}", backtrace::Backtrace::new());
self.cells = CellStorage::V(VecStorage::new(cells)); self.cells = CellStorage::V(VecStorage::new(cells));
} }
@ -967,9 +972,19 @@ impl Line {
/// to this line. /// to this line.
/// This function is used by rewrapping logic when joining wrapped /// This function is used by rewrapping logic when joining wrapped
/// lines back together. /// lines back together.
pub fn append_line(&mut self, mut other: Line, seqno: SequenceNo) { pub fn append_line(&mut self, other: Line, seqno: SequenceNo) {
self.coerce_vec_storage() match &mut self.cells {
.append(&mut other.coerce_vec_storage()); CellStorage::V(cells) => {
for cell in other.visible_cells() {
cells.push(cell.as_cell());
}
}
CellStorage::C(cl) => {
for cell in other.visible_cells() {
cl.append(cell.as_cell());
}
}
}
self.update_last_change_seqno(seqno); self.update_last_change_seqno(seqno);
self.invalidate_zones(); self.invalidate_zones();
} }
@ -1450,6 +1465,36 @@ impl ClusteredLine {
self.len += cell_width; self.len += cell_width;
} }
fn prune_trailing_blanks(&mut self) -> bool {
let num_spaces = self.text.chars().rev().take_while(|&c| c == ' ').count();
if num_spaces == 0 {
return false;
}
let blank = CellAttributes::blank();
let mut pruned = false;
for _ in 0..num_spaces {
let mut need_pop = false;
if let Some(cluster) = self.clusters.last_mut() {
if cluster.attrs != blank {
break;
}
cluster.cell_width -= 1;
self.text.pop();
self.len -= 1;
pruned = true;
if cluster.cell_width == 0 {
need_pop = true;
}
}
if need_pop {
self.clusters.pop();
}
}
pruned
}
fn set_last_cell_was_wrapped(&mut self, wrapped: bool) { fn set_last_cell_was_wrapped(&mut self, wrapped: bool) {
if let Some(last_cell) = self.iter().last() { if let Some(last_cell) = self.iter().last() {
if last_cell.attrs().wrapped() == wrapped { if last_cell.attrs().wrapped() == wrapped {