From 619cd19e91c7a1ae176a706f0e1f47887cd7a1ec Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Thu, 11 Nov 2021 04:26:36 -0500 Subject: avoid unnecessary rerenders and focus changes --- src/action.rs | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/action.rs (limited to 'src/action.rs') 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, + cvar: async_std::sync::Condvar, +} + +impl Debouncer { + 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 send(&self, action: Option) { + 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, + focus: Option, + 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 { + 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) { + 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, +) -> async_std::sync::Arc { + 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 +} -- cgit v1.2.3-54-g00ecf