diff --git a/src/grid.rs b/src/grid.rs index 858f646..5f7dad9 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,4 +1,4 @@ -use crate::term::BufWrite as _; +use crate::{row::Row, term::BufWrite as _}; use std::convert::TryInto as _; #[derive(Clone, Debug)] @@ -16,6 +16,11 @@ pub struct Grid { scrollback_offset: usize, } +pub enum RowFilter { + All, + Visible, +} + impl Grid { pub fn new(size: Size, scrollback_len: usize) -> Self { Self { @@ -106,6 +111,10 @@ impl Grid { self.origin_mode = self.saved_origin_mode; } + pub fn all_rows(&self) -> impl Iterator { + self.scrollback.iter().chain(self.rows.iter()) + } + pub fn visible_rows(&self) -> impl Iterator { let scrollback_len = self.scrollback.len(); let rows_len = self.rows.len(); @@ -199,6 +208,7 @@ impl Grid { pub fn write_contents_formatted( &self, + rows: RowFilter, contents: &mut Vec, ) -> crate::attrs::Attrs { crate::term::ClearAttrs::default().write_buf(contents); @@ -207,7 +217,8 @@ impl Grid { let mut prev_attrs = crate::attrs::Attrs::default(); let mut prev_pos = Pos::default(); let mut wrapping = false; - for (i, row) in self.visible_rows().enumerate() { + + let mut process_row = |i: usize, row: &Row| { let i = i.try_into().unwrap(); let (new_pos, new_attrs) = row.write_contents_formatted( contents, @@ -221,6 +232,19 @@ impl Grid { prev_pos = new_pos; prev_attrs = new_attrs; wrapping = row.wrapped(); + }; + + match rows { + RowFilter::All => { + for (i, row) in self.all_rows().enumerate() { + process_row(i, row); + } + } + RowFilter::Visible => { + for (i, row) in self.visible_rows().enumerate() { + process_row(i, row); + } + } } // writing a character to the last column of a row doesn't wrap the diff --git a/src/screen.rs b/src/screen.rs index a47bfa1..48ccedf 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -1,4 +1,9 @@ -use crate::term::BufWrite as _; +use crate::{ + grid::RowFilter, + term::{ + BufWrite as _, DisableAlternateScreen, EnableAlternateScreen, MoveTo, + }, +}; use std::convert::TryInto as _; use unicode_width::UnicodeWidthChar as _; @@ -239,13 +244,63 @@ impl Screen { #[must_use] pub fn contents_formatted(&self) -> Vec { let mut contents = vec![]; - self.write_contents_formatted(&mut contents); + self.write_contents_formatted( + self.grid(), + RowFilter::Visible, + &mut contents, + ); + contents + } + + /// Returns all output stored in the terminal, including both buffers (alternate and primary), and all scrollback + /// + /// Formatting information will be included inline as terminal escape + /// codes. The result will be suitable for feeding directly to a raw + /// terminal parser, and will result in the same visual output. + #[must_use] + pub fn all_contents_formatted(&self) -> Vec { + let mut contents = vec![]; + if self.modes.contains(Mode::AlternateScreen) { + self.write_contents_formatted( + &self.grid, + RowFilter::All, + &mut contents, + ); + + EnableAlternateScreen::default().write_buf(&mut contents); + MoveTo::default().write_buf(&mut contents); + self.write_contents_formatted( + &self.alternate_grid, + RowFilter::All, + &mut contents, + ); + } else { + EnableAlternateScreen::default().write_buf(&mut contents); + self.write_contents_formatted( + &self.alternate_grid, + RowFilter::All, + &mut contents, + ); + + DisableAlternateScreen::default().write_buf(&mut contents); + MoveTo::default().write_buf(&mut contents); + self.write_contents_formatted( + &self.grid, + RowFilter::All, + &mut contents, + ); + } contents } - fn write_contents_formatted(&self, contents: &mut Vec) { + fn write_contents_formatted( + &self, + grid: &crate::grid::Grid, + rows: RowFilter, + contents: &mut Vec, + ) { crate::term::HideCursor::new(self.hide_cursor()).write_buf(contents); - let prev_attrs = self.grid().write_contents_formatted(contents); + let prev_attrs = grid.write_contents_formatted(rows, contents); self.attrs.write_escape_code_diff(contents, &prev_attrs); } diff --git a/src/term.rs b/src/term.rs index 31e1f51..f197ab1 100644 --- a/src/term.rs +++ b/src/term.rs @@ -606,3 +606,23 @@ impl BufWrite for MouseProtocolEncoding { } } } + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct DisableAlternateScreen; + +impl BufWrite for DisableAlternateScreen { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b[?1049l"); + } +} + +#[derive(Default, Debug)] +#[must_use = "this struct does nothing unless you call write_buf"] +pub struct EnableAlternateScreen; + +impl BufWrite for EnableAlternateScreen { + fn write_buf(&self, buf: &mut Vec) { + buf.extend_from_slice(b"\x1b[?1049h"); + } +}