summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock25
-rw-r--r--Cargo.toml1
-rw-r--r--src/history.rs56
-rw-r--r--src/main.rs3
-rw-r--r--src/nbsh.rs219
5 files changed, 180 insertions, 124 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1797f47..7518950 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -156,9 +156,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blocking"
@@ -452,6 +452,7 @@ dependencies = [
"async-std",
"futures",
"libc",
+ "nix 0.23.0",
"pty-process",
"textmode",
"vt100",
@@ -459,9 +460,21 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.20.2"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945"
+checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
dependencies = [
"bitflags",
"cc",
@@ -547,7 +560,7 @@ dependencies = [
"async-io",
"async-process",
"libc",
- "nix",
+ "nix 0.20.0",
"thiserror",
]
@@ -625,7 +638,7 @@ dependencies = [
"blocking",
"futures-lite",
"itoa",
- "nix",
+ "nix 0.20.0",
"terminal_size",
"thiserror",
"vt100",
diff --git a/Cargo.toml b/Cargo.toml
index adb439d..9bed95f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,7 @@ async-process = "1.2.0"
async-std = "1.10.0"
futures = "0.3.17"
libc = "0.2.107"
+nix = "0.23.0"
pty-process = { version = "0.1.0", features = ["backend-async-std"] }
textmode = { version = "0.1.0", features = ["async"] }
vt100 = "0.12.0"
diff --git a/src/history.rs b/src/history.rs
index bf89ec1..00ad39a 100644
--- a/src/history.rs
+++ b/src/history.rs
@@ -25,7 +25,7 @@ impl History {
.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),
+ HistoryEntry::new(cmd, child.id().try_into().unwrap()),
));
let task_entry = async_std::sync::Arc::clone(&entry);
let task_action = self.action.clone();
@@ -42,6 +42,12 @@ impl History {
}
task_entry.lock_arc().await.running = false;
task_action
+ .send(crate::nbsh::Action::UpdateFocus(
+ crate::nbsh::InputSource::Repl,
+ ))
+ .await
+ .unwrap();
+ task_action
.send(crate::nbsh::Action::Render)
.await
.unwrap();
@@ -52,6 +58,12 @@ impl History {
}
});
self.entries.push(entry);
+ self.action
+ .send(crate::nbsh::Action::UpdateFocus(
+ crate::nbsh::InputSource::History(self.entries.len() - 1),
+ ))
+ .await
+ .unwrap();
self.action.send(crate::nbsh::Action::Render).await.unwrap();
Ok(self.entries.len() - 1)
}
@@ -61,10 +73,26 @@ impl History {
key: textmode::Key,
idx: usize,
) -> bool {
- if let textmode::Key::Bytes(b) = key {
- self.send_process_input(idx, &b).await.unwrap();
- } else {
- unreachable!();
+ match key {
+ textmode::Key::Ctrl(b'c') => {
+ let pid = self.entries[idx].lock_arc().await.pid;
+ nix::sys::signal::kill(pid, nix::sys::signal::Signal::SIGINT)
+ .unwrap();
+ }
+ textmode::Key::Ctrl(b'z') => {
+ self.action
+ .send(crate::nbsh::Action::UpdateFocus(
+ crate::nbsh::InputSource::Repl,
+ ))
+ .await
+ .unwrap();
+ }
+ textmode::Key::Ctrl(_) => {}
+ key => {
+ self.send_process_input(idx, &key.into_bytes())
+ .await
+ .unwrap();
+ }
}
false
}
@@ -75,6 +103,7 @@ impl History {
repl_lines: usize,
) -> anyhow::Result<()> {
let mut used_lines = repl_lines;
+ let mut pos = None;
for entry in self.entries.iter().rev() {
let entry = entry.lock_arc().await;
let screen = entry.vt.screen();
@@ -95,23 +124,30 @@ impl History {
}
out.write_str(&entry.cmd);
out.reset_attributes();
- out.write(b"\r\n");
if last_row > 5 {
+ out.write(b"\r\n");
out.set_bgcolor(textmode::color::RED);
out.write(b"...");
out.reset_attributes();
- out.write(b"\r\n");
}
+ let mut end_pos = (0, 0);
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.write(&row);
+ end_pos = out.screen().cursor_position();
+ }
+ if pos.is_none() {
+ pos = Some(end_pos);
}
out.reset_attributes();
}
+ if let Some(pos) = pos {
+ out.move_to(pos.0, pos.1);
+ }
Ok(())
}
@@ -126,15 +162,17 @@ impl History {
struct HistoryEntry {
cmd: String,
+ pid: nix::unistd::Pid,
vt: vt100::Parser,
running: bool, // option end time
// start time
}
impl HistoryEntry {
- fn new(cmd: &str) -> Self {
+ fn new(cmd: &str, pid: i32) -> Self {
Self {
cmd: cmd.into(),
+ pid: nix::unistd::Pid::from_raw(pid),
vt: vt100::Parser::new(24, 80, 0),
running: true,
}
diff --git a/src/main.rs b/src/main.rs
index 0d1bb69..97624df 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,8 +8,7 @@ mod nbsh;
mod repl;
async fn async_main() -> anyhow::Result<()> {
- let nbsh = nbsh::Nbsh::new();
- nbsh.run().await
+ nbsh::run().await
}
fn main() {
diff --git a/src/nbsh.rs b/src/nbsh.rs
index 8ba22b2..84d35e3 100644
--- a/src/nbsh.rs
+++ b/src/nbsh.rs
@@ -1,132 +1,137 @@
use textmode::Textmode as _;
-pub struct Nbsh {
- repl: crate::repl::Repl,
- history: crate::history::History,
+pub async fn run() -> anyhow::Result<()> {
+ let mut input = textmode::Input::new().await?;
+ let mut output = textmode::Output::new().await?;
- 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(action_w.clone()),
- history: crate::history::History::new(action_w),
- action: action_r,
- }
- }
-
- 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();
+ // 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 Self {
- repl,
- history,
- action,
- } = self;
+ let (action_w, action_r) = async_std::channel::unbounded();
- 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 repl = async_std::sync::Arc::new(async_std::sync::Mutex::new(
+ crate::repl::Repl::new(action_w.clone()),
+ ));
+ let history = async_std::sync::Arc::new(async_std::sync::Mutex::new(
+ crate::history::History::new(action_w),
+ ));
+ let input_source = async_std::sync::Arc::new(
+ async_std::sync::Mutex::new(InputSource::Repl),
+ );
- render(
- &mut output,
- &*repl.lock_arc().await,
- &*history.lock_arc().await,
- )
- .await
- .unwrap();
+ render(
+ &mut output,
+ &*repl.lock_arc().await,
+ &*history.lock_arc().await,
+ &*input_source.lock_arc().await,
+ )
+ .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);
+ {
+ let repl = async_std::sync::Arc::clone(&repl);
+ let history = async_std::sync::Arc::clone(&history);
+ let 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;
- }
- }
+ while let Ok(action) = action_r.recv().await {
+ handle_action(
+ action,
+ &mut output,
+ async_std::sync::Arc::clone(&repl),
+ async_std::sync::Arc::clone(&history),
+ async_std::sync::Arc::clone(&input_source),
+ )
+ .await;
}
});
+ }
- 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 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;
- }
+ loop {
+ let quit = handle_input(
+ &mut input,
+ async_std::sync::Arc::clone(&repl),
+ async_std::sync::Arc::clone(&history),
+ async_std::sync::Arc::clone(&input_source),
+ )
+ .await;
+ if quit {
+ break;
+ }
+ }
+
+ Ok(())
+}
+
+async fn handle_action(
+ action: Action,
+ output: &mut textmode::Output,
+ repl: async_std::sync::Arc<async_std::sync::Mutex<crate::repl::Repl>>,
+ history: async_std::sync::Arc<
+ async_std::sync::Mutex<crate::history::History>,
+ >,
+ input_source: async_std::sync::Arc<async_std::sync::Mutex<InputSource>>,
+) {
+ match action {
+ Action::Render => {
+ render(
+ output,
+ &*repl.lock_arc().await,
+ &*history.lock_arc().await,
+ &*input_source.lock_arc().await,
+ )
+ .await
+ .unwrap();
+ }
+ Action::Run(ref cmd) => {
+ history.lock_arc().await.run(cmd).await.unwrap();
}
+ Action::UpdateFocus(new_input_source) => {
+ *input_source.lock_arc().await = new_input_source;
+ }
+ }
+}
- Ok(())
+async fn handle_input(
+ input: &mut textmode::Input,
+ repl: async_std::sync::Arc<async_std::sync::Mutex<crate::repl::Repl>>,
+ history: async_std::sync::Arc<
+ async_std::sync::Mutex<crate::history::History>,
+ >,
+ input_source: async_std::sync::Arc<async_std::sync::Mutex<InputSource>>,
+) -> bool {
+ let key = input.read_key().await.unwrap();
+ if let Some(key) = key {
+ let input_source = *input_source.lock_arc().await;
+ 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 {
+ return true;
+ }
+ } else {
+ return true;
}
+ false
}
async fn render(
out: &mut textmode::Output,
repl: &crate::repl::Repl,
history: &crate::history::History,
+ input_source: &InputSource,
) -> anyhow::Result<()> {
out.clear();
- history.render(out, repl.lines()).await?;
- repl.render(out).await?;
+ if let InputSource::Repl = input_source {
+ history.render(out, repl.lines()).await?;
+ repl.render(out).await?;
+ } else {
+ history.render(out, 0).await?;
+ }
out.refresh().await?;
Ok(())
}