diff options
author | Jesse Luehrs <doy@tozt.net> | 2022-01-05 07:18:29 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2022-01-05 07:18:29 -0500 |
commit | a30174620d6b64f838989a634c265a353b2ab117 (patch) | |
tree | 023ff89d0a7b61550b17eb49702722c81c38499c | |
parent | 404ae6202e24c7bfc5625edb3ac064df4ecd105f (diff) | |
download | nbsh-a30174620d6b64f838989a634c265a353b2ab117.tar.gz nbsh-a30174620d6b64f838989a634c265a353b2ab117.zip |
a bunch more reorganization
-rw-r--r-- | src/info.rs | 20 | ||||
-rw-r--r-- | src/main.rs | 122 | ||||
-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.rs | 1 | ||||
-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 |