diff options
Diffstat (limited to 'vendor/indicatif/src/in_memory.rs')
-rw-r--r-- | vendor/indicatif/src/in_memory.rs | 399 |
1 files changed, 0 insertions, 399 deletions
diff --git a/vendor/indicatif/src/in_memory.rs b/vendor/indicatif/src/in_memory.rs deleted file mode 100644 index 046ae14..0000000 --- a/vendor/indicatif/src/in_memory.rs +++ /dev/null @@ -1,399 +0,0 @@ -use std::fmt::{Debug, Formatter, Write as _}; -use std::io::Write as _; -use std::sync::{Arc, Mutex}; - -use vt100::Parser; - -use crate::TermLike; - -/// A thin wrapper around [`vt100::Parser`]. -/// -/// This is just an [`Arc`] around its internal state, so it can be freely cloned. -#[cfg_attr(docsrs, doc(cfg(feature = "in_memory")))] -#[derive(Debug, Clone)] -pub struct InMemoryTerm { - state: Arc<Mutex<InMemoryTermState>>, -} - -impl InMemoryTerm { - pub fn new(rows: u16, cols: u16) -> InMemoryTerm { - assert!(rows > 0, "rows must be > 0"); - assert!(cols > 0, "cols must be > 0"); - InMemoryTerm { - state: Arc::new(Mutex::new(InMemoryTermState::new(rows, cols))), - } - } - - pub fn reset(&self) { - let mut state = self.state.lock().unwrap(); - *state = InMemoryTermState::new(state.height, state.width); - } - - pub fn contents(&self) -> String { - let state = self.state.lock().unwrap(); - - // For some reason, the `Screen::contents` method doesn't include newlines in what it - // returns, making it useless for our purposes. So we need to manually reconstruct the - // contents by iterating over the rows in the terminal buffer. - let mut rows = state - .parser - .screen() - .rows(0, state.width) - .collect::<Vec<_>>(); - - // Reverse the rows and trim empty lines from the end - rows = rows - .into_iter() - .rev() - .skip_while(|line| line.is_empty()) - .map(|line| line.trim_end().to_string()) - .collect(); - - // Un-reverse the rows and join them up with newlines - rows.reverse(); - rows.join("\n") - } - - pub fn contents_formatted(&self) -> Vec<u8> { - let state = self.state.lock().unwrap(); - - // For some reason, the `Screen::contents` method doesn't include newlines in what it - // returns, making it useless for our purposes. So we need to manually reconstruct the - // contents by iterating over the rows in the terminal buffer. - let mut rows = state - .parser - .screen() - .rows_formatted(0, state.width) - .collect::<Vec<_>>(); - - // Reverse the rows and trim empty lines from the end - rows = rows - .into_iter() - .rev() - .skip_while(|line| line.is_empty()) - .collect(); - - // Un-reverse the rows - rows.reverse(); - - // Calculate buffer size - let reset = b"[m"; - let len = rows.iter().map(|line| line.len() + reset.len() + 1).sum(); - - // Join rows up with reset codes and newlines - let mut contents = rows.iter().fold(Vec::with_capacity(len), |mut acc, cur| { - acc.extend_from_slice(cur); - acc.extend_from_slice(reset); - acc.push(b'\n'); - acc - }); - - // Remove last newline again, but leave the reset code - contents.truncate(len.saturating_sub(1)); - contents - } - - pub fn moves_since_last_check(&self) -> String { - let mut s = String::new(); - for line in std::mem::take(&mut self.state.lock().unwrap().history) { - writeln!(s, "{line:?}").unwrap(); - } - s - } -} - -impl TermLike for InMemoryTerm { - fn width(&self) -> u16 { - self.state.lock().unwrap().width - } - - fn height(&self) -> u16 { - self.state.lock().unwrap().height - } - - fn move_cursor_up(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Up(n)); - state.write_str(&format!("\x1b[{n}A")) - } - } - } - - fn move_cursor_down(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Down(n)); - state.write_str(&format!("\x1b[{n}B")) - } - } - } - - fn move_cursor_right(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Right(n)); - state.write_str(&format!("\x1b[{n}C")) - } - } - } - - fn move_cursor_left(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Left(n)); - state.write_str(&format!("\x1b[{n}D")) - } - } - } - - fn write_line(&self, s: &str) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Str(s.into())); - state.history.push(Move::NewLine); - - // Don't try to handle writing lines with additional newlines embedded in them - it's not - // worth the extra code for something that indicatif doesn't even do. May revisit in future. - debug_assert!( - s.lines().count() <= 1, - "calling write_line with embedded newlines is not allowed" - ); - - // vte100 needs the full \r\n sequence to jump to the next line and reset the cursor to - // the beginning of the line. Be flexible and take either \n or \r\n - state.write_str(s)?; - state.write_str("\r\n") - } - - fn write_str(&self, s: &str) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Str(s.into())); - state.write_str(s) - } - - fn clear_line(&self) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Clear); - state.write_str("\r\x1b[2K") - } - - fn flush(&self) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Flush); - state.parser.flush() - } -} - -struct InMemoryTermState { - width: u16, - height: u16, - parser: vt100::Parser, - history: Vec<Move>, -} - -impl InMemoryTermState { - pub(crate) fn new(rows: u16, cols: u16) -> InMemoryTermState { - InMemoryTermState { - width: cols, - height: rows, - parser: Parser::new(rows, cols, 0), - history: vec![], - } - } - - pub(crate) fn write_str(&mut self, s: &str) -> std::io::Result<()> { - self.parser.write_all(s.as_bytes()) - } -} - -impl Debug for InMemoryTermState { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InMemoryTermState").finish_non_exhaustive() - } -} - -#[derive(Debug, PartialEq, Clone)] -enum Move { - Up(usize), - Down(usize), - Left(usize), - Right(usize), - Str(String), - NewLine, - Clear, - Flush, -} - -#[cfg(test)] -mod test { - use super::*; - - fn cursor_pos(in_mem: &InMemoryTerm) -> (u16, u16) { - in_mem - .state - .lock() - .unwrap() - .parser - .screen() - .cursor_position() - } - - #[test] - fn line_wrapping() { - let in_mem = InMemoryTerm::new(10, 5); - assert_eq!(cursor_pos(&in_mem), (0, 0)); - - in_mem.write_str("ABCDE").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE"); - assert_eq!(cursor_pos(&in_mem), (0, 5)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("ABCDE") -"# - ); - - // Should wrap onto next line - in_mem.write_str("FG").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE\nFG"); - assert_eq!(cursor_pos(&in_mem), (1, 2)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("FG") -"# - ); - - in_mem.write_str("HIJ").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE\nFGHIJ"); - assert_eq!(cursor_pos(&in_mem), (1, 5)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("HIJ") -"# - ); - } - - #[test] - fn write_line() { - let in_mem = InMemoryTerm::new(10, 5); - assert_eq!(cursor_pos(&in_mem), (0, 0)); - - in_mem.write_line("A").unwrap(); - assert_eq!(in_mem.contents(), "A"); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("A") -NewLine -"# - ); - - in_mem.write_line("B").unwrap(); - assert_eq!(in_mem.contents(), "A\nB"); - assert_eq!(cursor_pos(&in_mem), (2, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("B") -NewLine -"# - ); - - in_mem.write_line("Longer than cols").unwrap(); - assert_eq!(in_mem.contents(), "A\nB\nLonge\nr tha\nn col\ns"); - assert_eq!(cursor_pos(&in_mem), (6, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("Longer than cols") -NewLine -"# - ); - } - - #[test] - fn basic_functionality() { - let in_mem = InMemoryTerm::new(10, 80); - - in_mem.write_line("This is a test line").unwrap(); - assert_eq!(in_mem.contents(), "This is a test line"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("This is a test line") -NewLine -"# - ); - - in_mem.write_line("And another line!").unwrap(); - assert_eq!(in_mem.contents(), "This is a test line\nAnd another line!"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("And another line!") -NewLine -"# - ); - - in_mem.move_cursor_up(1).unwrap(); - in_mem.write_str("TEST").unwrap(); - - assert_eq!(in_mem.contents(), "This is a test line\nTESTanother line!"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Up(1) -Str("TEST") -"# - ); - } - - #[test] - fn newlines() { - let in_mem = InMemoryTerm::new(10, 10); - in_mem.write_line("LINE ONE").unwrap(); - in_mem.write_line("LINE TWO").unwrap(); - in_mem.write_line("").unwrap(); - in_mem.write_line("LINE FOUR").unwrap(); - - assert_eq!(in_mem.contents(), "LINE ONE\nLINE TWO\n\nLINE FOUR"); - - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("LINE ONE") -NewLine -Str("LINE TWO") -NewLine -Str("") -NewLine -Str("LINE FOUR") -NewLine -"# - ); - } - - #[test] - fn cursor_zero_movement() { - let in_mem = InMemoryTerm::new(10, 80); - in_mem.write_line("LINE ONE").unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - // Check that moving zero rows/cols does not actually move cursor - in_mem.move_cursor_up(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - in_mem.move_cursor_down(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - in_mem.move_cursor_right(1).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - - in_mem.move_cursor_left(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - - in_mem.move_cursor_right(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - } -} |