summaryrefslogtreecommitdiffstats
path: root/src/history.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-11-09 21:23:19 -0500
committerJesse Luehrs <doy@tozt.net>2021-11-09 21:23:19 -0500
commitfbc4db18ecea9755199b945368c02f32c0481c95 (patch)
treedfe91e5f538aeb6642e74af09323c288933cb00b /src/history.rs
parent66cdb4e85efaa2e9abf270fc875239b6790c4a40 (diff)
downloadnbsh-fbc4db18ecea9755199b945368c02f32c0481c95.tar.gz
nbsh-fbc4db18ecea9755199b945368c02f32c0481c95.zip
basic command running and display
Diffstat (limited to 'src/history.rs')
-rw-r--r--src/history.rs126
1 files changed, 126 insertions, 0 deletions
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<async_std::sync::Arc<async_std::sync::Mutex<HistoryEntry>>>,
+}
+
+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<String>) {
+ let mut parts = full_cmd.split(' ');
+ let cmd = parts.next().unwrap();
+ (
+ cmd.to_string(),
+ parts.map(std::string::ToString::to_string).collect(),
+ )
+}