From fbc4db18ecea9755199b945368c02f32c0481c95 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 9 Nov 2021 21:23:19 -0500 Subject: basic command running and display --- src/history.rs | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/history.rs (limited to 'src/history.rs') diff --git a/src/history.rs b/src/history.rs new file mode 100644 index 0000000..339e53d --- /dev/null +++ b/src/history.rs @@ -0,0 +1,126 @@ +use async_std::io::ReadExt as _; +use pty_process::Command as _; +use textmode::Textmode as _; + +#[derive(Default)] +pub struct History { + entries: Vec>>, +} + +impl History { + pub fn new() -> Self { + Self::default() + } + + pub async fn run( + &mut self, + cmd: &str, + render: async_std::channel::Sender<()>, + ) -> anyhow::Result<()> { + let (exe, args) = parse_cmd(cmd); + let mut process = async_process::Command::new(&exe); + process.args(&args); + let child = process + .spawn_pty(Some(&pty_process::Size::new(24, 80))) + .unwrap(); + let entry = async_std::sync::Arc::new(async_std::sync::Mutex::new( + HistoryEntry::new(cmd), + )); + let task_entry = async_std::sync::Arc::clone(&entry); + let task_render = render.clone(); + async_std::task::spawn(async move { + loop { + let mut buf = [0_u8; 4096]; + match child.pty().read(&mut buf).await { + Ok(bytes) => { + task_entry.lock_arc().await.vt.process(&buf[..bytes]); + } + Err(e) => { + if e.raw_os_error() != Some(libc::EIO) { + eprintln!("pty read failed: {:?}", e); + } + task_entry.lock_arc().await.running = false; + task_render.send(()).await.unwrap(); + break; + } + } + task_render.send(()).await.unwrap(); + } + }); + self.entries.push(entry); + render.send(()).await.unwrap(); + Ok(()) + } + + pub async fn render( + &self, + out: &mut textmode::Output, + repl_lines: usize, + ) -> anyhow::Result<()> { + let mut used_lines = repl_lines; + for entry in self.entries.iter().rev() { + let entry = entry.lock_arc().await; + let screen = entry.vt.screen(); + let mut last_row = 0; + for (idx, row) in screen.rows(0, 80).enumerate() { + if !row.is_empty() { + last_row = idx + 1; + } + } + used_lines += 1 + std::cmp::min(6, last_row); + if used_lines > 24 { + break; + } + out.move_to((24 - used_lines).try_into().unwrap(), 0); + out.write_str("$ "); + if entry.running { + out.set_bgcolor(vt100::Color::Rgb(16, 64, 16)); + } + out.write_str(&entry.cmd); + out.reset_attributes(); + out.write(b"\r\n"); + if last_row > 5 { + out.set_bgcolor(textmode::color::RED); + out.write(b"..."); + out.reset_attributes(); + out.write(b"\r\n"); + } + for row in screen + .rows_formatted(0, 80) + .take(last_row) + .skip(last_row.saturating_sub(5)) + { + out.write(&row); + out.write(b"\r\n"); + } + out.reset_attributes(); + } + Ok(()) + } +} + +struct HistoryEntry { + cmd: String, + vt: vt100::Parser, + running: bool, // option end time + // start time +} + +impl HistoryEntry { + fn new(cmd: &str) -> Self { + Self { + cmd: cmd.into(), + vt: vt100::Parser::new(24, 80, 0), + running: true, + } + } +} + +fn parse_cmd(full_cmd: &str) -> (String, Vec) { + let mut parts = full_cmd.split(' '); + let cmd = parts.next().unwrap(); + ( + cmd.to_string(), + parts.map(std::string::ToString::to_string).collect(), + ) +} -- cgit v1.2.3-54-g00ecf