diff --git a/espanso-engine/src/event/internal.rs b/espanso-engine/src/event/internal.rs index ca43c2e..eac56a3 100644 --- a/espanso-engine/src/event/internal.rs +++ b/espanso-engine/src/event/internal.rs @@ -85,6 +85,14 @@ pub struct DiscardPreviousEvent { pub minimum_source_id: u32, } +#[derive(Debug, Clone, PartialEq)] +pub struct DiscardBetweenEvent { + // All Events with a source_id between start_id (included) and end_id (excluded) + // will be discarded + pub start_id: u32, + pub end_id: u32, +} + #[derive(Debug, Clone, PartialEq)] pub struct SecureInputEnabledEvent { pub app_name: String, diff --git a/espanso-engine/src/event/mod.rs b/espanso-engine/src/event/mod.rs index f43eb96..8769e0e 100644 --- a/espanso-engine/src/event/mod.rs +++ b/espanso-engine/src/event/mod.rs @@ -71,6 +71,7 @@ pub enum EventType { ImageResolved(internal::ImageResolvedEvent), MatchInjected, DiscardPrevious(internal::DiscardPreviousEvent), + DiscardBetween(internal::DiscardBetweenEvent), Undo(internal::UndoEvent), Disabled, diff --git a/espanso-engine/src/process/default.rs b/espanso-engine/src/process/default.rs index c20fe6d..826f388 100644 --- a/espanso-engine/src/process/default.rs +++ b/espanso-engine/src/process/default.rs @@ -25,11 +25,11 @@ use super::{ cause::CauseCompensateMiddleware, cursor_hint::CursorHintMiddleware, delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, + discard::EventsDiscardMiddleware, markdown::MarkdownMiddleware, match_select::MatchSelectMiddleware, matcher::MatcherMiddleware, multiplex::MultiplexMiddleware, - past_discard::PastEventsDiscardMiddleware, render::RenderMiddleware, }, DisableOptions, EnabledStatusProvider, MatchFilter, MatchInfoProvider, MatchProvider, @@ -73,14 +73,18 @@ impl<'a> DefaultProcessor<'a> { Self { event_queue: VecDeque::new(), middleware: vec![ - Box::new(PastEventsDiscardMiddleware::new()), + Box::new(EventsDiscardMiddleware::new()), Box::new(DisableMiddleware::new(disable_options)), Box::new(IconStatusMiddleware::new()), Box::new(MatcherMiddleware::new(matchers, matcher_options_provider)), Box::new(SuppressMiddleware::new(enabled_status_provider)), Box::new(ContextMenuMiddleware::new()), Box::new(HotKeyMiddleware::new()), - Box::new(MatchSelectMiddleware::new(match_filter, match_selector)), + Box::new(MatchSelectMiddleware::new( + match_filter, + match_selector, + event_sequence_provider, + )), Box::new(CauseCompensateMiddleware::new()), Box::new(MultiplexMiddleware::new(multiplexer)), Box::new(RenderMiddleware::new(renderer)), diff --git a/espanso-engine/src/process/middleware/past_discard.rs b/espanso-engine/src/process/middleware/discard.rs similarity index 52% rename from espanso-engine/src/process/middleware/past_discard.rs rename to espanso-engine/src/process/middleware/discard.rs index 5b2b9b5..249588d 100644 --- a/espanso-engine/src/process/middleware/past_discard.rs +++ b/espanso-engine/src/process/middleware/discard.rs @@ -24,42 +24,54 @@ use log::trace; use super::super::Middleware; use crate::event::{Event, EventType, SourceId}; -/// This middleware discards all events that have a source_id smaller than its -/// configured threshold. This useful to discard past events that might have -/// been stuck in the event queue for too long. -pub struct PastEventsDiscardMiddleware { - source_id_threshold: RefCell, +/// This middleware discards all events that have a source_id between +/// the given maximum and minimum. +/// This useful to discard past events that might have been stuck in the +/// event queue for too long, or events generated while the search bar was open. +pub struct EventsDiscardMiddleware { + min_id_threshold: RefCell, + max_id_threshold: RefCell, } -impl PastEventsDiscardMiddleware { +impl EventsDiscardMiddleware { pub fn new() -> Self { Self { - source_id_threshold: RefCell::new(0), + min_id_threshold: RefCell::new(0), + max_id_threshold: RefCell::new(0), } } } -impl Middleware for PastEventsDiscardMiddleware { +impl Middleware for EventsDiscardMiddleware { fn name(&self) -> &'static str { - "past_discard" + "discard" } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { - let mut source_id_threshold = self.source_id_threshold.borrow_mut(); + let mut min_id_threshold = self.min_id_threshold.borrow_mut(); + let mut max_id_threshold = self.max_id_threshold.borrow_mut(); // Filter out previous events - if event.source_id < *source_id_threshold { + if event.source_id < *max_id_threshold && event.source_id >= *min_id_threshold { trace!("discarding previous event: {:?}", event); return Event::caused_by(event.source_id, EventType::NOOP); } - // Update the minimum threshold + // Update the thresholds if let EventType::DiscardPrevious(m_event) = &event.etype { trace!( - "updating minimum source id threshold for events to: {}", + "updating discard max_id_threshold threshold for events to: {}", m_event.minimum_source_id ); - *source_id_threshold = m_event.minimum_source_id; + *max_id_threshold = m_event.minimum_source_id; + } else if let EventType::DiscardBetween(m_event) = &event.etype { + trace!( + "updating discard thresholds for events to: max={} min={}", + m_event.end_id, + m_event.start_id + ); + *max_id_threshold = m_event.end_id; + *min_id_threshold = m_event.start_id; } event diff --git a/espanso-engine/src/process/middleware/match_select.rs b/espanso-engine/src/process/middleware/match_select.rs index 5fe0461..2467b19 100644 --- a/espanso-engine/src/process/middleware/match_select.rs +++ b/espanso-engine/src/process/middleware/match_select.rs @@ -20,7 +20,13 @@ use log::{debug, error}; use super::super::Middleware; -use crate::event::{internal::MatchSelectedEvent, Event, EventType}; +use crate::{ + event::{ + internal::{DiscardBetweenEvent, MatchSelectedEvent}, + Event, EventType, + }, + process::EventSequenceProvider, +}; pub trait MatchFilter { fn filter_active(&self, matches_ids: &[i32]) -> Vec; @@ -33,13 +39,19 @@ pub trait MatchSelector { pub struct MatchSelectMiddleware<'a> { match_filter: &'a dyn MatchFilter, match_selector: &'a dyn MatchSelector, + event_sequence_provider: &'a dyn EventSequenceProvider, } impl<'a> MatchSelectMiddleware<'a> { - pub fn new(match_filter: &'a dyn MatchFilter, match_selector: &'a dyn MatchSelector) -> Self { + pub fn new( + match_filter: &'a dyn MatchFilter, + match_selector: &'a dyn MatchSelector, + event_sequence_provider: &'a dyn EventSequenceProvider, + ) -> Self { Self { match_filter, match_selector, + event_sequence_provider, } } } @@ -49,7 +61,7 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> { "match_select" } - fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { + fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { if let EventType::MatchesDetected(m_event) = event.etype { let matches_ids: Vec = m_event.matches.iter().map(|m| m.id).collect(); @@ -75,22 +87,40 @@ impl<'a> Middleware for MatchSelectMiddleware<'a> { } } _ => { + let start_event_id = self.event_sequence_provider.get_next_id(); + // Multiple matches, we need to ask the user which one to use - if let Some(selected_id) = self.match_selector.select(&valid_ids, m_event.is_search) { - let m = m_event.matches.into_iter().find(|m| m.id == selected_id); - if let Some(m) = m { - Event::caused_by( - event.source_id, - EventType::MatchSelected(MatchSelectedEvent { chosen: m }), - ) + let next_event = + if let Some(selected_id) = self.match_selector.select(&valid_ids, m_event.is_search) { + let m = m_event.matches.into_iter().find(|m| m.id == selected_id); + if let Some(m) = m { + Event::caused_by( + event.source_id, + EventType::MatchSelected(MatchSelectedEvent { chosen: m }), + ) + } else { + error!("MatchSelectMiddleware could not find the correspondent match"); + Event::caused_by(event.source_id, EventType::NOOP) + } } else { - error!("MatchSelectMiddleware could not find the correspondent match"); + debug!("MatchSelectMiddleware did not receive any match selection"); Event::caused_by(event.source_id, EventType::NOOP) - } - } else { - debug!("MatchSelectMiddleware did not receive any match selection"); - Event::caused_by(event.source_id, EventType::NOOP) - } + }; + + let end_event_id = self.event_sequence_provider.get_next_id(); + + // We want to prevent espanso from "stacking up" events while the search bar is open, + // therefore we filter out all events that were generated while the search bar was open. + // See also: https://github.com/federico-terzi/espanso/issues/781 + dispatch(Event::caused_by( + event.source_id, + EventType::DiscardBetween(DiscardBetweenEvent { + start_id: start_event_id, + end_id: end_event_id, + }), + )); + + next_event } }; } diff --git a/espanso-engine/src/process/middleware/mod.rs b/espanso-engine/src/process/middleware/mod.rs index ec2bcc7..9ba98b3 100644 --- a/espanso-engine/src/process/middleware/mod.rs +++ b/espanso-engine/src/process/middleware/mod.rs @@ -23,6 +23,7 @@ pub mod context_menu; pub mod cursor_hint; pub mod delay_modifiers; pub mod disable; +pub mod discard; pub mod exit; pub mod hotkey; pub mod icon_status; @@ -31,7 +32,6 @@ pub mod markdown; pub mod match_select; pub mod matcher; pub mod multiplex; -pub mod past_discard; pub mod render; pub mod search; pub mod suppress;