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",
]
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "dirs"
version = "1.0.5"
@ -251,6 +257,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "downcast"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]]
name = "dtoa"
version = "0.4.7"
@ -334,6 +346,7 @@ dependencies = [
"glob",
"lazy_static",
"log",
"mockall",
"ordered-float",
"regex",
"serde",
@ -455,6 +468,21 @@ dependencies = [
"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]]
name = "fuchsia-cprng"
version = "0.1.1"
@ -684,6 +712,33 @@ dependencies = [
"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]]
name = "named_pipe"
version = "0.4.1"
@ -699,6 +754,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "notify-rust"
version = "4.2.2"
@ -829,6 +890,35 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "proc-macro2"
version = "1.0.24"
@ -1288,6 +1378,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "unicase"
version = "2.6.0"

View File

@ -21,3 +21,4 @@ ordered-float = "2.0"
[dev-dependencies]
tempdir = "0.3.7"
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 resolve;
mod util;
pub(crate) mod default;
pub(crate) mod store;
#[cfg(test)]
use mockall::{automock, predicate::*};
#[cfg_attr(test, automock)]
pub trait Config: Send {
fn id(&self) -> i32;
fn label(&self) -> &str;
fn match_paths(&self) -> &[String];
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 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 {

View File

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

View File

@ -36,6 +36,9 @@ pub(crate) struct YAMLConfig {
#[serde(default)]
pub clipboard_threshold: Option<usize>,
#[serde(default)]
pub pre_paste_delay: Option<usize>,
#[serde(default)]
pub includes: Option<Vec<String>>,
@ -86,6 +89,9 @@ impl TryFrom<YAMLConfig> for ParsedConfig {
label: yaml_config.label,
backend: yaml_config.backend,
clipboard_threshold: yaml_config.clipboard_threshold,
pre_paste_delay: yaml_config.pre_paste_delay,
use_standard_includes: yaml_config.use_standard_includes,
includes: yaml_config.includes,
extra_includes: yaml_config.extra_includes,
@ -112,6 +118,7 @@ mod tests {
label: "test"
backend: clipboard
clipboard_threshold: 200
pre_paste_delay: 300
use_standard_includes: true
includes: ["test1"]
@ -136,6 +143,8 @@ mod tests {
backend: Some("clipboard".to_string()),
clipboard_threshold: Some(200),
pre_paste_delay: Some(300),
use_standard_includes: Some(true),
includes: Some(vec!["test1".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/>.
*/
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 anyhow::Result;
use log::error;
@ -118,7 +124,13 @@ impl Config for ResolvedConfig {
}
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("inject") => Backend::Inject,
Some("auto") => Backend::Auto,
@ -130,7 +142,17 @@ impl Config for ResolvedConfig {
}
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,
backend,
clipboard_threshold,
pre_paste_delay,
includes,
excludes,
extra_includes,

View File

@ -126,52 +126,22 @@ impl DefaultConfigStore {
#[cfg(test)]
mod tests {
use super::*;
use crate::config::MockConfig;
struct MockConfig {
label: String,
is_match: bool,
}
impl MockConfig {
pub fn new(label: &str, is_match: bool) -> Self {
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!()
}
pub fn new_mock(label: &'static str, is_match: bool) -> MockConfig {
let label = label.to_owned();
let mut mock = MockConfig::new();
mock.expect_id().return_const(0);
mock.expect_label().return_const(label);
mock.expect_is_match().return_const(is_match);
mock
}
#[test]
fn config_store_selects_correctly() {
let default = MockConfig::new("default", false);
let custom1 = MockConfig::new("custom1", false);
let custom2 = MockConfig::new("custom2", true);
let default = new_mock("default", false);
let custom1 = new_mock("custom1", false);
let custom2 = new_mock("custom2", true);
let store = DefaultConfigStore {
default: Box::new(default),
@ -193,9 +163,9 @@ mod tests {
#[test]
fn config_store_active_fallback_to_default_if_no_match() {
let default = MockConfig::new("default", false);
let custom1 = MockConfig::new("custom1", false);
let custom2 = MockConfig::new("custom2", false);
let default = new_mock("default", false);
let custom1 = new_mock("custom1", false);
let custom2 = new_mock("custom2", false);
let store = DefaultConfigStore {
default: Box::new(default),

View File

@ -22,7 +22,10 @@ use regex::Regex;
use std::{collections::HashMap, path::Path};
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::Config,
@ -139,10 +142,7 @@ fn deduplicate_matches(
}
// TODO: test case of matches with inner variables
fn deduplicate_vars(
vars: &mut [Variable],
var_map: &mut HashMap<Variable, StructId>,
) {
fn deduplicate_vars(vars: &mut [Variable], var_map: &mut HashMap<Variable, StructId>) {
for v in vars.iter_mut() {
let mut v_without_id = v.clone();
v_without_id.id = 0;
@ -254,7 +254,11 @@ impl Config for LegacyInteropConfig {
}
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,
});
for (i, m) in match_store.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);
for (i, m) in match_store
.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!(