1
0
mirror of https://github.com/asciinema/avt.git synced 2025-04-19 05:22:19 +03:00

Optimize terminal.dump() to return shorter state string

This commit is contained in:
Marcin Kulik 2025-02-11 15:17:56 +01:00
parent 6ceba121b5
commit 09d4081092
3 changed files with 155 additions and 92 deletions

View File

@ -369,22 +369,68 @@ impl Buffer {
}
pub fn dump(&self) -> String {
let mut cutoff = 0;
let mut wrapped = false;
for (i, line) in self.view().iter().enumerate() {
if wrapped || line.wrapped || !line.is_blank() {
cutoff = i + 1;
}
wrapped = line.wrapped;
}
let last = self.rows - 1;
let mut dump = String::new();
let mut pen = Pen::default();
self.view()
.iter()
.enumerate()
.map(|(i, line)| {
let mut dump = line.dump();
if i < last && !line.wrapped {
dump.push('\r');
dump.push('\n');
for (i, line) in self.view().iter().take(cutoff).enumerate() {
for cells in line.chunks(|c1, c2| c1.pen() != c2.pen()) {
if cells[0].pen() != &pen {
dump.push_str(&cells[0].pen().dump());
pen = *cells[0].pen();
}
dump
})
.collect()
self.rep_encode_cell_text(&cells, &mut dump);
}
if i < last && !line.wrapped {
dump.push('\r');
dump.push('\n');
}
}
dump
}
fn rep_encode_cell_text(&self, cells: &[Cell], dump: &mut String) {
let mut cells = cells.iter();
let mut prev = cells.next().unwrap().char();
let mut count = 1;
for cell in cells {
if cell.char() == prev {
count += 1;
} else if count > 5 {
dump.push_str(&format!("{}\x1b[{}b", prev, count - 1));
count = 1;
prev = cell.char();
} else {
for _ in 0..count {
dump.push(prev);
}
count = 1;
prev = cell.char();
}
}
if count > 5 {
dump.push_str(&format!("{}\x1b[{}b", prev, count - 1));
} else {
for _ in 0..count {
dump.push(prev);
}
}
}
#[cfg(test)]

View File

@ -160,7 +160,13 @@ impl Line {
.count()
}
pub fn dump(&self) -> String {
pub(crate) fn is_blank(&self) -> bool {
self.cells.iter().all(|c| c.is_default())
}
}
impl std::fmt::Debug for Line {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = String::new();
for cells in self.chunks(|c1, c2| c1.pen() != c2.pen()) {
@ -171,14 +177,6 @@ impl Line {
}
}
s
}
}
impl std::fmt::Debug for Line {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = self.dump();
if self.wrapped {
s.push('⏎');
}

View File

@ -74,6 +74,16 @@ impl Default for SavedCtx {
}
}
impl SavedCtx {
fn is_default(&self) -> bool {
self.cursor_col == 0
&& self.cursor_row == 0
&& self.pen.is_default()
&& !self.origin_mode
&& self.auto_wrap_mode
}
}
impl Terminal {
pub fn new((cols, rows): (usize, usize), scrollback_limit: Option<usize>) -> Self {
let primary_buffer = Buffer::new(cols, rows, scrollback_limit, None);
@ -1261,61 +1271,66 @@ impl Terminal {
// 1. dump primary screen buffer
// TODO don't include trailing empty lines
let mut seq: String = self.primary_buffer().dump();
// 2. setup tab stops
// clear all tab stops
seq.push_str("\u{9b}5W");
if self.tabs != Tabs::new(self.cols) {
// clear all tab stops
seq.push_str("\u{9b}5W");
// set each tab stop
for t in &self.tabs {
seq.push_str(&format!("\u{9b}{}`\u{1b}[W", t + 1));
// set each tab stop
for t in &self.tabs {
seq.push_str(&format!("\u{9b}{}`\u{1b}[W", t + 1));
}
}
// 3. configure saved context for primary screen
if !primary_ctx.auto_wrap_mode {
// disable auto-wrap mode
seq.push_str("\u{9b}?7l");
if !primary_ctx.is_default() {
if !primary_ctx.auto_wrap_mode {
// disable auto-wrap mode
seq.push_str("\u{9b}?7l");
}
if primary_ctx.origin_mode {
// enable origin mode
seq.push_str("\u{9b}?6h");
}
// fix cursor in target position
seq.push_str(&format!(
"\u{9b}{};{}H",
primary_ctx.cursor_row + 1,
primary_ctx.cursor_col + 1
));
// configure pen
seq.push_str(&primary_ctx.pen.dump());
// save cursor
seq.push_str("\u{1b}7");
if !primary_ctx.auto_wrap_mode {
// re-enable auto-wrap mode
seq.push_str("\u{9b}?7h");
}
if primary_ctx.origin_mode {
// re-disable origin mode
seq.push_str("\u{9b}?6l");
}
}
if primary_ctx.origin_mode {
// enable origin mode
seq.push_str("\u{9b}?6h");
}
// fix cursor in target position
seq.push_str(&format!(
"\u{9b}{};{}H",
primary_ctx.cursor_row + 1,
primary_ctx.cursor_col + 1
));
// configure pen
seq.push_str(&primary_ctx.pen.dump());
// save cursor
seq.push_str("\u{1b}7");
// prevent pen bleed into alt screen buffer
seq.push_str("\u{1b}[m");
if !primary_ctx.auto_wrap_mode {
// re-enable auto-wrap mode
seq.push_str("\u{9b}?7h");
}
if primary_ctx.origin_mode {
// re-disable origin mode
seq.push_str("\u{9b}?6l");
}
// 4. dump alternate screen buffer
// switch to alternate screen
seq.push_str("\u{9b}?1047h");
if self.active_buffer_type == BufferType::Alternate || !alternate_ctx.is_default() {
seq.push_str("\u{9b}?1047h");
}
if self.active_buffer_type == BufferType::Alternate {
// move cursor home
@ -1327,42 +1342,44 @@ impl Terminal {
// 5. configure saved context for alternate screen
if !alternate_ctx.auto_wrap_mode {
// disable auto-wrap mode
seq.push_str("\u{9b}?7l");
}
if !alternate_ctx.is_default() {
if !alternate_ctx.auto_wrap_mode {
// disable auto-wrap mode
seq.push_str("\u{9b}?7l");
}
if alternate_ctx.origin_mode {
// enable origin mode
seq.push_str("\u{9b}?6h");
}
if alternate_ctx.origin_mode {
// enable origin mode
seq.push_str("\u{9b}?6h");
}
// fix cursor in target position
seq.push_str(&format!(
"\u{9b}{};{}H",
alternate_ctx.cursor_row + 1,
alternate_ctx.cursor_col + 1
));
// fix cursor in target position
seq.push_str(&format!(
"\u{9b}{};{}H",
alternate_ctx.cursor_row + 1,
alternate_ctx.cursor_col + 1
));
// configure pen
seq.push_str(&alternate_ctx.pen.dump());
// configure pen
seq.push_str(&alternate_ctx.pen.dump());
// save cursor
seq.push_str("\u{1b}7");
// save cursor
seq.push_str("\u{1b}7");
if !alternate_ctx.auto_wrap_mode {
// re-enable auto-wrap mode
seq.push_str("\u{9b}?7h");
}
if !alternate_ctx.auto_wrap_mode {
// re-enable auto-wrap mode
seq.push_str("\u{9b}?7h");
}
if alternate_ctx.origin_mode {
// re-disable origin mode
seq.push_str("\u{9b}?6l");
if alternate_ctx.origin_mode {
// re-disable origin mode
seq.push_str("\u{9b}?6l");
}
}
// 6. ensure the right buffer is active
if self.active_buffer_type == BufferType::Primary {
if self.active_buffer_type == BufferType::Primary && !alternate_ctx.is_default() {
// switch back to primary screen
seq.push_str("\u{9b}?1047l");
}
@ -1378,11 +1395,13 @@ impl Terminal {
// 8. setup margins
// note: this resets cursor position - must be done before fixing cursor
seq.push_str(&format!(
"\u{9b}{};{}r",
self.top_margin + 1,
self.bottom_margin + 1
));
if self.top_margin > 0 || self.bottom_margin < self.rows - 1 {
seq.push_str(&format!(
"\u{9b}{};{}r",
self.top_margin + 1,
self.bottom_margin + 1
));
}
// 9. setup cursor
@ -1432,7 +1451,7 @@ impl Terminal {
}
if self.cursor.col >= self.cols {
// move cursor past right border by re-printing the character in
// move cursor past the right border by re-printing the character in
// the last column
let cell = self.buffer[(self.cols - 1, self.cursor.row)];
seq.push_str(&format!("{}{}", cell.pen().dump(), cell.char()));
@ -1447,7 +1466,7 @@ impl Terminal {
}
// Following 3 steps must happen after ALL prints as they alter print behaviour,
// including the "move cursor past right border one" above.
// including the "move cursor past the right border one" above.
// 10. setup charset