summaryrefslogtreecommitdiffstats
path: root/src/nbsh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/nbsh.rs')
-rw-r--r--src/nbsh.rs106
1 files changed, 106 insertions, 0 deletions
diff --git a/src/nbsh.rs b/src/nbsh.rs
new file mode 100644
index 0000000..772ff5c
--- /dev/null
+++ b/src/nbsh.rs
@@ -0,0 +1,106 @@
+use textmode::Textmode as _;
+
+pub struct Nbsh {
+ repl: crate::repl::Repl,
+ history: crate::history::History,
+}
+
+impl Nbsh {
+ pub fn new() -> Self {
+ Self {
+ repl: crate::repl::Repl::new(),
+ history: crate::history::History::new(),
+ }
+ }
+
+ pub async fn run(self) -> anyhow::Result<()> {
+ let mut input = textmode::Input::new().await?;
+ let mut output = textmode::Output::new().await?;
+
+ // avoid the guards getting stuck in a task that doesn't run to
+ // completion
+ 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();
+
+ self.render(&mut output).await.unwrap();
+
+ let locked_self =
+ async_std::sync::Arc::new(async_std::sync::Mutex::new(self));
+
+ 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();
+ }
+ 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();
+ }
+ });
+
+ 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();
+ }
+ });
+
+ readline_task.await;
+
+ Ok(())
+ }
+
+ fn handle_key(
+ &mut self,
+ key: Option<textmode::Key>,
+ ) -> (bool, Option<String>) {
+ 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(&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(())
+ }
+}