summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/history.rs51
-rw-r--r--src/nbsh.rs176
-rw-r--r--src/repl.rs58
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<async_std::sync::Arc<async_std::sync::Mutex<HistoryEntry>>>,
+ action: async_std::channel::Sender<crate::nbsh::Action>,
}
impl History {
- pub fn new() -> Self {
- Self::default()
+ pub fn new(
+ action: async_std::channel::Sender<crate::nbsh::Action>,
+ ) -> 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<usize> {
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<Action>,
}
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<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(
+ 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<crate::nbsh::Action>,
}
impl Repl {
- pub fn new() -> Self {
+ pub fn new(
+ action: async_std::channel::Sender<crate::nbsh::Action>,
+ ) -> 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();
+ }
}