diff options
author | Jesse Luehrs <doy@tozt.net> | 2021-11-11 04:26:36 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2021-11-11 04:26:36 -0500 |
commit | 619cd19e91c7a1ae176a706f0e1f47887cd7a1ec (patch) | |
tree | f1d922ff835e1cc412f0101dcba44fe29aeecc6a /src/action.rs | |
parent | cf04fbb6427eb10332c5bc143cc453aa2906e84c (diff) | |
download | nbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.tar.gz nbsh-619cd19e91c7a1ae176a706f0e1f47887cd7a1ec.zip |
avoid unnecessary rerenders and focus changes
Diffstat (limited to 'src/action.rs')
-rw-r--r-- | src/action.rs | 92 |
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 +} |