From 165faf398ab2e00ebf18ad730f18b9277f612213 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 11 Dec 2021 21:00:49 -0500 Subject: significantly refactor the event loop --- src/action.rs | 144 ------------------------------ src/event.rs | 125 ++++++++++++++++++++++++++ src/history.rs | 39 ++++---- src/main.rs | 35 ++++---- src/readline.rs | 54 ++---------- src/state.rs | 269 +++++++++++++++++++++++++++++++------------------------- 6 files changed, 314 insertions(+), 352 deletions(-) delete mode 100644 src/action.rs create mode 100644 src/event.rs diff --git a/src/action.rs b/src/action.rs deleted file mode 100644 index ff0402b..0000000 --- a/src/action.rs +++ /dev/null @@ -1,144 +0,0 @@ -#[derive(Debug)] -pub enum Action { - Key(textmode::Key), - Render, - ForceRedraw, - Run(String), - UpdateFocus(Focus), - UpdateScene(Scene), - CheckUpdateScene, - Resize((u16, u16)), - Quit, -} - -#[derive(Copy, Clone, Debug)] -pub enum Focus { - Readline, - History(usize), - Scrolling(Option), -} - -#[derive(Copy, Clone, Debug)] -pub enum Scene { - Readline, - Fullscreen, -} - -pub struct Reader { - pending: async_std::sync::Mutex, - cvar: async_std::sync::Condvar, -} - -impl Reader { - pub fn new( - input: async_std::channel::Receiver, - ) -> async_std::sync::Arc { - let this = std::sync::Arc::new(Self { - pending: async_std::sync::Mutex::new(Pending::new()), - cvar: async_std::sync::Condvar::new(), - }); - { - let this = std::sync::Arc::clone(&this); - async_std::task::spawn(async move { - while let Ok(action) = input.recv().await { - this.new_action(Some(action)).await; - } - this.new_action(None).await; - }); - } - this - } - - pub async fn recv(&self) -> Option { - let mut pending = self - .cvar - .wait_until(self.pending.lock().await, |pending| { - pending.has_action() - }) - .await; - pending.get_action() - } - - async fn new_action(&self, action: Option) { - let mut pending = self.pending.lock().await; - pending.new_action(&action); - self.cvar.notify_one(); - } -} - -#[derive(Default)] -struct Pending { - key: std::collections::VecDeque, - render: Option<()>, - force_redraw: Option<()>, - run: std::collections::VecDeque, - focus: Option, - scene: Option, - check_scene: Option<()>, - size: Option<(u16, u16)>, - done: bool, -} - -impl Pending { - fn new() -> Self { - Self::default() - } - - fn has_action(&self) -> bool { - self.done - || !self.key.is_empty() - || self.render.is_some() - || self.force_redraw.is_some() - || !self.run.is_empty() - || self.focus.is_some() - || self.scene.is_some() - || self.check_scene.is_some() - || self.size.is_some() - } - - fn get_action(&mut self) -> Option { - if self.done { - return None; - } - if !self.key.is_empty() { - return Some(Action::Key(self.key.pop_front().unwrap())); - } - if self.size.is_some() { - return Some(Action::Resize(self.size.take().unwrap())); - } - if !self.run.is_empty() { - return Some(Action::Run(self.run.pop_front().unwrap())); - } - if self.focus.is_some() { - return Some(Action::UpdateFocus(self.focus.take().unwrap())); - } - if self.scene.is_some() { - return Some(Action::UpdateScene(self.scene.take().unwrap())); - } - if self.check_scene.take().is_some() { - return Some(Action::CheckUpdateScene); - } - if self.force_redraw.take().is_some() { - self.render.take(); - return Some(Action::ForceRedraw); - } - if self.render.take().is_some() { - return Some(Action::Render); - } - unreachable!() - } - - fn new_action(&mut self, action: &Option) { - match action { - Some(Action::Key(key)) => self.key.push_back(key.clone()), - Some(Action::Render) => self.render = Some(()), - Some(Action::ForceRedraw) => self.force_redraw = Some(()), - Some(Action::Run(cmd)) => self.run.push_back(cmd.to_string()), - Some(Action::UpdateFocus(focus)) => self.focus = Some(*focus), - Some(Action::UpdateScene(scene)) => self.scene = Some(*scene), - Some(Action::CheckUpdateScene) => self.check_scene = Some(()), - Some(Action::Resize(size)) => self.size = Some(*size), - Some(Action::Quit) | None => self.done = true, - } - } -} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..ca44239 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,125 @@ +#[derive(Debug)] +pub enum Event { + Key(textmode::Key), + Resize((u16, u16)), + ProcessOutput, + ProcessAlternateScreen, + ProcessExit, + ClockTimer, + Quit, +} + +pub struct Reader { + pending: async_std::sync::Mutex, + cvar: async_std::sync::Condvar, +} + +impl Reader { + pub fn new( + input: async_std::channel::Receiver, + ) -> async_std::sync::Arc { + let this = std::sync::Arc::new(Self { + pending: async_std::sync::Mutex::new(Pending::new()), + cvar: async_std::sync::Condvar::new(), + }); + { + let this = std::sync::Arc::clone(&this); + async_std::task::spawn(async move { + while let Ok(event) = input.recv().await { + this.new_event(Some(event)).await; + } + this.new_event(None).await; + }); + } + this + } + + pub async fn recv(&self) -> Option { + let mut pending = self + .cvar + .wait_until(self.pending.lock().await, |pending| { + pending.has_event() + }) + .await; + pending.get_event() + } + + async fn new_event(&self, event: Option) { + let mut pending = self.pending.lock().await; + pending.new_event(&event); + self.cvar.notify_one(); + } +} + +#[derive(Default)] +struct Pending { + key: std::collections::VecDeque, + size: Option<(u16, u16)>, + process_output: bool, + process_alternate_screen: bool, + process_exit: bool, + clock_timer: bool, + done: bool, +} + +impl Pending { + fn new() -> Self { + Self::default() + } + + fn has_event(&self) -> bool { + self.done + || !self.key.is_empty() + || self.size.is_some() + || self.process_output + || self.process_alternate_screen + || self.process_exit + || self.clock_timer + } + + fn get_event(&mut self) -> Option { + if self.done { + return None; + } + if let Some(key) = self.key.pop_front() { + return Some(Event::Key(key)); + } + if let Some(size) = self.size.take() { + return Some(Event::Resize(size)); + } + if self.process_exit { + self.process_exit = false; + return Some(Event::ProcessExit); + } + if self.process_alternate_screen { + self.process_alternate_screen = false; + return Some(Event::ProcessAlternateScreen); + } + if self.clock_timer { + self.clock_timer = false; + return Some(Event::ClockTimer); + } + // process_output should be last because it will often be the case + // that there is ~always new process output (cat on large files, yes, + // etc) and that shouldn't prevent other events from happening + if self.process_output { + self.process_output = false; + return Some(Event::ProcessOutput); + } + unreachable!() + } + + fn new_event(&mut self, event: &Option) { + match event { + Some(Event::Key(key)) => self.key.push_back(key.clone()), + Some(Event::Resize(size)) => self.size = Some(*size), + Some(Event::ProcessOutput) => self.process_output = true, + Some(Event::ProcessAlternateScreen) => { + self.process_alternate_screen = true; + } + Some(Event::ProcessExit) => self.process_exit = true, + Some(Event::ClockTimer) => self.clock_timer = true, + Some(Event::Quit) | None => self.done = true, + } + } +} diff --git a/src/history.rs b/src/history.rs index 9e0215f..59cf822 100644 --- a/src/history.rs +++ b/src/history.rs @@ -19,13 +19,6 @@ impl History { } } - pub async fn handle_key(&self, key: textmode::Key, idx: usize) { - let entry = self.entries[idx].lock_arc().await; - if entry.running() { - entry.input.send(key.into_bytes()).await.unwrap(); - } - } - pub async fn render( &self, out: &mut textmode::Output, @@ -90,7 +83,7 @@ impl History { pub async fn run( &mut self, cmd: &str, - action_w: async_std::channel::Sender, + event_w: async_std::channel::Sender, ) -> anyhow::Result { let (exe, args) = crate::parse::cmd(cmd); let (input_w, input_r) = async_std::channel::unbounded(); @@ -102,10 +95,8 @@ impl History { entry.lock_arc().await.exit_info = Some(ExitInfo::new( async_std::process::ExitStatus::from_raw(code << 8), )); - action_w - .send(crate::action::Action::UpdateFocus( - crate::action::Focus::Readline, - )) + event_w + .send(crate::event::Event::ProcessExit) .await .unwrap(); } else { @@ -122,7 +113,7 @@ impl History { async_std::sync::Arc::clone(&entry), input_r, resize_r, - action_w, + event_w, ); } self.entries.push(entry); @@ -423,6 +414,12 @@ impl Entry { } } + pub async fn send_input(&self, bytes: Vec) { + if self.running() { + self.input.send(bytes).await.unwrap(); + } + } + pub fn cmd(&self) -> String { self.cmd.clone() } @@ -499,7 +496,7 @@ fn run_process( entry: crate::util::Mutex, input_r: async_std::channel::Receiver>, resize_r: async_std::channel::Receiver<(u16, u16)>, - action_w: async_std::channel::Sender, + event_w: async_std::channel::Sender, ) { async_std::task::spawn(async move { loop { @@ -525,13 +522,13 @@ fn run_process( if entry.fullscreen.is_none() && pre_alternate_screen != post_alternate_screen { - action_w - .send(crate::action::Action::CheckUpdateScene) + event_w + .send(crate::event::Event::ProcessAlternateScreen) .await .unwrap(); } - action_w - .send(crate::action::Action::Render) + event_w + .send(crate::event::Event::ProcessOutput) .await .unwrap(); } @@ -544,10 +541,8 @@ fn run_process( entry.lock_arc().await.exit_info = Some( ExitInfo::new(child.status().await.unwrap()), ); - action_w - .send(crate::action::Action::UpdateFocus( - crate::action::Focus::Readline, - )) + event_w + .send(crate::event::Event::ProcessExit) .await .unwrap(); break; diff --git a/src/main.rs b/src/main.rs index 1a15f6e..225b21b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,14 @@ #![warn(clippy::pedantic)] #![warn(clippy::nursery)] #![allow(clippy::missing_const_for_fn)] +#![allow(clippy::struct_excessive_bools)] #![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_lines)] #![allow(clippy::unused_self)] -mod action; mod builtins; mod env; +mod event; mod format; mod history; mod parse; @@ -37,15 +38,13 @@ fn get_offset() -> time::UtcOffset { } } -async fn resize( - action_w: &async_std::channel::Sender, -) { +async fn resize(event_w: &async_std::channel::Sender) { let size = terminal_size::terminal_size().map_or( (24, 80), |(terminal_size::Width(w), terminal_size::Height(h))| (h, w), ); - action_w - .send(crate::action::Action::Resize(size)) + event_w + .send(crate::event::Event::Resize(size)) .await .unwrap(); } @@ -59,7 +58,7 @@ async fn async_main() -> anyhow::Result<()> { let _input_guard = input.take_raw_guard(); let _output_guard = output.take_screen_guard(); - let (action_w, action_r) = async_std::channel::unbounded(); + let (event_w, event_r) = async_std::channel::unbounded(); let mut state = state::State::new(get_offset()); state.render(&mut output, true).await.unwrap(); @@ -68,28 +67,28 @@ async fn async_main() -> anyhow::Result<()> { let mut signals = signal_hook_async_std::Signals::new(&[ signal_hook::consts::signal::SIGWINCH, ])?; - let action_w = action_w.clone(); + let event_w = event_w.clone(); async_std::task::spawn(async move { while signals.next().await.is_some() { - resize(&action_w).await; + resize(&event_w).await; } }); } - resize(&action_w).await; + resize(&event_w).await; { - let action_w = action_w.clone(); + let event_w = event_w.clone(); async_std::task::spawn(async move { while let Some(key) = input.read_key().await.unwrap() { - action_w.send(action::Action::Key(key)).await.unwrap(); + event_w.send(event::Event::Key(key)).await.unwrap(); } }); } // redraw the clock every second { - let action_w = action_w.clone(); + 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(), @@ -101,16 +100,16 @@ async fn async_main() -> anyhow::Result<()> { let mut interval = async_std::stream::interval( std::time::Duration::from_secs(1), ); - action_w.send(crate::action::Action::Render).await.unwrap(); + event_w.send(crate::event::Event::ClockTimer).await.unwrap(); while interval.next().await.is_some() { - action_w.send(crate::action::Action::Render).await.unwrap(); + event_w.send(crate::event::Event::ClockTimer).await.unwrap(); } }); } - let action_reader = action::Reader::new(action_r); - while let Some(action) = action_reader.recv().await { - state.handle_action(action, &mut output, &action_w).await; + let event_reader = event::Reader::new(event_r); + while let Some(event) = event_reader.recv().await { + state.handle_event(event, &mut output, &event_w).await; } Ok(()) diff --git a/src/readline.rs b/src/readline.rs index 3f71c5a..e13c00f 100644 --- a/src/readline.rs +++ b/src/readline.rs @@ -16,46 +16,6 @@ impl Readline { } } - pub async fn handle_key( - &mut self, - key: textmode::Key, - history_size: usize, - ) -> Option { - 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 Some(crate::action::Action::Quit); - } - textmode::Key::Ctrl(b'l') => { - return Some(crate::action::Action::ForceRedraw); - } - textmode::Key::Ctrl(b'm') => { - let cmd = self.input(); - self.clear_input(); - return Some(crate::action::Action::Run(cmd)); - } - textmode::Key::Ctrl(b'u') => self.clear_backwards(), - textmode::Key::Backspace => self.backspace(), - textmode::Key::Left => self.cursor_left(), - textmode::Key::Right => self.cursor_right(), - textmode::Key::Up => { - if history_size > 0 { - return Some(crate::action::Action::UpdateFocus( - crate::action::Focus::Scrolling(Some( - history_size - 1, - )), - )); - } - } - _ => {} - } - Some(crate::action::Action::Render) - } - pub async fn render( &self, out: &mut textmode::Output, @@ -120,11 +80,11 @@ impl Readline { 2 // XXX handle wrapping } - fn input(&self) -> String { + pub fn input(&self) -> String { self.input_line.clone() } - fn add_input(&mut self, s: &str) { + pub fn add_input(&mut self, s: &str) { self.input_line.insert_str(self.byte_pos(), s); self.pos += s.chars().count(); } @@ -134,7 +94,7 @@ impl Readline { self.pos = s.chars().count(); } - fn backspace(&mut self) { + pub fn backspace(&mut self) { while self.pos > 0 { self.pos -= 1; let width = @@ -145,17 +105,17 @@ impl Readline { } } - fn clear_input(&mut self) { + pub fn clear_input(&mut self) { self.input_line.clear(); self.pos = 0; } - fn clear_backwards(&mut self) { + pub fn clear_backwards(&mut self) { self.input_line = self.input_line.chars().skip(self.pos).collect(); self.pos = 0; } - fn cursor_left(&mut self) { + pub fn cursor_left(&mut self) { if self.pos == 0 { return; } @@ -169,7 +129,7 @@ impl Readline { } } - fn cursor_right(&mut self) { + pub fn cursor_right(&mut self) { if self.pos == self.input_line.chars().count() { return; } 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), +} + +#[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, + event_w: &async_std::channel::Sender, ) { 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 { - 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 { + ) -> (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, + ) -> (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>, - ) -> 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>, ) { 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 { 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, } } -- cgit v1.2.3-54-g00ecf