summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-11-11 04:26:36 -0500
committerJesse Luehrs <doy@tozt.net>2021-11-11 04:26:36 -0500
commit619cd19e91c7a1ae176a706f0e1f47887cd7a1ec (patch)
treef1d922ff835e1cc412f0101dcba44fe29aeecc6a /src
parentcf04fbb6427eb10332c5bc143cc453aa2906e84c (diff)
downloadnbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.tar.gz
nbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.zip
avoid unnecessary rerenders and focus changes
Diffstat (limited to 'src')
-rw-r--r--src/action.rs92
-rw-r--r--src/history.rs12
-rw-r--r--src/main.rs4
-rw-r--r--src/readline.rs8
-rw-r--r--src/state.rs17
5 files changed, 110 insertions, 23 deletions
diff --git a/src/action.rs b/src/action.rs
new file mode 100644
index 0000000..2cd54b7
--- /dev/null
+++ b/src/action.rs
@@ -0,0 +1,92 @@
+#[derive(Debug)]
+pub enum Action {
+ Render,
+ Run(String),
+ UpdateFocus(crate::state::Focus),
+}
+
+pub struct Debouncer {
+ pending: async_std::sync::Mutex<Pending>,
+ cvar: async_std::sync::Condvar,
+}
+
+impl Debouncer {
+ pub async fn recv(&self) -> Option<Action> {
+ let mut pending = self
+ .cvar
+ .wait_until(self.pending.lock().await, |pending| {
+ pending.has_event()
+ })
+ .await;
+ pending.get_event()
+ }
+
+ async fn send(&self, action: Option<Action>) {
+ let mut pending = self.pending.lock().await;
+ pending.new_event(&action);
+ self.cvar.notify_one();
+ }
+}
+
+#[derive(Default)]
+struct Pending {
+ render: bool,
+ run: std::collections::VecDeque<String>,
+ focus: Option<crate::state::Focus>,
+ done: bool,
+}
+
+impl Pending {
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn has_event(&self) -> bool {
+ self.render || !self.run.is_empty() || self.focus.is_some()
+ }
+
+ fn get_event(&mut self) -> Option<Action> {
+ 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.render {
+ self.render = false;
+ return Some(Action::Render);
+ }
+ if self.done {
+ return None;
+ }
+ unreachable!()
+ }
+
+ fn new_event(&mut self, action: &Option<Action>) {
+ match action {
+ Some(Action::Run(cmd)) => self.run.push_back(cmd.to_string()),
+ Some(Action::UpdateFocus(focus)) => self.focus = Some(*focus),
+ Some(Action::Render) => self.render = true,
+ None => self.done = true,
+ }
+ }
+}
+
+pub fn debounce(
+ input: async_std::channel::Receiver<Action>,
+) -> async_std::sync::Arc<Debouncer> {
+ let debouncer = std::sync::Arc::new(Debouncer {
+ pending: async_std::sync::Mutex::new(Pending::new()),
+ cvar: async_std::sync::Condvar::new(),
+ });
+ {
+ let debouncer = std::sync::Arc::clone(&debouncer);
+ async_std::task::spawn(async move {
+ while let Ok(action) = input.recv().await {
+ debouncer.send(Some(action)).await;
+ }
+ debouncer.send(None).await;
+ });
+ }
+ debouncer
+}
diff --git a/src/history.rs b/src/history.rs
index 58de34c..e5ad161 100644
--- a/src/history.rs
+++ b/src/history.rs
@@ -4,12 +4,12 @@ use textmode::Textmode as _;
pub struct History {
entries: Vec<crate::util::Mutex<HistoryEntry>>,
- action: async_std::channel::Sender<crate::state::Action>,
+ action: async_std::channel::Sender<crate::action::Action>,
}
impl History {
pub fn new(
- action: async_std::channel::Sender<crate::state::Action>,
+ action: async_std::channel::Sender<crate::action::Action>,
) -> Self {
Self {
entries: vec![],
@@ -58,7 +58,7 @@ impl History {
}
task_entry.lock_arc().await.running = false;
task_action
- .send(crate::state::Action::UpdateFocus(
+ .send(crate::action::Action::UpdateFocus(
crate::state::Focus::Readline,
))
.await
@@ -67,7 +67,7 @@ impl History {
}
}
task_action
- .send(crate::state::Action::Render)
+ .send(crate::action::Action::Render)
.await
.unwrap();
}
@@ -87,7 +87,7 @@ impl History {
});
self.entries.push(entry);
self.action
- .send(crate::state::Action::UpdateFocus(
+ .send(crate::action::Action::UpdateFocus(
crate::state::Focus::History(self.entries.len() - 1),
))
.await
@@ -108,7 +108,7 @@ impl History {
}
textmode::Key::Ctrl(b'z') => {
self.action
- .send(crate::state::Action::UpdateFocus(
+ .send(crate::action::Action::UpdateFocus(
crate::state::Focus::Readline,
))
.await
diff --git a/src/main.rs b/src/main.rs
index fe259fa..a0e4bd0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::unused_self)]
+mod action;
mod history;
mod readline;
mod state;
@@ -27,7 +28,8 @@ async fn async_main() -> anyhow::Result<()> {
{
let state = async_std::sync::Arc::clone(&state);
async_std::task::spawn(async move {
- while let Ok(action) = action_r.recv().await {
+ let debouncer = crate::action::debounce(action_r);
+ while let Some(action) = debouncer.recv().await {
state.lock_arc().await.handle_action(action).await;
}
});
diff --git a/src/readline.rs b/src/readline.rs
index 8ff001c..cc6c776 100644
--- a/src/readline.rs
+++ b/src/readline.rs
@@ -3,12 +3,12 @@ use textmode::Textmode as _;
pub struct Readline {
prompt: String,
input_line: String,
- action: async_std::channel::Sender<crate::state::Action>,
+ action: async_std::channel::Sender<crate::action::Action>,
}
impl Readline {
pub fn new(
- action: async_std::channel::Sender<crate::state::Action>,
+ action: async_std::channel::Sender<crate::action::Action>,
) -> Self {
Self {
prompt: "$ ".into(),
@@ -29,7 +29,7 @@ impl Readline {
}
textmode::Key::Ctrl(b'm') => {
self.action
- .send(crate::state::Action::Run(self.input()))
+ .send(crate::action::Action::Run(self.input()))
.await
.unwrap();
self.clear_input();
@@ -38,7 +38,7 @@ impl Readline {
_ => {}
}
self.action
- .send(crate::state::Action::Render)
+ .send(crate::action::Action::Render)
.await
.unwrap();
false
diff --git a/src/state.rs b/src/state.rs
index 6051329..5a1e1bd 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -9,7 +9,7 @@ pub struct State {
impl State {
pub fn new(
- actions: async_std::channel::Sender<Action>,
+ actions: async_std::channel::Sender<crate::action::Action>,
output: textmode::Output,
) -> Self {
let readline = crate::readline::Readline::new(actions.clone());
@@ -37,15 +37,15 @@ impl State {
Ok(())
}
- pub async fn handle_action(&mut self, action: Action) {
+ pub async fn handle_action(&mut self, action: crate::action::Action) {
match action {
- Action::Render => {
+ crate::action::Action::Render => {
self.render().await.unwrap();
}
- Action::Run(ref cmd) => {
+ crate::action::Action::Run(ref cmd) => {
self.history.run(cmd).await.unwrap();
}
- Action::UpdateFocus(new_focus) => {
+ crate::action::Action::UpdateFocus(new_focus) => {
self.focus = new_focus;
self.render().await.unwrap();
}
@@ -65,10 +65,3 @@ pub enum Focus {
Readline,
History(usize),
}
-
-#[derive(Debug)]
-pub enum Action {
- Render,
- Run(String),
- UpdateFocus(Focus),
-}