summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-05 07:18:29 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-05 07:18:29 -0500
commita30174620d6b64f838989a634c265a353b2ab117 (patch)
tree023ff89d0a7b61550b17eb49702722c81c38499c
parent404ae6202e24c7bfc5625edb3ac064df4ecd105f (diff)
downloadnbsh-a30174620d6b64f838989a634c265a353b2ab117.tar.gz
nbsh-a30174620d6b64f838989a634c265a353b2ab117.zip
a bunch more reorganization
-rw-r--r--src/info.rs20
-rw-r--r--src/main.rs122
-rw-r--r--src/shell/event.rs (renamed from src/event.rs)0
-rw-r--r--src/shell/history/entry.rs (renamed from src/state/history/entry.rs)6
-rw-r--r--src/shell/history/mod.rs (renamed from src/state/history/mod.rs)17
-rw-r--r--src/shell/history/pty.rs (renamed from src/state/history/pty.rs)10
-rw-r--r--src/shell/mod.rs (renamed from src/state/mod.rs)128
-rw-r--r--src/shell/prelude.rs1
-rw-r--r--src/shell/readline.rs (renamed from src/state/readline.rs)0
9 files changed, 158 insertions, 146 deletions
diff --git a/src/info.rs b/src/info.rs
index 5653326..e441b16 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -28,3 +28,23 @@ pub fn time(offset: time::UtcOffset) -> anyhow::Result<String> {
time::OffsetDateTime::now_utc().to_offset(offset),
))
}
+
+// the time crate is currently unable to get the local offset on unix due to
+// soundness concerns, so we have to do it manually/:
+//
+// https://github.com/time-rs/time/issues/380
+pub fn get_offset() -> time::UtcOffset {
+ let offset_str =
+ std::process::Command::new("date").args(&["+%:z"]).output();
+ if let Ok(offset_str) = offset_str {
+ let offset_str = String::from_utf8(offset_str.stdout).unwrap();
+ time::UtcOffset::parse(
+ offset_str.trim(),
+ &time::format_description::parse("[offset_hour]:[offset_minute]")
+ .unwrap(),
+ )
+ .unwrap_or(time::UtcOffset::UTC)
+ } else {
+ time::UtcOffset::UTC
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index d858f58..d69ca20 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,136 +14,18 @@
mod env;
pub use env::Env;
-mod event;
-pub use event::Event;
mod format;
mod info;
mod parse;
mod pipeline;
-mod state;
-
-use async_std::stream::StreamExt as _;
-use textmode::Textmode as _;
-
-// the time crate is currently unable to get the local offset on unix due to
-// soundness concerns, so we have to do it manually/:
-//
-// https://github.com/time-rs/time/issues/380
-fn get_offset() -> time::UtcOffset {
- let offset_str =
- std::process::Command::new("date").args(&["+%:z"]).output();
- if let Ok(offset_str) = offset_str {
- let offset_str = String::from_utf8(offset_str.stdout).unwrap();
- time::UtcOffset::parse(
- offset_str.trim(),
- &time::format_description::parse("[offset_hour]:[offset_minute]")
- .unwrap(),
- )
- .unwrap_or(time::UtcOffset::UTC)
- } else {
- time::UtcOffset::UTC
- }
-}
+mod shell;
async fn async_main() -> anyhow::Result<i32> {
if std::env::args().nth(1).as_deref() == Some("--internal-cmd-runner") {
return pipeline::run().await;
}
- 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 (event_w, event_r) = async_std::channel::unbounded();
-
- {
- // nix::sys::signal::Signal is repr(i32)
- #[allow(clippy::as_conversions)]
- let signals = signal_hook_async_std::Signals::new(&[
- nix::sys::signal::Signal::SIGWINCH as i32,
- ])?;
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- // nix::sys::signal::Signal is repr(i32)
- #[allow(clippy::as_conversions)]
- let mut signals = async_std::stream::once(
- nix::sys::signal::Signal::SIGWINCH as i32,
- )
- .chain(signals);
- while signals.next().await.is_some() {
- event_w
- .send(Event::Resize(
- terminal_size::terminal_size().map_or(
- (24, 80),
- |(
- terminal_size::Width(w),
- terminal_size::Height(h),
- )| { (h, w) },
- ),
- ))
- .await
- .unwrap();
- }
- });
- }
-
- {
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- while let Some(key) = input.read_key().await.unwrap() {
- event_w.send(Event::Key(key)).await.unwrap();
- }
- });
- }
-
- // redraw the clock every second
- {
- let event_w = event_w.clone();
- async_std::task::spawn(async move {
- let first_sleep = 1_000_000_000_u64.saturating_sub(
- time::OffsetDateTime::now_utc().nanosecond().into(),
- );
- async_std::task::sleep(std::time::Duration::from_nanos(
- first_sleep,
- ))
- .await;
- let mut interval = async_std::stream::interval(
- std::time::Duration::from_secs(1),
- );
- event_w.send(Event::ClockTimer).await.unwrap();
- while interval.next().await.is_some() {
- event_w.send(Event::ClockTimer).await.unwrap();
- }
- });
- }
-
- let mut state = state::State::new(get_offset());
- let event_reader = event::Reader::new(event_r);
- while let Some(event) = event_reader.recv().await {
- match state.handle_event(event, &event_w).await {
- Some(state::Action::Refresh) => {
- state.render(&mut output).await?;
- output.refresh().await?;
- }
- Some(state::Action::HardRefresh) => {
- state.render(&mut output).await?;
- output.hard_refresh().await?;
- }
- Some(state::Action::Resize(rows, cols)) => {
- output.set_size(rows, cols);
- state.render(&mut output).await?;
- output.hard_refresh().await?;
- }
- Some(state::Action::Quit) => break,
- None => {}
- }
- }
-
- Ok(0)
+ shell::run().await
}
fn main() {
diff --git a/src/event.rs b/src/shell/event.rs
index 0343cdb..0343cdb 100644
--- a/src/event.rs
+++ b/src/shell/event.rs
diff --git a/src/state/history/entry.rs b/src/shell/history/entry.rs
index 31b4926..ab08a72 100644
--- a/src/state/history/entry.rs
+++ b/src/shell/history/entry.rs
@@ -1,3 +1,5 @@
+use crate::shell::prelude::*;
+
use std::os::unix::process::ExitStatusExt as _;
pub struct Entry {
@@ -272,11 +274,11 @@ impl Entry {
pub async fn finish(
&mut self,
env: crate::Env,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) {
self.exit_info = Some(ExitInfo::new(*env.latest_status()));
self.env = env;
- event_w.send(crate::Event::PtyClose).await.unwrap();
+ event_w.send(Event::PtyClose).await.unwrap();
}
}
diff --git a/src/state/history/mod.rs b/src/shell/history/mod.rs
index 0723f77..76e7d3b 100644
--- a/src/state/history/mod.rs
+++ b/src/shell/history/mod.rs
@@ -1,3 +1,5 @@
+use crate::shell::prelude::*;
+
use async_std::io::WriteExt as _;
use futures_lite::future::FutureExt as _;
use std::os::unix::io::{FromRawFd as _, IntoRawFd as _};
@@ -92,7 +94,7 @@ impl History {
&mut self,
ast: crate::parse::Commands,
env: &crate::Env,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> anyhow::Result<usize> {
let (input_w, input_r) = async_std::channel::unbounded();
let (resize_w, resize_r) = async_std::channel::unbounded();
@@ -123,7 +125,7 @@ impl History {
&mut self,
e: crate::parse::Error,
env: &crate::Env,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> anyhow::Result<usize> {
// XXX would be great to not have to do this
let (input_w, input_r) = async_std::channel::unbounded();
@@ -275,7 +277,7 @@ fn run_commands(
mut env: crate::Env,
input_r: async_std::channel::Receiver<Vec<u8>>,
resize_r: async_std::channel::Receiver<(u16, u16)>,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) {
async_std::task::spawn(async move {
let pty = match pty::Pty::new(
@@ -329,7 +331,7 @@ fn run_commands(
async fn run_pipeline(
pty: &pty::Pty,
env: &mut crate::Env,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> anyhow::Result<(async_std::process::ExitStatus, bool)> {
let mut cmd = pty_process::Command::new(std::env::current_exe().unwrap());
cmd.arg("--internal-cmd-runner");
@@ -379,10 +381,9 @@ async fn run_pipeline(
let exit = async { Res::Exit(child.status_no_drop().await) };
match read.or(exit).await {
Res::Read(Ok(event)) => match event {
- crate::pipeline::Event::Suspend(idx) => event_w
- .send(crate::Event::ChildSuspend(idx))
- .await
- .unwrap(),
+ crate::pipeline::Event::Suspend(idx) => {
+ event_w.send(Event::ChildSuspend(idx)).await.unwrap();
+ }
crate::pipeline::Event::Exit(new_env) => *env = new_env,
},
Res::Read(Err(e)) => {
diff --git a/src/state/history/pty.rs b/src/shell/history/pty.rs
index 146cf68..602a568 100644
--- a/src/state/history/pty.rs
+++ b/src/shell/history/pty.rs
@@ -1,3 +1,5 @@
+use crate::shell::prelude::*;
+
use async_std::io::{ReadExt as _, WriteExt as _};
use futures_lite::future::FutureExt as _;
@@ -12,7 +14,7 @@ impl Pty {
entry: &async_std::sync::Arc<async_std::sync::Mutex<super::Entry>>,
input_r: async_std::channel::Receiver<Vec<u8>>,
resize_r: async_std::channel::Receiver<(u16, u16)>,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> anyhow::Result<Self> {
let (close_w, close_r) = async_std::channel::unbounded();
@@ -50,7 +52,7 @@ async fn pty_task(
input_r: async_std::channel::Receiver<Vec<u8>>,
resize_r: async_std::channel::Receiver<(u16, u16)>,
close_r: async_std::channel::Receiver<()>,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) {
loop {
enum Res {
@@ -68,7 +70,7 @@ async fn pty_task(
Res::Read(res) => match res {
Ok(bytes) => {
entry.lock_arc().await.process(&buf[..bytes]);
- event_w.send(crate::Event::PtyOutput).await.unwrap();
+ event_w.send(Event::PtyOutput).await.unwrap();
}
Err(e) => {
if e.raw_os_error() != Some(libc::EIO) {
@@ -95,7 +97,7 @@ async fn pty_task(
},
Res::Close(res) => match res {
Ok(()) => {
- event_w.send(crate::Event::PtyClose).await.unwrap();
+ event_w.send(Event::PtyClose).await.unwrap();
return;
}
Err(e) => {
diff --git a/src/state/mod.rs b/src/shell/mod.rs
index 28ab705..a19ae09 100644
--- a/src/state/mod.rs
+++ b/src/shell/mod.rs
@@ -1,6 +1,110 @@
+use crate::shell::prelude::*;
+
+use async_std::stream::StreamExt as _;
+use textmode::Textmode as _;
+
+mod event;
mod history;
+mod prelude;
mod readline;
+pub async fn run() -> anyhow::Result<i32> {
+ 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 (event_w, event_r) = async_std::channel::unbounded();
+
+ {
+ // nix::sys::signal::Signal is repr(i32)
+ #[allow(clippy::as_conversions)]
+ let signals = signal_hook_async_std::Signals::new(&[
+ nix::sys::signal::Signal::SIGWINCH as i32,
+ ])?;
+ let event_w = event_w.clone();
+ async_std::task::spawn(async move {
+ // nix::sys::signal::Signal is repr(i32)
+ #[allow(clippy::as_conversions)]
+ let mut signals = async_std::stream::once(
+ nix::sys::signal::Signal::SIGWINCH as i32,
+ )
+ .chain(signals);
+ while signals.next().await.is_some() {
+ event_w
+ .send(Event::Resize(
+ terminal_size::terminal_size().map_or(
+ (24, 80),
+ |(
+ terminal_size::Width(w),
+ terminal_size::Height(h),
+ )| { (h, w) },
+ ),
+ ))
+ .await
+ .unwrap();
+ }
+ });
+ }
+
+ {
+ let event_w = event_w.clone();
+ async_std::task::spawn(async move {
+ while let Some(key) = input.read_key().await.unwrap() {
+ event_w.send(Event::Key(key)).await.unwrap();
+ }
+ });
+ }
+
+ // redraw the clock every second
+ {
+ let event_w = event_w.clone();
+ async_std::task::spawn(async move {
+ let first_sleep = 1_000_000_000_u64.saturating_sub(
+ time::OffsetDateTime::now_utc().nanosecond().into(),
+ );
+ async_std::task::sleep(std::time::Duration::from_nanos(
+ first_sleep,
+ ))
+ .await;
+ let mut interval = async_std::stream::interval(
+ std::time::Duration::from_secs(1),
+ );
+ event_w.send(Event::ClockTimer).await.unwrap();
+ while interval.next().await.is_some() {
+ event_w.send(Event::ClockTimer).await.unwrap();
+ }
+ });
+ }
+
+ let mut shell = Shell::new(crate::info::get_offset());
+ let event_reader = event::Reader::new(event_r);
+ while let Some(event) = event_reader.recv().await {
+ match shell.handle_event(event, &event_w).await {
+ Some(Action::Refresh) => {
+ shell.render(&mut output).await?;
+ output.refresh().await?;
+ }
+ Some(Action::HardRefresh) => {
+ shell.render(&mut output).await?;
+ output.hard_refresh().await?;
+ }
+ Some(Action::Resize(rows, cols)) => {
+ output.set_size(rows, cols);
+ shell.render(&mut output).await?;
+ output.hard_refresh().await?;
+ }
+ Some(Action::Quit) => break,
+ None => {}
+ }
+ }
+
+ Ok(0)
+}
+
#[derive(Copy, Clone, Debug)]
enum Focus {
Readline,
@@ -21,7 +125,7 @@ pub enum Action {
Quit,
}
-pub struct State {
+pub struct Shell {
readline: readline::Readline,
history: history::History,
env: crate::Env,
@@ -32,7 +136,7 @@ pub struct State {
offset: time::UtcOffset,
}
-impl State {
+impl Shell {
pub fn new(offset: time::UtcOffset) -> Self {
Self {
readline: readline::Readline::new(),
@@ -121,11 +225,11 @@ impl State {
pub async fn handle_event(
&mut self,
- event: crate::Event,
- event_w: &async_std::channel::Sender<crate::Event>,
+ event: Event,
+ event_w: &async_std::channel::Sender<Event>,
) -> Option<Action> {
match event {
- crate::Event::Key(key) => {
+ Event::Key(key) => {
return if self.escape {
self.escape = false;
self.handle_key_escape(key, event_w.clone()).await
@@ -148,12 +252,12 @@ impl State {
}
};
}
- crate::Event::Resize(new_size) => {
+ Event::Resize(new_size) => {
self.readline.resize(new_size).await;
self.history.resize(new_size).await;
return Some(Action::Resize(new_size.0, new_size.1));
}
- crate::Event::PtyOutput => {
+ Event::PtyOutput => {
// the number of visible lines may have changed, so make sure
// the focus is still visible
self.history
@@ -165,7 +269,7 @@ impl State {
.await;
self.scene = self.default_scene(self.focus, None).await;
}
- crate::Event::PtyClose => {
+ Event::PtyClose => {
if let Some(idx) = self.focus_idx() {
let entry = self.history.entry(idx).await;
if !entry.running() {
@@ -186,12 +290,12 @@ impl State {
}
}
}
- crate::Event::ChildSuspend(idx) => {
+ Event::ChildSuspend(idx) => {
if self.focus_idx() == Some(idx) {
self.set_focus(Focus::Readline, None).await;
}
}
- crate::Event::ClockTimer => {}
+ Event::ClockTimer => {}
};
Some(Action::Refresh)
}
@@ -199,7 +303,7 @@ impl State {
async fn handle_key_escape(
&mut self,
key: textmode::Key,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> Option<Action> {
match key {
textmode::Key::Ctrl(b'd') => {
@@ -319,7 +423,7 @@ impl State {
async fn handle_key_readline(
&mut self,
key: textmode::Key,
- event_w: async_std::channel::Sender<crate::Event>,
+ event_w: async_std::channel::Sender<Event>,
) -> Option<Action> {
match key {
textmode::Key::Char(c) => {
diff --git a/src/shell/prelude.rs b/src/shell/prelude.rs
new file mode 100644
index 0000000..19f58f2
--- /dev/null
+++ b/src/shell/prelude.rs
@@ -0,0 +1 @@
+pub use super::event::Event;
diff --git a/src/state/readline.rs b/src/shell/readline.rs
index e423e62..e423e62 100644
--- a/src/state/readline.rs
+++ b/src/shell/readline.rs