summaryrefslogtreecommitdiffstats
path: root/src/action.rs
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/action.rs
parentcf04fbb6427eb10332c5bc143cc453aa2906e84c (diff)
downloadnbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.tar.gz
nbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.zip
avoid unnecessary rerenders and focus changes
Diffstat (limited to 'src/action.rs')
-rw-r--r--src/action.rs92
1 files changed, 92 insertions, 0 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
+}