summaryrefslogtreecommitdiffstats
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
parent62fe2477fb8bbbfbca34779b992979f9c1a8ba53 (diff)
downloadnbsh-165faf398ab2e00ebf18ad730f18b9277f612213.tar.gz
nbsh-165faf398ab2e00ebf18ad730f18b9277f612213.zip
significantly refactor the event loop
-rw-r--r--src/action.rs144
-rw-r--r--src/event.rs125
-rw-r--r--src/history.rs39
-rw-r--r--src/main.rs35
-rw-r--r--src/readline.rs54
-rw-r--r--src/state.rs269
6 files changed, 314 insertions, 352 deletions
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<usize>),
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum Scene {
- Readline,
- Fullscreen,
-}
-
-pub struct Reader {
- pending: async_std::sync::Mutex<Pending>,
- cvar: async_std::sync::Condvar,
-}
-
-impl Reader {
- pub fn new(
- input: async_std::channel::Receiver<Action>,
- ) -> async_std::sync::Arc<Self> {
- 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<Action> {
- 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<Action>) {
- let mut pending = self.pending.lock().await;
- pending.new_action(&action);
- self.cvar.notify_one();
- }
-}
-
-#[derive(Default)]
-struct Pending {
- key: std::collections::VecDeque<textmode::Key>,
- render: Option<()>,
- force_redraw: Option<()>,
- run: std::collections::VecDeque<String>,
- focus: Option<Focus>,
- scene: Option<Scene>,
- 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<Action> {
- 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<Action>) {
- 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<Pending>,
+ cvar: async_std::sync::Condvar,
+}
+
+impl Reader {
+ pub fn new(
+ input: async_std::channel::Receiver<Event>,
+ ) -> async_std::sync::Arc<Self> {
+ 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<Event> {
+ 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<Event>) {
+ let mut pending = self.pending.lock().await;
+ pending.new_event(&event);
+ self.cvar.notify_one();
+ }
+}
+
+#[derive(Default)]
+struct Pending {
+ key: std::collections::VecDeque<textmode::Key>,
+ 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<Event> {
+ 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<Event>) {
+ 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<crate::action::Action>,
+ event_w: async_std::channel::Sender<crate::event::Event>,
) -> anyhow::Result<usize> {
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<u8>) {
+ 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<Entry>,
input_r: async_std::channel::Receiver<Vec<u8>>,
resize_r: async_std::channel::Receiver<(u16, u16)>,
- action_w: async_std::channel::Sender<crate::action::Action>,
+ event_w: async_std::channel::Sender<crate::event::Event>,
) {
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<crate::action::Action>,
-) {
+async fn resize(event_w: &async_std::channel::Sender<crate::event::Event>) {
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<crate::action::Action> {
- 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<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,
}
}