diff --git a/espanso/src/config.rs b/espanso/src/config.rs index b318c5a..1ade53a 100644 --- a/espanso/src/config.rs +++ b/espanso/src/config.rs @@ -18,7 +18,11 @@ */ use anyhow::{Context, Result}; -use espanso_config::{config::ConfigStore, error::{ErrorLevel, NonFatalErrorSet}, matches::store::MatchStore}; +use espanso_config::{ + config::ConfigStore, + error::{ErrorLevel, NonFatalErrorSet}, + matches::store::MatchStore, +}; use log::{error, info, warn}; use std::path::Path; @@ -80,7 +84,8 @@ pub fn load_config(config_path: &Path, packages_path: &Path) -> Result Result>> {}", non_fatal_error_set.file.to_string_lossy().to_string()); + warn!( + ">>> {}", + non_fatal_error_set.file.to_string_lossy().to_string() + ); for record in &non_fatal_error_set.errors { if record.level == ErrorLevel::Error { error!("{:?}", record.error); @@ -106,7 +114,8 @@ pub fn load_config(config_path: &Path, packages_path: &Path) -> Result, patches: Vec) -> Self { + pub fn from_store_with_patches( + config_store: Box, + patches: Vec, + ) -> Self { // Only keep the patches that should be active in the current system - let active_patches = patches.into_iter().filter(|patch| { - let should_be_activated = (patch.should_be_activated)(); + let active_patches = patches + .into_iter() + .filter(|patch| { + let is_enabled = (patch.is_enabled)(); - if should_be_activated { - debug!("activating '{}' patch", patch.name); - } else { - debug!("skipping '{}' patch", patch.name); - } + if is_enabled { + debug!("enabled '{}' patch", patch.name); + } else { + debug!("skipping '{}' patch", patch.name); + } + + is_enabled + }) + .collect(); - should_be_activated - }).collect(); - Self { config_store, patches: active_patches, @@ -60,15 +66,19 @@ impl ConfigStore for PatchedConfigStore { self.config_store.default() } - fn active<'f>( - &'f self, - app: &espanso_config::config::AppProperties, - ) -> Arc{ - todo!() + fn active<'f>(&'f self, app: &espanso_config::config::AppProperties) -> Arc { + let active_config = self.config_store.active(app); + + // Check if a patch should be applied + if let Some(patch) = self.patches.iter().find(|patch| (patch.should_patch)(app)) { + (patch.apply)(active_config) + } else { + active_config + } } fn configs(&self) -> Vec> { - todo!() + self.config_store.configs() } fn get_all_match_paths(&self) -> std::collections::HashSet { @@ -76,4 +86,4 @@ impl ConfigStore for PatchedConfigStore { } } -// TODO: test \ No newline at end of file +// TODO: test diff --git a/espanso/src/patch/mod.rs b/espanso/src/patch/mod.rs index 303e3a3..bc782dc 100644 --- a/espanso/src/patch/mod.rs +++ b/espanso/src/patch/mod.rs @@ -19,107 +19,34 @@ use std::sync::Arc; -use espanso_config::config::{AppProperties, Backend, Config, ToggleKey}; +use espanso_config::config::{AppProperties, Config, ConfigStore}; mod config_store; mod patches; -pub fn get_builtin_patches() -> Vec { - // TODO - vec![ - patches::win::onenote_for_windows_10::patch(), - ] +pub fn patch_store(store: Box) -> Box { + Box::new(config_store::PatchedConfigStore::from_store(store)) } -// TODO: fix visibility levels (pub/pub(crate)) +fn get_builtin_patches() -> Vec { + #[cfg(target_os = "windows")] + return vec![ + patches::win::onenote_for_windows_10::patch(), + ]; -// TODO: patches should be "merged" at runtime with the active config, unless a config option (like "apply_patch") -// is set. This is needed because we still want to allow the user to define user-specific configs without -// losing the patch-specific changes + #[cfg(target_os = "macos")] + return vec![]; + + #[cfg(target_os = "linux")] + return vec![ + // TODO: all the terminals registered in the legacy version + libre office + // + firefox + ]; +} pub struct PatchDefinition { pub name: &'static str, - pub should_be_activated: fn() -> bool, - pub patch_config: fn(config: Arc) -> Arc, -} - -pub struct DefaultPatchedConfig { - base: Arc, -} - -pub trait PatchedConfig: Config { - // TODO: can we pass a simple reference here? - fn get_base(&self) -> Arc; - - fn id(&self) -> i32 { - self.get_base().id() - } - - fn label(&self) -> &str { - self.get_base().label() - } - - fn match_paths(&self) -> &[String] { - self.get_base().match_paths() - } - - fn backend(&self) -> Backend { - self.get_base().backend() - } - - fn clipboard_threshold(&self) -> usize { - self.get_base().clipboard_threshold() - } - - fn pre_paste_delay(&self) -> usize { - self.get_base().pre_paste_delay() - } - - fn paste_shortcut_event_delay(&self) -> usize { - self.get_base().paste_shortcut_event_delay() - } - - fn paste_shortcut(&self) -> Option { - self.get_base().paste_shortcut() - } - - fn disable_x11_fast_inject(&self) -> bool { - self.get_base().disable_x11_fast_inject() - } - - fn toggle_key(&self) -> Option { - self.get_base().toggle_key() - } - - fn auto_restart(&self) -> bool { - self.get_base().auto_restart() - } - - fn preserve_clipboard(&self) -> bool { - self.get_base().preserve_clipboard() - } - - fn restore_clipboard_delay(&self) -> usize { - self.get_base().restore_clipboard_delay() - } - - fn inject_delay(&self) -> Option { - self.get_base().inject_delay() - } - - fn key_delay(&self) -> Option { - self.get_base().key_delay() - } - - fn word_separators(&self) -> Vec { - self.get_base().word_separators() - } - - fn backspace_limit(&self) -> usize { - self.get_base().backspace_limit() - } - - fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool { - self.get_base().is_match(app) - } -} + pub is_enabled: fn() -> bool, + pub should_patch: fn(app: &AppProperties) -> bool, + pub apply: fn(config: Arc) -> Arc, +} \ No newline at end of file diff --git a/espanso/src/patch/patches/macros.rs b/espanso/src/patch/patches/macros.rs new file mode 100644 index 0000000..e365186 --- /dev/null +++ b/espanso/src/patch/patches/macros.rs @@ -0,0 +1,76 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +#[macro_export] +macro_rules! generate_patchable_config { + ( $struct_name:ident, $( $config_field:ident ->$config_type:ty ),* ) => { + use std::sync::Arc; + use espanso_config::config::{AppProperties, Config}; + + pub struct $struct_name { + base: Arc, + patch: Patches, + } + + impl $struct_name { + #[allow(dead_code)] + pub fn patch(base: Arc, patch: Patches) -> Self { + Self { + base, + patch, + } + } + } + + #[derive(Default)] + pub struct Patches { + $( + pub $config_field: Option<$config_type>, + )* + } + + impl Config for $struct_name { + $( + fn $config_field(&self) -> $config_type { + if let Some(patched) = self.patch.$config_field.clone() { + return patched; + } + + self.base.$config_field() + } + )* + + fn id(&self) -> i32 { + self.base.id() + } + + fn label(&self) -> &str { + self.base.label() + } + + fn match_paths(&self) -> &[String] { + self.base.match_paths() + } + + fn is_match<'b>(&self, app: &AppProperties<'b>) -> bool { + self.base.is_match(app) + } + } + }; +} \ No newline at end of file diff --git a/espanso/src/patch/patches/mod.rs b/espanso/src/patch/patches/mod.rs index 6303b8f..091b2f9 100644 --- a/espanso/src/patch/patches/mod.rs +++ b/espanso/src/patch/patches/mod.rs @@ -16,81 +16,29 @@ * You should have received a copy of the GNU General Public License * along with espanso. If not, see . */ + +use espanso_config::config::{Backend, ToggleKey}; +#[cfg(target_os = "windows")] pub mod win; +#[macro_use] +mod macros; - -// #[macro_export] -// macro_rules! create_patch { -// // TODO: add function body -// ( $should_be_activated:expr, $( $config_field:ident ->$config_type:ty ),* ) => { -// { -// $( -// if $child.$x.is_none() { -// $child.$x = $parent.$x.clone(); -// } -// )* - -// // Build a temporary object to verify that all fields -// // are being used at compile time -// $t { -// $( -// $x: None, -// )* -// }; -// } -// { -// use crate::patch::{ConfigProxy, Patch}; - -// pub struct PatchImpl<'a> { -// patched_config: PatchedConfig<'a>, -// } - -// impl<'a> PatchImpl<'a> { -// pub fn new(default: &'a dyn Config) -> Self { -// Self { -// patched_config: PatchedConfig::new(default), -// } -// } -// } - -// impl<'a> Patch<'a> for PatchImpl<'a> { -// fn should_be_activated(&self) -> bool { -// $should_be_activated -// } - -// fn patch_name(&self) -> &'static str { -// todo!() -// } - -// fn config(&self) -> &'a dyn Config { -// &self.patched_config -// } -// } - -// pub struct PatchedConfig<'a> { -// default: &'a dyn Config, -// } - -// impl<'a> PatchedConfig<'a> { -// pub fn new(default: &'a dyn Config) -> Self { -// Self { default } -// } -// } - -// impl<'a> ConfigProxy<'a> for PatchedConfig<'a> { -// fn get_default(&self) -> &'a dyn espanso_config::config::Config { -// self.default -// } - -// $( -// fn $config_field(&self) -> $config_type { -// todo!() -// } -// )* -// } - -// } -// }; -// } +generate_patchable_config!( + PatchedConfig, + backend -> Backend, + clipboard_threshold -> usize, + pre_paste_delay -> usize, + paste_shortcut_event_delay -> usize, + paste_shortcut -> Option, + disable_x11_fast_inject -> bool, + toggle_key -> Option, + auto_restart -> bool, + preserve_clipboard -> bool, + restore_clipboard_delay -> usize, + inject_delay -> Option, + key_delay -> Option, + word_separators -> Vec, + backspace_limit -> usize +); diff --git a/espanso/src/patch/patches/win/onenote_for_windows_10.rs b/espanso/src/patch/patches/win/onenote_for_windows_10.rs index ea8a788..aefb459 100644 --- a/espanso/src/patch/patches/win/onenote_for_windows_10.rs +++ b/espanso/src/patch/patches/win/onenote_for_windows_10.rs @@ -17,14 +17,24 @@ * along with espanso. If not, see . */ +use std::sync::Arc; + +use crate::patch::patches::{PatchedConfig, Patches}; use crate::patch::PatchDefinition; pub fn patch() -> PatchDefinition { PatchDefinition { - name: file!(), - should_be_activated: || cfg!(target_os = "windows"), - patch_config: |base| { - todo!() + name: module_path!().split(":").last().unwrap_or("unknown"), + is_enabled: || cfg!(target_os = "windows"), + should_patch: |app| app.title.unwrap_or_default().contains("OneNote"), + apply: |base| { + Arc::new(PatchedConfig::patch( + base, + Patches { + key_delay: Some(Some(10)), + ..Default::default() + }, + )) }, } -} +} \ No newline at end of file