feat(config): add pre_paste_delay configuration option

This commit is contained in:
Federico Terzi 2021-05-08 14:57:18 +02:00
parent a40125715f
commit 139fa7e511
9 changed files with 215 additions and 58 deletions

96
Cargo.lock generated
View File

@ -220,6 +220,12 @@ dependencies = [
"libdbus-sys", "libdbus-sys",
] ]
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "1.0.5" version = "1.0.5"
@ -251,6 +257,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "downcast"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.7" version = "0.4.7"
@ -334,6 +346,7 @@ dependencies = [
"glob", "glob",
"lazy_static", "lazy_static",
"log", "log",
"mockall",
"ordered-float", "ordered-float",
"regex", "regex",
"serde", "serde",
@ -455,6 +468,21 @@ dependencies = [
"widestring", "widestring",
] ]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]]
name = "fragile"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -684,6 +712,33 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "mockall"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d614ad23f9bb59119b8b5670a85c7ba92c5e9adf4385c81ea00c51c8be33d5"
dependencies = [
"cfg-if",
"downcast",
"fragile",
"lazy_static",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd4234635bca06fc96c7368d038061e0aae1b00a764dc817e900dc974e3deea"
dependencies = [
"cfg-if",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.60",
]
[[package]] [[package]]
name = "named_pipe" name = "named_pipe"
version = "0.4.1" version = "0.4.1"
@ -699,6 +754,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]] [[package]]
name = "notify-rust" name = "notify-rust"
version = "4.2.2" version = "4.2.2"
@ -829,6 +890,35 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "predicates"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
dependencies = [
"difference",
"float-cmp",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
dependencies = [
"predicates-core",
"treeline",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.24" version = "1.0.24"
@ -1288,6 +1378,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"

View File

@ -21,3 +21,4 @@ ordered-float = "2.0"
[dev-dependencies] [dev-dependencies]
tempdir = "0.3.7" tempdir = "0.3.7"
tempfile = "3.2.0" tempfile = "3.2.0"
mockall = "0.9.1"

View File

@ -0,0 +1,22 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
pub(crate) const DEFAULT_CLIPBOARD_THRESHOLD: usize = 100;
pub(crate) const DEFAULT_PRE_PASTE_DELAY: usize = 100;

View File

@ -25,16 +25,33 @@ mod parse;
mod path; mod path;
mod resolve; mod resolve;
mod util; mod util;
pub(crate) mod default;
pub(crate) mod store; pub(crate) mod store;
#[cfg(test)]
use mockall::{automock, predicate::*};
#[cfg_attr(test, automock)]
pub trait Config: Send { pub trait Config: Send {
fn id(&self) -> i32; fn id(&self) -> i32;
fn label(&self) -> &str; fn label(&self) -> &str;
fn match_paths(&self) -> &[String]; fn match_paths(&self) -> &[String];
fn backend(&self) -> Backend; fn backend(&self) -> Backend;
// Number of chars after which a match is injected with the clipboard
// backend instead of the default one. This is done for efficiency
// reasons, as injecting a long match through separate events becomes
// slow for long strings.
fn clipboard_threshold(&self) -> usize; fn clipboard_threshold(&self) -> usize;
fn is_match(&self, app: &AppProperties) -> bool; // Delay (in ms) that espanso should wait to trigger the paste shortcut
// after copying the content in the clipboard. This is needed because
// if we trigger a "paste" shortcut before the content is actually
// copied in the clipboard, the operation will fail.
fn pre_paste_delay(&self) -> usize;
// TODO: add other delay options (start by the ones needed in clipboard injector)
fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool;
} }
pub trait ConfigStore: Send { pub trait ConfigStore: Send {

View File

@ -30,6 +30,8 @@ pub(crate) struct ParsedConfig {
pub backend: Option<String>, pub backend: Option<String>,
pub clipboard_threshold: Option<usize>, pub clipboard_threshold: Option<usize>,
pub pre_paste_delay: Option<usize>,
// Includes // Includes
pub includes: Option<Vec<String>>, pub includes: Option<Vec<String>>,
pub excludes: Option<Vec<String>>, pub excludes: Option<Vec<String>>,

View File

@ -36,6 +36,9 @@ pub(crate) struct YAMLConfig {
#[serde(default)] #[serde(default)]
pub clipboard_threshold: Option<usize>, pub clipboard_threshold: Option<usize>,
#[serde(default)]
pub pre_paste_delay: Option<usize>,
#[serde(default)] #[serde(default)]
pub includes: Option<Vec<String>>, pub includes: Option<Vec<String>>,
@ -86,6 +89,9 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
label: yaml_config.label, label: yaml_config.label,
backend: yaml_config.backend, backend: yaml_config.backend,
clipboard_threshold: yaml_config.clipboard_threshold, clipboard_threshold: yaml_config.clipboard_threshold,
pre_paste_delay: yaml_config.pre_paste_delay,
use_standard_includes: yaml_config.use_standard_includes, use_standard_includes: yaml_config.use_standard_includes,
includes: yaml_config.includes, includes: yaml_config.includes,
extra_includes: yaml_config.extra_includes, extra_includes: yaml_config.extra_includes,
@ -112,6 +118,7 @@ mod tests {
label: "test" label: "test"
backend: clipboard backend: clipboard
clipboard_threshold: 200 clipboard_threshold: 200
pre_paste_delay: 300
use_standard_includes: true use_standard_includes: true
includes: ["test1"] includes: ["test1"]
@ -136,6 +143,8 @@ mod tests {
backend: Some("clipboard".to_string()), backend: Some("clipboard".to_string()),
clipboard_threshold: Some(200), clipboard_threshold: Some(200),
pre_paste_delay: Some(300),
use_standard_includes: Some(true), use_standard_includes: Some(true),
includes: Some(vec!["test1".to_string()]), includes: Some(vec!["test1".to_string()]),
extra_includes: Some(vec!["test2".to_string()]), extra_includes: Some(vec!["test2".to_string()]),

View File

@ -17,7 +17,13 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use super::{AppProperties, Backend, Config, parse::ParsedConfig, path::calculate_paths, util::os_matches}; use super::{
default::{DEFAULT_CLIPBOARD_THRESHOLD, DEFAULT_PRE_PASTE_DELAY},
parse::ParsedConfig,
path::calculate_paths,
util::os_matches,
AppProperties, Backend, Config,
};
use crate::{counter::next_id, merge}; use crate::{counter::next_id, merge};
use anyhow::Result; use anyhow::Result;
use log::error; use log::error;
@ -118,7 +124,13 @@ impl Config for ResolvedConfig {
} }
fn backend(&self) -> Backend { fn backend(&self) -> Backend {
match self.parsed.backend.as_deref().map(|b| b.to_lowercase()).as_deref() { match self
.parsed
.backend
.as_deref()
.map(|b| b.to_lowercase())
.as_deref()
{
Some("clipboard") => Backend::Clipboard, Some("clipboard") => Backend::Clipboard,
Some("inject") => Backend::Inject, Some("inject") => Backend::Inject,
Some("auto") => Backend::Auto, Some("auto") => Backend::Auto,
@ -130,7 +142,17 @@ impl Config for ResolvedConfig {
} }
fn clipboard_threshold(&self) -> usize { fn clipboard_threshold(&self) -> usize {
self.parsed.clipboard_threshold.unwrap_or(100) self
.parsed
.clipboard_threshold
.unwrap_or(DEFAULT_CLIPBOARD_THRESHOLD)
}
fn pre_paste_delay(&self) -> usize {
self
.parsed
.pre_paste_delay
.unwrap_or(DEFAULT_PRE_PASTE_DELAY)
} }
} }
@ -190,6 +212,7 @@ impl ResolvedConfig {
label, label,
backend, backend,
clipboard_threshold, clipboard_threshold,
pre_paste_delay,
includes, includes,
excludes, excludes,
extra_includes, extra_includes,

View File

@ -126,52 +126,22 @@ impl DefaultConfigStore {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::config::MockConfig;
struct MockConfig { pub fn new_mock(label: &'static str, is_match: bool) -> MockConfig {
label: String, let label = label.to_owned();
is_match: bool, let mut mock = MockConfig::new();
} mock.expect_id().return_const(0);
mock.expect_label().return_const(label);
impl MockConfig { mock.expect_is_match().return_const(is_match);
pub fn new(label: &str, is_match: bool) -> Self { mock
Self {
label: label.to_string(),
is_match,
}
}
}
impl Config for MockConfig {
fn id(&self) -> i32 {
0
}
fn label(&self) -> &str {
&self.label
}
fn match_paths(&self) -> &[String] {
unimplemented!()
}
fn is_match(&self, _: &crate::config::AppProperties) -> bool {
self.is_match
}
fn backend(&self) -> crate::config::Backend {
unimplemented!()
}
fn clipboard_threshold(&self) -> usize {
unimplemented!()
}
} }
#[test] #[test]
fn config_store_selects_correctly() { fn config_store_selects_correctly() {
let default = MockConfig::new("default", false); let default = new_mock("default", false);
let custom1 = MockConfig::new("custom1", false); let custom1 = new_mock("custom1", false);
let custom2 = MockConfig::new("custom2", true); let custom2 = new_mock("custom2", true);
let store = DefaultConfigStore { let store = DefaultConfigStore {
default: Box::new(default), default: Box::new(default),
@ -193,9 +163,9 @@ mod tests {
#[test] #[test]
fn config_store_active_fallback_to_default_if_no_match() { fn config_store_active_fallback_to_default_if_no_match() {
let default = MockConfig::new("default", false); let default = new_mock("default", false);
let custom1 = MockConfig::new("custom1", false); let custom1 = new_mock("custom1", false);
let custom2 = MockConfig::new("custom2", false); let custom2 = new_mock("custom2", false);
let store = DefaultConfigStore { let store = DefaultConfigStore {
default: Box::new(default), default: Box::new(default),

View File

@ -22,7 +22,10 @@ use regex::Regex;
use std::{collections::HashMap, path::Path}; use std::{collections::HashMap, path::Path};
use self::config::LegacyConfig; use self::config::LegacyConfig;
use crate::matches::{MatchEffect, group::loader::yaml::parse::{YAMLMatch, YAMLVariable}}; use crate::matches::{
group::loader::yaml::parse::{YAMLMatch, YAMLVariable},
MatchEffect,
};
use crate::{config::store::DefaultConfigStore, counter::StructId}; use crate::{config::store::DefaultConfigStore, counter::StructId};
use crate::{ use crate::{
config::Config, config::Config,
@ -139,10 +142,7 @@ fn deduplicate_matches(
} }
// TODO: test case of matches with inner variables // TODO: test case of matches with inner variables
fn deduplicate_vars( fn deduplicate_vars(vars: &mut [Variable], var_map: &mut HashMap<Variable, StructId>) {
vars: &mut [Variable],
var_map: &mut HashMap<Variable, StructId>,
) {
for v in vars.iter_mut() { for v in vars.iter_mut() {
let mut v_without_id = v.clone(); let mut v_without_id = v.clone();
v_without_id.id = 0; v_without_id.id = 0;
@ -254,7 +254,11 @@ impl Config for LegacyInteropConfig {
} }
fn clipboard_threshold(&self) -> usize { fn clipboard_threshold(&self) -> usize {
100 crate::config::default::DEFAULT_CLIPBOARD_THRESHOLD
}
fn pre_paste_delay(&self) -> usize {
crate::config::default::DEFAULT_PRE_PASTE_DELAY
} }
} }
@ -472,8 +476,21 @@ mod tests {
exec: None, exec: None,
}); });
for (i, m) in match_store.query(default_config.match_paths()).matches.into_iter().enumerate() { for (i, m) in match_store
assert_eq!(m.id, match_store.query(active_config.match_paths()).matches.get(i).unwrap().id); .query(default_config.match_paths())
.matches
.into_iter()
.enumerate()
{
assert_eq!(
m.id,
match_store
.query(active_config.match_paths())
.matches
.get(i)
.unwrap()
.id
);
} }
assert_eq!( assert_eq!(