diff --git a/src/config.rs b/src/config.rs index 6e9ca14..50a3421 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,6 +12,8 @@ use std::collections::HashSet; use regex::Regex; use std::process::exit; use log::{debug, info, warn, error}; +use std::cell::RefCell; +use std::time::SystemTime; // TODO: add documentation link const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("res/config.yaml"); @@ -171,23 +173,27 @@ impl ConfigSet { // TODO: tests } } -pub trait ConfigManager { - fn active_config(&self) -> &Configs; - fn default_config(&self) -> &Configs; - fn matches(&self) -> &Vec; +pub trait ConfigManager<'a> { + fn active_config(&'a self) -> &'a Configs; + fn default_config(&'a self) -> &'a Configs; + fn matches(&'a self) -> &'a Vec; } -pub struct RuntimeConfigManager { +pub struct RuntimeConfigManager<'a, S: SystemManager> { set: ConfigSet, title_regexps: Vec>, class_regexps: Vec>, exec_regexps: Vec>, - system_manager: S + system_manager: S, + + // Cache + last_config_update: RefCell, + last_config: RefCell> } -impl RuntimeConfigManager { - pub fn new(set: ConfigSet, system_manager: S) -> RuntimeConfigManager { +impl <'a, S: SystemManager> RuntimeConfigManager<'a, S> { + pub fn new<'b>(set: ConfigSet, system_manager: S) -> RuntimeConfigManager<'b, S> { // Compile all the regexps let title_regexps = set.specific.iter().map( |config| { @@ -237,18 +243,21 @@ impl RuntimeConfigManager { } ).collect(); + let last_config_update = RefCell::new(SystemTime::now()); + let last_config = RefCell::new(None); + RuntimeConfigManager { set, title_regexps, class_regexps, exec_regexps, - system_manager + system_manager, + last_config_update, + last_config } } -} -impl ConfigManager for RuntimeConfigManager { - fn active_config(&self) -> &Configs { + fn calculate_active_config(&'a self) -> &'a Configs { // TODO: optimize performance by avoiding some of these checks if no Configs use the filters debug!("Requested config for window:"); @@ -308,12 +317,36 @@ impl ConfigManager for RuntimeConfigManager { debug!("No matches for custom configs, using default settings."); &self.set.default } +} - fn default_config(&self) -> &Configs { +impl <'a, S: SystemManager> ConfigManager<'a> for RuntimeConfigManager<'a, S> { + fn active_config(&'a self) -> &'a Configs { + let mut last_config_update = self.last_config_update.borrow_mut(); + if let Ok(elapsed) = (*last_config_update).elapsed() { + *last_config_update = SystemTime::now(); + + if elapsed.as_millis() < 800 { // TODO: make config option + let last_config = self.last_config.borrow(); + if let Some(cached_config) = *last_config { + debug!("Using cached config"); + return cached_config; + } + } + } + + let config = self.calculate_active_config(); + + let mut last_config = self.last_config.borrow_mut(); + *last_config = Some(config); + + config + } + + fn default_config(&'a self) -> &'a Configs { &self.set.default } - fn matches(&self) -> &Vec { + fn matches(&'a self) -> &'a Vec { &self.active_config().matches } } \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index 31d3aff..7c411cf 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -6,7 +6,7 @@ use crate::clipboard::ClipboardManager; use log::{info}; use crate::ui::UIManager; -pub struct Engine<'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager, +pub struct Engine<'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> { sender: S, clipboard_manager: &'a C, @@ -14,14 +14,14 @@ pub struct Engine<'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager, ui_manager: &'a U, } -impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager, U: UIManager> +impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> Engine<'a, S, C, M, U> { - pub fn new<'b>(sender: S, clipboard_manager: &'b C, config_manager: &'b M, ui_manager: &'b U) -> Engine<'b, S, C, M, U> { + pub fn new(sender: S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U) -> Engine<'a, S, C, M, U> { Engine{sender, clipboard_manager, config_manager, ui_manager } } } -impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager, U: UIManager> +impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager> MatchReceiver for Engine<'a, S, C, M, U>{ fn on_match(&self, m: &Match) { diff --git a/src/matcher/scrolling.rs b/src/matcher/scrolling.rs index 2dbd6ea..94b68cb 100644 --- a/src/matcher/scrolling.rs +++ b/src/matcher/scrolling.rs @@ -6,7 +6,7 @@ use crate::keyboard::KeyModifier::BACKSPACE; use std::time::SystemTime; use std::collections::VecDeque; -pub struct ScrollingMatcher<'a, R: MatchReceiver, M: ConfigManager> { +pub struct ScrollingMatcher<'a, R: MatchReceiver, M: ConfigManager<'a>> { config_manager: &'a M, receiver: R, current_set_queue: RefCell>>>, @@ -19,7 +19,7 @@ struct MatchEntry<'a> { _match: &'a Match } -impl <'a, R: MatchReceiver, M: ConfigManager> super::Matcher for ScrollingMatcher<'a, R, M> where{ +impl <'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMatcher<'a, R, M> where{ fn handle_char(&self, c: char) { // if not enabled, avoid any processing if !*(self.is_enabled.borrow()) { @@ -100,7 +100,7 @@ impl <'a, R: MatchReceiver, M: ConfigManager> super::Matcher for ScrollingMatche } } } -impl <'a, R: MatchReceiver, M: ConfigManager> ScrollingMatcher<'a, R, M> { +impl <'a, R: MatchReceiver, M: ConfigManager<'a>> ScrollingMatcher<'a, R, M> { pub fn new(config_manager: &'a M, receiver: R) -> ScrollingMatcher<'a, R, M> { let current_set_queue = RefCell::new(VecDeque::new()); let toggle_press_time = RefCell::new(SystemTime::now());