From d0c26780d93a14be4d70bf2c784b79b3b0f06186 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 10 Nov 2021 01:03:11 -0500 Subject: refactor --- src/history.rs | 51 ++++++++++++----- src/nbsh.rs | 176 +++++++++++++++++++++++++++++++++++---------------------- src/repl.rs | 58 ++++++++++++++----- 3 files changed, 188 insertions(+), 97 deletions(-) diff --git a/src/history.rs b/src/history.rs index 339e53d..bf89ec1 100644 --- a/src/history.rs +++ b/src/history.rs @@ -2,21 +2,22 @@ use async_std::io::ReadExt as _; use pty_process::Command as _; use textmode::Textmode as _; -#[derive(Default)] pub struct History { entries: Vec>>, + action: async_std::channel::Sender, } impl History { - pub fn new() -> Self { - Self::default() + pub fn new( + action: async_std::channel::Sender, + ) -> Self { + Self { + entries: vec![], + action, + } } - pub async fn run( - &mut self, - cmd: &str, - render: async_std::channel::Sender<()>, - ) -> anyhow::Result<()> { + pub async fn run(&mut self, cmd: &str) -> anyhow::Result { let (exe, args) = parse_cmd(cmd); let mut process = async_process::Command::new(&exe); process.args(&args); @@ -27,7 +28,7 @@ impl History { HistoryEntry::new(cmd), )); let task_entry = async_std::sync::Arc::clone(&entry); - let task_render = render.clone(); + let task_action = self.action.clone(); async_std::task::spawn(async move { loop { let mut buf = [0_u8; 4096]; @@ -40,16 +41,32 @@ impl History { eprintln!("pty read failed: {:?}", e); } task_entry.lock_arc().await.running = false; - task_render.send(()).await.unwrap(); + task_action + .send(crate::nbsh::Action::Render) + .await + .unwrap(); break; } } - task_render.send(()).await.unwrap(); + task_action.send(crate::nbsh::Action::Render).await.unwrap(); } }); self.entries.push(entry); - render.send(()).await.unwrap(); - Ok(()) + self.action.send(crate::nbsh::Action::Render).await.unwrap(); + Ok(self.entries.len() - 1) + } + + pub async fn handle_key( + &mut self, + key: textmode::Key, + idx: usize, + ) -> bool { + if let textmode::Key::Bytes(b) = key { + self.send_process_input(idx, &b).await.unwrap(); + } else { + unreachable!(); + } + false } pub async fn render( @@ -97,6 +114,14 @@ impl History { } Ok(()) } + + async fn send_process_input( + &self, + idx: usize, + input: &[u8], + ) -> anyhow::Result<()> { + todo!() + } } struct HistoryEntry { diff --git a/src/nbsh.rs b/src/nbsh.rs index 772ff5c..8ba22b2 100644 --- a/src/nbsh.rs +++ b/src/nbsh.rs @@ -3,13 +3,17 @@ use textmode::Textmode as _; pub struct Nbsh { repl: crate::repl::Repl, history: crate::history::History, + + action: async_std::channel::Receiver, } impl Nbsh { pub fn new() -> Self { + let (action_w, action_r) = async_std::channel::unbounded(); Self { - repl: crate::repl::Repl::new(), - history: crate::history::History::new(), + repl: crate::repl::Repl::new(action_w.clone()), + history: crate::history::History::new(action_w), + action: action_r, } } @@ -22,85 +26,119 @@ impl Nbsh { let _input_guard = input.take_raw_guard(); let _output_guard = output.take_screen_guard(); - let (run_w, run_r) = async_std::channel::unbounded(); - let (render_w, render_r) = async_std::channel::unbounded(); + let Self { + repl, + history, + action, + } = self; - self.render(&mut output).await.unwrap(); + let repl = + async_std::sync::Arc::new(async_std::sync::Mutex::new(repl)); + let history = + async_std::sync::Arc::new(async_std::sync::Mutex::new(history)); + let input_source = async_std::sync::Arc::new( + async_std::sync::Mutex::new(InputSource::Repl), + ); - let locked_self = - async_std::sync::Arc::new(async_std::sync::Mutex::new(self)); + render( + &mut output, + &*repl.lock_arc().await, + &*history.lock_arc().await, + ) + .await + .unwrap(); - let readline_self = std::sync::Arc::clone(&locked_self); - let readline_render = render_w.clone(); - let readline_task = async_std::task::spawn(async move { - loop { - let key = input.read_key().await.unwrap(); - let mut self_ = readline_self.lock_arc().await; - let (last, cmd) = self_.handle_key(key); - if last { - break; - } - if let Some(cmd) = cmd { - run_w.send(cmd).await.unwrap(); + let action_history = async_std::sync::Arc::clone(&history); + let action_repl = async_std::sync::Arc::clone(&repl); + let action_input_source = async_std::sync::Arc::clone(&input_source); + async_std::task::spawn(async move { + while let Ok(action) = action.recv().await { + match action { + Action::Render => { + render( + &mut output, + &*action_repl.lock_arc().await, + &*action_history.lock_arc().await, + ) + .await + .unwrap(); + } + Action::Run(cmd) => { + action_history + .lock_arc() + .await + .run(&cmd) + .await + .unwrap(); + } + Action::UpdateFocus(new_input_source) => { + *action_input_source.lock_arc().await = + new_input_source; + } } - readline_render.send(()).await.unwrap(); } }); - let history_self = std::sync::Arc::clone(&locked_self); - let history_render = render_w.clone(); - async_std::task::spawn(async move { - while let Ok(cmd) = run_r.recv().await { - let mut self_ = history_self.lock_arc().await; - self_ - .history - .run(&cmd, history_render.clone()) - .await - .unwrap(); + loop { + let input_source = *input_source.lock_arc().await; + match input_source { + InputSource::Repl => { + input.parse_utf8(true); + input.parse_ctrl(true); + input.parse_meta(true); + input.parse_special_keys(true); + input.parse_single(false); + } + InputSource::History(_) => { + input.parse_utf8(false); + input.parse_ctrl(false); + input.parse_meta(false); + input.parse_special_keys(false); + input.parse_single(false); + } } - }); - - let render_self = std::sync::Arc::clone(&locked_self); - async_std::task::spawn(async move { - while let Ok(()) = render_r.recv().await { - while let Ok(()) = render_r.try_recv() {} - let self_ = render_self.lock_arc().await; - self_.render(&mut output).await.unwrap(); + let key = input.read_key().await.unwrap(); + if let Some(key) = key { + let quit = match input_source { + InputSource::Repl => { + repl.lock_arc().await.handle_key(key).await + } + InputSource::History(idx) => { + history.lock_arc().await.handle_key(key, idx).await + } + }; + if quit { + break; + } + } else { + break; } - }); - - readline_task.await; + } Ok(()) } +} - fn handle_key( - &mut self, - key: Option, - ) -> (bool, Option) { - let mut cmd = None; - match key { - Some(textmode::Key::String(s)) => self.repl.add_input(&s), - Some(textmode::Key::Char(c)) => { - self.repl.add_input(&c.to_string()); - } - Some(textmode::Key::Ctrl(b'c')) => self.repl.clear_input(), - Some(textmode::Key::Ctrl(b'd')) | None => return (true, None), - Some(textmode::Key::Ctrl(b'm')) => { - cmd = Some(self.repl.input()); - self.repl.clear_input(); - } - Some(textmode::Key::Backspace) => self.repl.backspace(), - _ => {} - } - (false, cmd) - } +async fn render( + out: &mut textmode::Output, + repl: &crate::repl::Repl, + history: &crate::history::History, +) -> anyhow::Result<()> { + out.clear(); + history.render(out, repl.lines()).await?; + repl.render(out).await?; + out.refresh().await?; + Ok(()) +} - async fn render(&self, out: &mut textmode::Output) -> anyhow::Result<()> { - out.clear(); - self.history.render(out, self.repl.lines()).await?; - self.repl.render(out).await?; - out.refresh().await?; - Ok(()) - } +#[derive(Copy, Clone, Debug)] +pub enum InputSource { + Repl, + History(usize), +} + +pub enum Action { + Render, + Run(String), + UpdateFocus(InputSource), } diff --git a/src/repl.rs b/src/repl.rs index 258718e..223aa80 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -3,30 +3,42 @@ use textmode::Textmode as _; pub struct Repl { prompt: String, input_line: String, + action: async_std::channel::Sender, } impl Repl { - pub fn new() -> Self { + pub fn new( + action: async_std::channel::Sender, + ) -> Self { Self { prompt: "$ ".into(), input_line: "".into(), + action, } } - pub fn input(&self) -> String { - self.input_line.clone() - } - - pub fn add_input(&mut self, s: &str) { - self.input_line.push_str(s); - } - - pub fn backspace(&mut self) { - self.input_line.pop(); - } - - pub fn clear_input(&mut self) { - self.input_line.clear(); + pub async fn handle_key(&mut self, key: textmode::Key) -> bool { + match key { + textmode::Key::String(s) => self.add_input(&s), + textmode::Key::Char(c) => { + self.add_input(&c.to_string()); + } + textmode::Key::Ctrl(b'c') => self.clear_input(), + textmode::Key::Ctrl(b'd') => { + return true; + } + textmode::Key::Ctrl(b'm') => { + self.action + .send(crate::nbsh::Action::Run(self.input())) + .await + .unwrap(); + self.clear_input(); + } + textmode::Key::Backspace => self.backspace(), + _ => {} + } + self.action.send(crate::nbsh::Action::Render).await.unwrap(); + false } pub fn lines(&self) -> usize { @@ -42,4 +54,20 @@ impl Repl { out.write_str(&self.input_line); Ok(()) } + + fn input(&self) -> String { + self.input_line.clone() + } + + fn add_input(&mut self, s: &str) { + self.input_line.push_str(s); + } + + fn backspace(&mut self) { + self.input_line.pop(); + } + + fn clear_input(&mut self) { + self.input_line.clear(); + } } -- cgit v1.2.3-54-g00ecf