summaryrefslogtreecommitdiffstats
path: root/src/state.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-12-11 21:00:49 -0500
committerJesse Luehrs <doy@tozt.net>2021-12-11 21:00:49 -0500
commit165faf398ab2e00ebf18ad730f18b9277f612213 (patch)
treef6b0d0b7d11ba11574d27a7380f2e75319c5ac5a /src/state.rs
parent62fe2477fb8bbbfbca34779b992979f9c1a8ba53 (diff)
downloadnbsh-165faf398ab2e00ebf18ad730f18b9277f612213.tar.gz
nbsh-165faf398ab2e00ebf18ad730f18b9277f612213.zip
significantly refactor the event loop
Diffstat (limited to 'src/state.rs')
-rw-r--r--src/state.rs269
1 files changed, 148 insertions, 121 deletions
diff --git a/src/state.rs b/src/state.rs
index f086334..dd32c5c 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,10 +1,23 @@
use textmode::Textmode as _;
+#[derive(Copy, Clone, Debug)]
+enum Focus {
+ Readline,
+ History(usize),
+ Scrolling(Option<usize>),
+}
+
+#[derive(Copy, Clone, Debug)]
+enum Scene {
+ Readline,
+ Fullscreen,
+}
+
pub struct State {
readline: crate::readline::Readline,
history: crate::history::History,
- focus: crate::action::Focus,
- scene: crate::action::Scene,
+ focus: Focus,
+ scene: Scene,
escape: bool,
hide_readline: bool,
offset: time::UtcOffset,
@@ -15,8 +28,8 @@ impl State {
Self {
readline: crate::readline::Readline::new(),
history: crate::history::History::new(),
- focus: crate::action::Focus::Readline,
- scene: crate::action::Scene::Readline,
+ focus: Focus::Readline,
+ scene: Scene::Readline,
escape: false,
hide_readline: false,
offset,
@@ -30,8 +43,8 @@ impl State {
) -> anyhow::Result<()> {
out.clear();
match self.scene {
- crate::action::Scene::Readline => match self.focus {
- crate::action::Focus::Readline => {
+ Scene::Readline => match self.focus {
+ Focus::Readline => {
self.history
.render(
out,
@@ -50,7 +63,7 @@ impl State {
)
.await?;
}
- crate::action::Focus::History(idx) => {
+ Focus::History(idx) => {
if self.hide_readline {
self.history
.render(out, 0, Some(idx), false, self.offset)
@@ -77,7 +90,7 @@ impl State {
out.move_to(pos.0, pos.1);
}
}
- crate::action::Focus::Scrolling(idx) => {
+ Focus::Scrolling(idx) => {
self.history
.render(
out,
@@ -98,8 +111,8 @@ impl State {
out.hide_cursor(true);
}
},
- crate::action::Scene::Fullscreen => {
- if let crate::action::Focus::History(idx) = self.focus {
+ Scene::Fullscreen => {
+ if let Focus::History(idx) = self.focus {
self.history.render_fullscreen(out, idx).await;
} else {
unreachable!();
@@ -114,61 +127,68 @@ impl State {
Ok(())
}
- pub async fn handle_action(
+ pub async fn handle_event(
&mut self,
- action: crate::action::Action,
+ event: crate::event::Event,
out: &mut textmode::Output,
- action_w: &async_std::channel::Sender<crate::action::Action>,
+ event_w: &async_std::channel::Sender<crate::event::Event>,
) {
let mut hard_refresh = false;
- match action {
- crate::action::Action::Key(key) => {
- if let Some(action) = self.handle_key(key).await {
- action_w.send(action).await.unwrap();
+ match event {
+ crate::event::Event::Key(key) => {
+ let (quit, hard) = if self.escape {
+ self.escape = false;
+ self.handle_key_escape(key).await
+ } else if key == textmode::Key::Ctrl(b'e') {
+ self.escape = true;
+ (false, false)
+ } else {
+ match self.focus {
+ Focus::Readline => {
+ self.handle_key_readline(key, event_w.clone())
+ .await
+ }
+ Focus::History(idx) => {
+ self.handle_key_history(key, idx).await;
+ (false, false)
+ }
+ Focus::Scrolling(_) => {
+ self.handle_key_escape(key).await
+ }
+ }
+ };
+ if quit {
+ event_w.send(crate::event::Event::Quit).await.unwrap();
+ }
+ if hard {
+ hard_refresh = true;
}
}
- crate::action::Action::Render => {
- // for instance, if we are rerendering because of command
- // output, that output could increase the number of lines of
- // output of a command, pushing the currently focused entry
- // off the top of the screen
+ crate::event::Event::Resize(new_size) => {
+ self.readline.resize(new_size).await;
+ self.history.resize(new_size).await;
+ out.set_size(new_size.0, new_size.1);
+ hard_refresh = true;
+ }
+ crate::event::Event::ProcessOutput => {
+ // the number of visible lines may have changed, so make sure
+ // the focus is still visible
self.history
.make_focus_visible(
self.readline.lines(),
self.focus_idx(),
- matches!(
- self.focus,
- crate::action::Focus::Scrolling(_)
- ),
+ matches!(self.focus, Focus::Scrolling(_)),
)
.await;
}
- crate::action::Action::ForceRedraw => {
- hard_refresh = true;
- }
- crate::action::Action::Run(ref cmd) => {
- let idx =
- self.history.run(cmd, action_w.clone()).await.unwrap();
- self.set_focus(crate::action::Focus::History(idx), None)
- .await;
- self.hide_readline = true;
- }
- crate::action::Action::UpdateFocus(new_focus) => {
- self.set_focus(new_focus, None).await;
- }
- crate::action::Action::UpdateScene(new_scene) => {
- self.scene = new_scene;
- }
- crate::action::Action::CheckUpdateScene => {
+ crate::event::Event::ProcessAlternateScreen => {
self.scene = self.default_scene(self.focus, None).await;
}
- crate::action::Action::Resize(new_size) => {
- self.readline.resize(new_size).await;
- self.history.resize(new_size).await;
- out.set_size(new_size.0, new_size.1);
- out.hard_refresh().await.unwrap();
+ crate::event::Event::ProcessExit => {
+ self.set_focus(Focus::Readline, None).await;
}
- crate::action::Action::Quit => {
+ crate::event::Event::ClockTimer => {}
+ crate::event::Event::Quit => {
// the debouncer should return None in this case
unreachable!();
}
@@ -176,51 +196,20 @@ impl State {
self.render(out, hard_refresh).await.unwrap();
}
- async fn handle_key(
- &mut self,
- key: textmode::Key,
- ) -> Option<crate::action::Action> {
- if self.escape {
- self.escape = false;
- self.handle_key_escape(key).await
- } else if key == textmode::Key::Ctrl(b'e') {
- self.escape = true;
- None
- } else {
- match self.focus {
- crate::action::Focus::Readline => {
- self.readline
- .handle_key(key, self.history.entry_count())
- .await
- }
- crate::action::Focus::History(idx) => {
- self.history.handle_key(key, idx).await;
- None
- }
- crate::action::Focus::Scrolling(_) => {
- self.handle_key_escape(key).await
- }
- }
- }
- }
-
async fn handle_key_escape(
&mut self,
key: textmode::Key,
- ) -> Option<crate::action::Action> {
+ ) -> (bool, bool) {
match key {
textmode::Key::Ctrl(b'd') => {
- return Some(crate::action::Action::Quit);
+ return (true, false);
}
textmode::Key::Ctrl(b'e') => {
- self.set_focus(
- crate::action::Focus::Scrolling(self.focus_idx()),
- None,
- )
- .await;
+ self.set_focus(Focus::Scrolling(self.focus_idx()), None)
+ .await;
}
textmode::Key::Ctrl(b'l') => {
- return Some(crate::action::Action::ForceRedraw);
+ return (false, true);
}
textmode::Key::Ctrl(b'm') => {
let idx = self.focus_idx();
@@ -232,8 +221,8 @@ impl State {
};
if focus {
self.set_focus(
- idx.map_or(crate::action::Focus::Readline, |idx| {
- crate::action::Focus::History(idx)
+ idx.map_or(Focus::Readline, |idx| {
+ Focus::History(idx)
}),
entry,
)
@@ -244,31 +233,25 @@ impl State {
if let Some(idx) = self.focus_idx() {
let entry = self.history.entry(idx).await;
self.readline.set_input(&entry.cmd());
- self.set_focus(
- crate::action::Focus::Readline,
- Some(entry),
- )
- .await;
+ self.set_focus(Focus::Readline, Some(entry)).await;
}
}
textmode::Key::Char('e') => {
- if let crate::action::Focus::History(idx) = self.focus {
- self.history
- .handle_key(textmode::Key::Ctrl(b'e'), idx)
+ if let Focus::History(idx) = self.focus {
+ self.handle_key_history(textmode::Key::Ctrl(b'e'), idx)
.await;
}
}
textmode::Key::Char('f') => {
if let Some(idx) = self.focus_idx() {
let mut entry = self.history.entry(idx).await;
- let mut focus = crate::action::Focus::History(idx);
- if let crate::action::Focus::Scrolling(_) = self.focus {
+ let mut focus = Focus::History(idx);
+ if let Focus::Scrolling(_) = self.focus {
entry.set_fullscreen(true);
} else {
entry.toggle_fullscreen();
if !entry.should_fullscreen() && !entry.running() {
- focus =
- crate::action::Focus::Scrolling(Some(idx));
+ focus = Focus::Scrolling(Some(idx));
}
}
self.set_focus(focus, Some(entry)).await;
@@ -276,50 +259,94 @@ impl State {
}
textmode::Key::Char('j') | textmode::Key::Down => {
self.set_focus(
- crate::action::Focus::Scrolling(
- self.scroll_down(self.focus_idx()),
- ),
+ Focus::Scrolling(self.scroll_down(self.focus_idx())),
None,
)
.await;
}
textmode::Key::Char('k') | textmode::Key::Up => {
self.set_focus(
- crate::action::Focus::Scrolling(
- self.scroll_up(self.focus_idx()),
- ),
+ Focus::Scrolling(self.scroll_up(self.focus_idx())),
None,
)
.await;
}
textmode::Key::Char('r') => {
- self.set_focus(crate::action::Focus::Readline, None).await;
+ self.set_focus(Focus::Readline, None).await;
+ }
+ _ => {}
+ }
+ (false, false)
+ }
+
+ async fn handle_key_readline(
+ &mut self,
+ key: textmode::Key,
+ event_w: async_std::channel::Sender<crate::event::Event>,
+ ) -> (bool, bool) {
+ match key {
+ textmode::Key::Char(c) => {
+ self.readline.add_input(&c.to_string());
+ }
+ textmode::Key::Ctrl(b'c') => self.readline.clear_input(),
+ textmode::Key::Ctrl(b'd') => {
+ return (true, false);
+ }
+ textmode::Key::Ctrl(b'l') => {
+ return (false, true);
+ }
+ textmode::Key::Ctrl(b'm') => {
+ let cmd = self.readline.input();
+ self.readline.clear_input();
+ let idx =
+ self.history.run(&cmd, event_w.clone()).await.unwrap();
+ self.set_focus(Focus::History(idx), None).await;
+ self.hide_readline = true;
+ }
+ textmode::Key::Ctrl(b'u') => self.readline.clear_backwards(),
+ textmode::Key::Backspace => self.readline.backspace(),
+ textmode::Key::Left => self.readline.cursor_left(),
+ textmode::Key::Right => self.readline.cursor_right(),
+ textmode::Key::Up => {
+ let entry_count = self.history.entry_count();
+ if entry_count > 0 {
+ self.set_focus(
+ Focus::Scrolling(Some(entry_count - 1)),
+ None,
+ )
+ .await;
+ }
}
_ => {}
}
- Some(crate::action::Action::Render)
+ (false, false)
+ }
+
+ async fn handle_key_history(&mut self, key: textmode::Key, idx: usize) {
+ self.history
+ .entry(idx)
+ .await
+ .send_input(key.into_bytes())
+ .await;
}
async fn default_scene(
&self,
- focus: crate::action::Focus,
+ focus: Focus,
entry: Option<async_std::sync::MutexGuardArc<crate::history::Entry>>,
- ) -> crate::action::Scene {
+ ) -> Scene {
match focus {
- crate::action::Focus::Readline
- | crate::action::Focus::Scrolling(_) => {
- crate::action::Scene::Readline
- }
- crate::action::Focus::History(idx) => {
+ Focus::Readline | Focus::Scrolling(_) => Scene::Readline,
+ Focus::History(idx) => {
let fullscreen = if let Some(entry) = entry {
entry.should_fullscreen()
} else {
self.history.entry(idx).await.should_fullscreen()
};
if fullscreen {
- crate::action::Scene::Fullscreen
+ Scene::Fullscreen
} else {
- crate::action::Scene::Readline
+ Scene::Readline
}
}
}
@@ -327,7 +354,7 @@ impl State {
async fn set_focus(
&mut self,
- new_focus: crate::action::Focus,
+ new_focus: Focus,
entry: Option<async_std::sync::MutexGuardArc<crate::history::Entry>>,
) {
self.focus = new_focus;
@@ -341,16 +368,16 @@ impl State {
.make_focus_visible(
self.readline.lines(),
self.focus_idx(),
- matches!(self.focus, crate::action::Focus::Scrolling(_)),
+ matches!(self.focus, Focus::Scrolling(_)),
)
.await;
}
fn focus_idx(&self) -> Option<usize> {
match self.focus {
- crate::action::Focus::History(idx) => Some(idx),
- crate::action::Focus::Readline => None,
- crate::action::Focus::Scrolling(idx) => idx,
+ Focus::History(idx) => Some(idx),
+ Focus::Readline => None,
+ Focus::Scrolling(idx) => idx,
}
}