Fix formatting

This commit is contained in:
Federico Terzi 2020-08-15 19:35:14 +02:00
parent 161017f024
commit 411078a503
20 changed files with 312 additions and 179 deletions

View File

@ -244,7 +244,7 @@ pub struct Configs {
#[serde(default = "default_secure_input_watcher_interval")] #[serde(default = "default_secure_input_watcher_interval")]
pub secure_input_watcher_interval: i32, pub secure_input_watcher_interval: i32,
#[serde(default = "default_mac_post_inject_delay")] #[serde(default = "default_mac_post_inject_delay")]
pub mac_post_inject_delay: u64, pub mac_post_inject_delay: u64,
@ -282,7 +282,7 @@ pub struct Configs {
pub global_vars: Vec<MatchVariable>, pub global_vars: Vec<MatchVariable>,
#[serde(default = "default_modulo_path")] #[serde(default = "default_modulo_path")]
pub modulo_path: Option<String> pub modulo_path: Option<String>,
} }
// Macro used to validate config fields // Macro used to validate config fields

View File

@ -19,7 +19,7 @@
use crate::clipboard::ClipboardManager; use crate::clipboard::ClipboardManager;
use crate::config::BackendType; use crate::config::BackendType;
use crate::config::{Configs, ConfigManager}; use crate::config::{ConfigManager, Configs};
use crate::event::{ActionEventReceiver, ActionType, SystemEvent, SystemEventReceiver}; use crate::event::{ActionEventReceiver, ActionType, SystemEvent, SystemEventReceiver};
use crate::keyboard::KeyboardManager; use crate::keyboard::KeyboardManager;
use crate::matcher::{Match, MatchReceiver}; use crate::matcher::{Match, MatchReceiver};
@ -158,9 +158,7 @@ impl<
if cfg!(target_os = "linux") { if cfg!(target_os = "linux") {
let all_ascii = target_string.chars().all(|c| c.is_ascii()); let all_ascii = target_string.chars().all(|c| c.is_ascii());
if all_ascii { if all_ascii {
debug!( debug!("All elements of the replacement are ascii, using Inject backend");
"All elements of the replacement are ascii, using Inject backend"
);
&BackendType::Inject &BackendType::Inject
} else { } else {
debug!("There are non-ascii characters, using Clipboard backend"); debug!("There are non-ascii characters, using Clipboard backend");
@ -273,7 +271,10 @@ impl<
// Disallow undo backspace if cursor positioning is used // Disallow undo backspace if cursor positioning is used
if cursor_rewind.is_none() { if cursor_rewind.is_none() {
expansion_data = Some((m.triggers[trigger_offset].clone(), target_string.chars().count() as i32)); expansion_data = Some((
m.triggers[trigger_offset].clone(),
target_string.chars().count() as i32,
));
} }
if let Some(moves) = cursor_rewind { if let Some(moves) = cursor_rewind {
@ -310,7 +311,9 @@ impl<
// giving back the control. Otherwise, the injected actions will be handled back // giving back the control. Otherwise, the injected actions will be handled back
// by espanso itself. // by espanso itself.
if cfg!(target_os = "macos") { if cfg!(target_os = "macos") {
std::thread::sleep(std::time::Duration::from_millis(config.mac_post_inject_delay)); std::thread::sleep(std::time::Duration::from_millis(
config.mac_post_inject_delay,
));
} }
// Re-allow espanso to interpret actions // Re-allow espanso to interpret actions
@ -350,7 +353,8 @@ impl<
if let Some(ref last_expansion_data) = *last_expansion_data { if let Some(ref last_expansion_data) = *last_expansion_data {
let (trigger_string, injected_text_len) = last_expansion_data; let (trigger_string, injected_text_len) = last_expansion_data;
// Delete the previously injected text, minus one character as it has been consumed by the backspace // Delete the previously injected text, minus one character as it has been consumed by the backspace
self.keyboard_manager.delete_string(&config, *injected_text_len - 1); self.keyboard_manager
.delete_string(&config, *injected_text_len - 1);
// Restore previous text // Restore previous text
self.inject_text(&config, trigger_string, false); self.inject_text(&config, trigger_string, false);
} }

View File

@ -18,8 +18,8 @@
*/ */
use crate::clipboard::ClipboardManager; use crate::clipboard::ClipboardManager;
use serde_yaml::Mapping;
use crate::extension::ExtensionResult; use crate::extension::ExtensionResult;
use serde_yaml::Mapping;
use std::collections::HashMap; use std::collections::HashMap;
pub struct ClipboardExtension { pub struct ClipboardExtension {
@ -37,7 +37,12 @@ impl super::Extension for ClipboardExtension {
String::from("clipboard") String::from("clipboard")
} }
fn calculate(&self, _: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
_: &Mapping,
_: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
if let Some(clipboard) = self.clipboard_manager.get_clipboard() { if let Some(clipboard) = self.clipboard_manager.get_clipboard() {
Some(ExtensionResult::Single(clipboard)) Some(ExtensionResult::Single(clipboard))
} else { } else {

View File

@ -17,10 +17,10 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use chrono::{DateTime, Local, Duration}; use crate::extension::ExtensionResult;
use chrono::{DateTime, Duration, Local};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct DateExtension {} pub struct DateExtension {}
@ -35,13 +35,18 @@ impl super::Extension for DateExtension {
String::from("date") String::from("date")
} }
fn calculate(&self, params: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
_: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let mut now: DateTime<Local> = Local::now(); let mut now: DateTime<Local> = Local::now();
// Compute the given offset // Compute the given offset
let offset = params.get(&Value::from("offset")); let offset = params.get(&Value::from("offset"));
if let Some(offset) = offset { if let Some(offset) = offset {
let seconds = offset.as_i64().unwrap_or_else(|| { 0 }); let seconds = offset.as_i64().unwrap_or_else(|| 0);
let offset = Duration::seconds(seconds); let offset = Duration::seconds(seconds);
now = now + offset; now = now + offset;
} }

View File

@ -17,9 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct DummyExtension { pub struct DummyExtension {
name: String, name: String,
@ -38,11 +38,18 @@ impl super::Extension for DummyExtension {
self.name.clone() self.name.clone()
} }
fn calculate(&self, params: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
_: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let echo = params.get(&Value::from("echo")); let echo = params.get(&Value::from("echo"));
if let Some(echo) = echo { if let Some(echo) = echo {
Some(ExtensionResult::Single(echo.as_str().unwrap_or_default().to_owned())) Some(ExtensionResult::Single(
echo.as_str().unwrap_or_default().to_owned(),
))
} else { } else {
None None
} }

View File

@ -17,10 +17,10 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::{config::Configs, extension::ExtensionResult, ui::modulo::ModuloManager};
use log::{error, warn};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ui::modulo::ModuloManager, extension::ExtensionResult, config::Configs};
use log::{error, warn};
pub struct FormExtension { pub struct FormExtension {
manager: ModuloManager, manager: ModuloManager,
@ -29,9 +29,7 @@ pub struct FormExtension {
impl FormExtension { impl FormExtension {
pub fn new(config: &Configs) -> FormExtension { pub fn new(config: &Configs) -> FormExtension {
let manager = ModuloManager::new(config); let manager = ModuloManager::new(config);
FormExtension { FormExtension { manager }
manager,
}
} }
} }
@ -40,7 +38,12 @@ impl super::Extension for FormExtension {
"form".to_owned() "form".to_owned()
} }
fn calculate(&self, params: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
_: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let layout = params.get(&Value::from("layout")); let layout = params.get(&Value::from("layout"));
let layout = if let Some(value) = layout { let layout = if let Some(value) = layout {
value.as_str().unwrap_or_default().to_string() value.as_str().unwrap_or_default().to_string()
@ -48,21 +51,24 @@ impl super::Extension for FormExtension {
error!("invoking form extension without specifying a layout"); error!("invoking form extension without specifying a layout");
return None; return None;
}; };
let mut form_config = Mapping::new(); let mut form_config = Mapping::new();
form_config.insert(Value::from("layout"), Value::from(layout)); form_config.insert(Value::from("layout"), Value::from(layout));
if let Some(fields) = params.get(&Value::from("fields")) {
form_config.insert(Value::from("fields"), fields.clone());
}
let serialized_config: String = serde_yaml::to_string(&form_config).expect("unable to serialize form config");
let output = self.manager.invoke(&["form", "-i", "-"], &serialized_config); if let Some(fields) = params.get(&Value::from("fields")) {
form_config.insert(Value::from("fields"), fields.clone());
}
let serialized_config: String =
serde_yaml::to_string(&form_config).expect("unable to serialize form config");
let output = self
.manager
.invoke(&["form", "-i", "-"], &serialized_config);
// On macOS, after the form closes we have to wait until the user releases the modifier keys // On macOS, after the form closes we have to wait until the user releases the modifier keys
on_form_close(); on_form_close();
if let Some(output) = output { if let Some(output) = output {
let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output); let json: Result<HashMap<String, String>, _> = serde_json::from_str(&output);
match json { match json {
@ -77,7 +83,7 @@ impl super::Extension for FormExtension {
} else { } else {
error!("modulo form didn't return any output"); error!("modulo form didn't return any output");
return None; return None;
} }
} }
} }
@ -92,4 +98,4 @@ fn on_form_close() {
if !released { if !released {
warn!("Wait for modifiers release timed out! Please after closing the form, release your modifiers keys (CTRL, CMD, ALT, SHIFT)"); warn!("Wait for modifiers release timed out! Please after closing the form, release your modifiers keys (CTRL, CMD, ALT, SHIFT)");
} }
} }

View File

@ -17,20 +17,20 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::{config::Configs, clipboard::ClipboardManager}; use crate::{clipboard::ClipboardManager, config::Configs};
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::collections::HashMap; use std::collections::HashMap;
mod clipboard; mod clipboard;
mod date; mod date;
pub mod dummy; pub mod dummy;
mod form;
pub mod multiecho;
mod random; mod random;
mod script; mod script;
mod shell; mod shell;
pub mod multiecho;
pub mod vardummy;
mod utils; mod utils;
mod form; pub mod vardummy;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ExtensionResult { pub enum ExtensionResult {
@ -40,10 +40,18 @@ pub enum ExtensionResult {
pub trait Extension { pub trait Extension {
fn name(&self) -> String; fn name(&self) -> String;
fn calculate(&self, params: &Mapping, args: &Vec<String>, current_vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult>; fn calculate(
&self,
params: &Mapping,
args: &Vec<String>,
current_vars: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult>;
} }
pub fn get_extensions(config: &Configs, clipboard_manager: Box<dyn ClipboardManager>) -> Vec<Box<dyn Extension>> { pub fn get_extensions(
config: &Configs,
clipboard_manager: Box<dyn ClipboardManager>,
) -> Vec<Box<dyn Extension>> {
vec![ vec![
Box::new(date::DateExtension::new()), Box::new(date::DateExtension::new()),
Box::new(shell::ShellExtension::new()), Box::new(shell::ShellExtension::new()),

View File

@ -17,9 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct MultiEchoExtension {} pub struct MultiEchoExtension {}
@ -34,7 +34,12 @@ impl super::Extension for MultiEchoExtension {
"multiecho".to_owned() "multiecho".to_owned()
} }
fn calculate(&self, params: &Mapping, _: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
_: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let mut output: HashMap<String, String> = HashMap::new(); let mut output: HashMap<String, String> = HashMap::new();
for (key, value) in params.iter() { for (key, value) in params.iter() {
if let Some(key) = key.as_str() { if let Some(key) = key.as_str() {

View File

@ -17,11 +17,11 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use log::{error, warn}; use log::{error, warn};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct RandomExtension {} pub struct RandomExtension {}
@ -36,7 +36,12 @@ impl super::Extension for RandomExtension {
String::from("random") String::from("random")
} }
fn calculate(&self, params: &Mapping, args: &Vec<String>, _: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
args: &Vec<String>,
_: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let choices = params.get(&Value::from("choices")); let choices = params.get(&Value::from("choices"));
if choices.is_none() { if choices.is_none() {
warn!("No 'choices' parameter specified for random variable"); warn!("No 'choices' parameter specified for random variable");
@ -89,7 +94,9 @@ mod tests {
let output = output.unwrap(); let output = output.unwrap();
assert!(choices.into_iter().any(|x| ExtensionResult::Single(x.to_owned()) == output)); assert!(choices
.into_iter()
.any(|x| ExtensionResult::Single(x.to_owned()) == output));
} }
#[test] #[test]
@ -107,6 +114,8 @@ mod tests {
let rendered_choices = vec!["first test", "second test", "test third"]; let rendered_choices = vec!["first test", "second test", "test third"];
assert!(rendered_choices.into_iter().any(|x| ExtensionResult::Single(x.to_owned()) == output)); assert!(rendered_choices
.into_iter()
.any(|x| ExtensionResult::Single(x.to_owned()) == output));
} }
} }

View File

@ -17,12 +17,12 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use log::{error, warn}; use log::{error, warn};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct ScriptExtension {} pub struct ScriptExtension {}
@ -37,7 +37,12 @@ impl super::Extension for ScriptExtension {
String::from("script") String::from("script")
} }
fn calculate(&self, params: &Mapping, user_args: &Vec<String>, vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
user_args: &Vec<String>,
vars: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let args = params.get(&Value::from("args")); let args = params.get(&Value::from("args"));
if args.is_none() { if args.is_none() {
warn!("No 'args' parameter specified for script variable"); warn!("No 'args' parameter specified for script variable");
@ -69,10 +74,20 @@ impl super::Extension for ScriptExtension {
*arg = arg.replace("%HOME%", &home_dir.to_string_lossy().to_string()); *arg = arg.replace("%HOME%", &home_dir.to_string_lossy().to_string());
} }
if arg.contains("%CONFIG%") { if arg.contains("%CONFIG%") {
*arg = arg.replace("%CONFIG%", &crate::context::get_config_dir().to_string_lossy().to_string()); *arg = arg.replace(
"%CONFIG%",
&crate::context::get_config_dir()
.to_string_lossy()
.to_string(),
);
} }
if arg.contains("%PACKAGES%") { if arg.contains("%PACKAGES%") {
*arg = arg.replace("%PACKAGES%", &crate::context::get_package_dir().to_string_lossy().to_string()); *arg = arg.replace(
"%PACKAGES%",
&crate::context::get_package_dir()
.to_string_lossy()
.to_string(),
);
} }
// On Windows, correct paths separators // On Windows, correct paths separators
@ -162,7 +177,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -179,7 +197,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world\n".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world\n".to_owned())
);
} }
#[test] #[test]
@ -195,7 +216,10 @@ mod tests {
let output = extension.calculate(&params, &vec!["jon".to_owned()], &HashMap::new()); let output = extension.calculate(&params, &vec!["jon".to_owned()], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -212,7 +236,10 @@ mod tests {
let output = extension.calculate(&params, &vec!["jon".to_owned()], &HashMap::new()); let output = extension.calculate(&params, &vec!["jon".to_owned()], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world jon".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world jon".to_owned())
);
} }
#[test] #[test]
@ -228,12 +255,18 @@ mod tests {
let mut subvars = HashMap::new(); let mut subvars = HashMap::new();
subvars.insert("name".to_owned(), "John".to_owned()); subvars.insert("name".to_owned(), "John".to_owned());
vars.insert("form1".to_owned(), ExtensionResult::Multiple(subvars)); vars.insert("form1".to_owned(), ExtensionResult::Multiple(subvars));
vars.insert("var1".to_owned(), ExtensionResult::Single("hello".to_owned())); vars.insert(
"var1".to_owned(),
ExtensionResult::Single("hello".to_owned()),
);
let extension = ScriptExtension::new(); let extension = ScriptExtension::new();
let output = extension.calculate(&params, &vec![], &vars); let output = extension.calculate(&params, &vec![], &vars);
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello John".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello John".to_owned())
);
} }
} }

View File

@ -17,12 +17,12 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use log::{error, info, warn}; use log::{error, info, warn};
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::process::{Command, Output};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult; use std::process::{Command, Output};
lazy_static! { lazy_static! {
static ref POS_ARG_REGEX: Regex = if cfg!(target_os = "windows") { static ref POS_ARG_REGEX: Regex = if cfg!(target_os = "windows") {
@ -150,7 +150,12 @@ impl super::Extension for ShellExtension {
String::from("shell") String::from("shell")
} }
fn calculate(&self, params: &Mapping, args: &Vec<String>, vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
args: &Vec<String>,
vars: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let cmd = params.get(&Value::from("cmd")); let cmd = params.get(&Value::from("cmd"));
if cmd.is_none() { if cmd.is_none() {
warn!("No 'cmd' parameter specified for shell variable"); warn!("No 'cmd' parameter specified for shell variable");
@ -260,9 +265,15 @@ mod tests {
assert!(output.is_some()); assert!(output.is_some());
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world\r\n".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world\r\n".to_owned())
);
} else { } else {
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world\n".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world\n".to_owned())
);
} }
} }
@ -275,7 +286,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -290,7 +304,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -303,7 +320,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -317,7 +337,10 @@ mod tests {
let output = extension.calculate(&params, &vec![], &HashMap::new()); let output = extension.calculate(&params, &vec![], &HashMap::new());
assert!(output.is_some()); assert!(output.is_some());
assert_eq!(output.unwrap(), ExtensionResult::Single("hello world".to_owned())); assert_eq!(
output.unwrap(),
ExtensionResult::Single("hello world".to_owned())
);
} }
#[test] #[test]
@ -354,13 +377,16 @@ mod tests {
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
params.insert(Value::from("cmd"), Value::from("echo %ESPANSO_VAR1%")); params.insert(Value::from("cmd"), Value::from("echo %ESPANSO_VAR1%"));
params.insert(Value::from("shell"), Value::from("cmd")); params.insert(Value::from("shell"), Value::from("cmd"));
}else{ } else {
params.insert(Value::from("cmd"), Value::from("echo $ESPANSO_VAR1")); params.insert(Value::from("cmd"), Value::from("echo $ESPANSO_VAR1"));
} }
let extension = ShellExtension::new(); let extension = ShellExtension::new();
let mut vars: HashMap<String, ExtensionResult> = HashMap::new(); let mut vars: HashMap<String, ExtensionResult> = HashMap::new();
vars.insert("var1".to_owned(), ExtensionResult::Single("hello".to_owned())); vars.insert(
"var1".to_owned(),
ExtensionResult::Single("hello".to_owned()),
);
let output = extension.calculate(&params, &vec![], &vars); let output = extension.calculate(&params, &vec![], &vars);
assert!(output.is_some()); assert!(output.is_some());
@ -373,7 +399,7 @@ mod tests {
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
params.insert(Value::from("cmd"), Value::from("echo %ESPANSO_FORM1_NAME%")); params.insert(Value::from("cmd"), Value::from("echo %ESPANSO_FORM1_NAME%"));
params.insert(Value::from("shell"), Value::from("cmd")); params.insert(Value::from("shell"), Value::from("cmd"));
}else{ } else {
params.insert(Value::from("cmd"), Value::from("echo $ESPANSO_FORM1_NAME")); params.insert(Value::from("cmd"), Value::from("echo $ESPANSO_FORM1_NAME"));
} }

View File

@ -1,8 +1,10 @@
use crate::extension::ExtensionResult;
use std::collections::HashMap; use std::collections::HashMap;
use std::process::Command; use std::process::Command;
use crate::extension::ExtensionResult;
pub fn convert_to_env_variables(original_vars: &HashMap<String, ExtensionResult>) -> HashMap<String, String> { pub fn convert_to_env_variables(
original_vars: &HashMap<String, ExtensionResult>,
) -> HashMap<String, String> {
let mut output = HashMap::new(); let mut output = HashMap::new();
for (key, result) in original_vars.iter() { for (key, result) in original_vars.iter() {
@ -10,13 +12,13 @@ pub fn convert_to_env_variables(original_vars: &HashMap<String, ExtensionResult>
ExtensionResult::Single(value) => { ExtensionResult::Single(value) => {
let name = format!("ESPANSO_{}", key.to_uppercase()); let name = format!("ESPANSO_{}", key.to_uppercase());
output.insert(name, value.clone()); output.insert(name, value.clone());
}, }
ExtensionResult::Multiple(values) => { ExtensionResult::Multiple(values) => {
for (sub_key, sub_value) in values.iter() { for (sub_key, sub_value) in values.iter() {
let name = format!("ESPANSO_{}_{}", key.to_uppercase(), sub_key.to_uppercase()); let name = format!("ESPANSO_{}_{}", key.to_uppercase(), sub_key.to_uppercase());
output.insert(name, sub_value.clone()); output.insert(name, sub_value.clone());
} }
}, }
} }
} }
@ -36,7 +38,6 @@ pub fn set_command_flags(command: &mut Command) {
// NOOP on Linux and macOS // NOOP on Linux and macOS
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -49,11 +50,14 @@ mod tests {
subvars.insert("name".to_owned(), "John".to_owned()); subvars.insert("name".to_owned(), "John".to_owned());
subvars.insert("lastname".to_owned(), "Snow".to_owned()); subvars.insert("lastname".to_owned(), "Snow".to_owned());
vars.insert("form1".to_owned(), ExtensionResult::Multiple(subvars)); vars.insert("form1".to_owned(), ExtensionResult::Multiple(subvars));
vars.insert("var1".to_owned(), ExtensionResult::Single("test".to_owned())); vars.insert(
"var1".to_owned(),
ExtensionResult::Single("test".to_owned()),
);
let output = convert_to_env_variables(&vars); let output = convert_to_env_variables(&vars);
assert_eq!(output.get("ESPANSO_FORM1_NAME").unwrap(), "John"); assert_eq!(output.get("ESPANSO_FORM1_NAME").unwrap(), "John");
assert_eq!(output.get("ESPANSO_FORM1_LASTNAME").unwrap(), "Snow"); assert_eq!(output.get("ESPANSO_FORM1_LASTNAME").unwrap(), "Snow");
assert_eq!(output.get("ESPANSO_VAR1").unwrap(), "test"); assert_eq!(output.get("ESPANSO_VAR1").unwrap(), "test");
} }
} }

View File

@ -17,9 +17,9 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::extension::ExtensionResult;
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::collections::HashMap; use std::collections::HashMap;
use crate::extension::ExtensionResult;
pub struct VarDummyExtension {} pub struct VarDummyExtension {}
@ -34,7 +34,12 @@ impl super::Extension for VarDummyExtension {
"vardummy".to_owned() "vardummy".to_owned()
} }
fn calculate(&self, params: &Mapping, _: &Vec<String>, vars: &HashMap<String, ExtensionResult>) -> Option<ExtensionResult> { fn calculate(
&self,
params: &Mapping,
_: &Vec<String>,
vars: &HashMap<String, ExtensionResult>,
) -> Option<ExtensionResult> {
let target = params.get(&Value::from("target")); let target = params.get(&Value::from("target"));
if let Some(target) = target { if let Some(target) = target {

View File

@ -20,7 +20,7 @@
use super::PasteShortcut; use super::PasteShortcut;
use crate::bridge::macos::*; use crate::bridge::macos::*;
use crate::config::Configs; use crate::config::Configs;
use log::{error}; use log::error;
use std::ffi::CString; use std::ffi::CString;
pub struct MacKeyboardManager {} pub struct MacKeyboardManager {}
@ -78,12 +78,12 @@ impl super::KeyboardManager for MacKeyboardManager {
pub fn wait_for_modifiers_release() -> bool { pub fn wait_for_modifiers_release() -> bool {
let start = std::time::SystemTime::now(); let start = std::time::SystemTime::now();
while start.elapsed().unwrap_or_default().as_millis() < 3000 { while start.elapsed().unwrap_or_default().as_millis() < 3000 {
let pressed = unsafe { crate::bridge::macos::are_modifiers_pressed() }; let pressed = unsafe { crate::bridge::macos::are_modifiers_pressed() };
if pressed == 0 { if pressed == 0 {
return true return true;
} }
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
} }
false false
} }

View File

@ -17,7 +17,7 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>. * along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/ */
#![cfg_attr(not(test), windows_subsystem = "windows")] #![cfg_attr(not(test), windows_subsystem = "windows")]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -352,7 +352,7 @@ fn main() {
fn attach_console() { fn attach_console() {
// When using the windows subsystem we loose the terminal output. // When using the windows subsystem we loose the terminal output.
// Therefore we try to attach to the current console if available. // Therefore we try to attach to the current console if available.
unsafe {winapi::um::wincon::AttachConsole(0xFFFFFFFF)}; unsafe { winapi::um::wincon::AttachConsole(0xFFFFFFFF) };
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
@ -582,8 +582,13 @@ fn watcher_background(sender: Sender<Event>) {
}; };
if let Some(path) = path { if let Some(path) = path {
if path.extension().unwrap_or_default() == "yml" && if path.extension().unwrap_or_default() == "yml"
!path.file_name().unwrap_or_default().to_string_lossy().starts_with("."){ && !path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.starts_with(".")
{
// Only load non-hidden yml files // Only load non-hidden yml files
true true
} else { } else {
@ -688,7 +693,10 @@ fn worker_background(
let keyboard_manager = keyboard::get_manager(); let keyboard_manager = keyboard::get_manager();
let extensions = extension::get_extensions(config_manager.default_config(), Box::new(clipboard::get_manager())); let extensions = extension::get_extensions(
config_manager.default_config(),
Box::new(clipboard::get_manager()),
);
let renderer = let renderer =
render::default::DefaultRenderer::new(extensions, config_manager.default_config().clone()); render::default::DefaultRenderer::new(extensions, config_manager.default_config().clone());

View File

@ -75,7 +75,8 @@ impl<'de> serde::Deserialize<'de> for Match {
impl<'a> From<&'a AutoMatch> for Match { impl<'a> From<&'a AutoMatch> for Match {
fn from(other: &'a AutoMatch) -> Self { fn from(other: &'a AutoMatch) -> Self {
lazy_static! { lazy_static! {
static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(\\w+)(\\.\\w+)?\\s*\\}\\}").unwrap(); static ref VAR_REGEX: Regex =
Regex::new("\\{\\{\\s*(\\w+)(\\.\\w+)?\\s*\\}\\}").unwrap();
}; };
let mut triggers = if !other.triggers.is_empty() { let mut triggers = if !other.triggers.is_empty() {
@ -145,14 +146,15 @@ impl<'a> From<&'a AutoMatch> for Match {
}; };
MatchContentType::Text(content) MatchContentType::Text(content)
} else if let Some(form) = &other.form { // Form shorthand } else if let Some(form) = &other.form {
// Form shorthand
// Replace all the form fields with actual variables // Replace all the form fields with actual variables
let new_replace = VAR_REGEX.replace_all(&form, |caps: &Captures| { let new_replace = VAR_REGEX.replace_all(&form, |caps: &Captures| {
let var_name = caps.get(1).unwrap().as_str(); let var_name = caps.get(1).unwrap().as_str();
format!("{{{{form1.{}}}}}", var_name) format!("{{{{form1.{}}}}}", var_name)
}); });
let new_replace = new_replace.to_string(); let new_replace = new_replace.to_string();
// Convert the form data to valid variables // Convert the form data to valid variables
let mut params = Mapping::new(); let mut params = Mapping::new();
if let Some(fields) = &other.form_fields { if let Some(fields) = &other.form_fields {
@ -163,14 +165,12 @@ impl<'a> From<&'a AutoMatch> for Match {
params.insert(Value::from("fields"), Value::from(mapping_fields)); params.insert(Value::from("fields"), Value::from(mapping_fields));
} }
params.insert(Value::from("layout"), Value::from(form.to_owned())); params.insert(Value::from("layout"), Value::from(form.to_owned()));
let vars = vec![ let vars = vec![MatchVariable {
MatchVariable { name: "form1".to_owned(),
name: "form1".to_owned(), var_type: "form".to_owned(),
var_type: "form".to_owned(), params,
params, }];
}
];
let content = TextContent { let content = TextContent {
replace: new_replace, replace: new_replace,
@ -241,7 +241,7 @@ struct AutoMatch {
#[serde(default = "default_form")] #[serde(default = "default_form")]
pub form: Option<String>, pub form: Option<String>,
#[serde(default = "default_form_fields")] #[serde(default = "default_form_fields")]
pub form_fields: Option<HashMap<String, Value>>, pub form_fields: Option<HashMap<String, Value>>,
@ -603,20 +603,24 @@ mod tests {
match _match.content { match _match.content {
MatchContentType::Text(content) => { MatchContentType::Text(content) => {
let mut mapping = Mapping::new(); let mut mapping = Mapping::new();
mapping.insert(Value::from("layout"), Value::from("Hey {{name}}, how are you? {{greet}}")); mapping.insert(
assert_eq!(content, TextContent { Value::from("layout"),
replace: "Hey {{form1.name}}, how are you? {{form1.greet}}".to_owned(), Value::from("Hey {{name}}, how are you? {{greet}}"),
_has_vars: true, );
vars: vec![ assert_eq!(
MatchVariable { content,
TextContent {
replace: "Hey {{form1.name}}, how are you? {{form1.greet}}".to_owned(),
_has_vars: true,
vars: vec![MatchVariable {
name: "form1".to_owned(), name: "form1".to_owned(),
var_type: "form".to_owned(), var_type: "form".to_owned(),
params: mapping, params: mapping,
} }]
] }
}); );
}, }
_ => panic!("wrong content") _ => panic!("wrong content"),
} }
} }
@ -639,20 +643,24 @@ mod tests {
submapping.insert(Value::from("name"), Value::from(name_mapping)); submapping.insert(Value::from("name"), Value::from(name_mapping));
let mut mapping = Mapping::new(); let mut mapping = Mapping::new();
mapping.insert(Value::from("fields"), Value::from(submapping)); mapping.insert(Value::from("fields"), Value::from(submapping));
mapping.insert(Value::from("layout"), Value::from("Hey {{name}}, how are you? {{greet}}")); mapping.insert(
assert_eq!(content, TextContent { Value::from("layout"),
replace: "Hey {{form1.name}}, how are you? {{form1.greet}}".to_owned(), Value::from("Hey {{name}}, how are you? {{greet}}"),
_has_vars: true, );
vars: vec![ assert_eq!(
MatchVariable { content,
TextContent {
replace: "Hey {{form1.name}}, how are you? {{form1.greet}}".to_owned(),
_has_vars: true,
vars: vec![MatchVariable {
name: "form1".to_owned(), name: "form1".to_owned(),
var_type: "form".to_owned(), var_type: "form".to_owned(),
params: mapping, params: mapping,
} }]
] }
}); );
}, }
_ => panic!("wrong content") _ => panic!("wrong content"),
} }
} }
} }

View File

@ -106,7 +106,7 @@ impl<'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMat
.word_separators .word_separators
.contains(&c.chars().nth(0).unwrap_or_default()); .contains(&c.chars().nth(0).unwrap_or_default());
let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut(); let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut();
(*was_previous_char_a_match) = false; (*was_previous_char_a_match) = false;
let mut was_previous_word_separator = self.was_previous_char_word_separator.borrow_mut(); let mut was_previous_word_separator = self.was_previous_char_word_separator.borrow_mut();
@ -212,8 +212,7 @@ impl<'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMat
self.receiver self.receiver
.on_match(mtc, trailing_separator, entry.trigger_offset); .on_match(mtc, trailing_separator, entry.trigger_offset);
(*was_previous_char_a_match) = true; (*was_previous_char_a_match) = true;
} }
} }
@ -221,7 +220,7 @@ impl<'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMat
fn handle_modifier(&self, m: KeyModifier) { fn handle_modifier(&self, m: KeyModifier) {
let config = self.config_manager.default_config(); let config = self.config_manager.default_config();
let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut(); let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut();
// TODO: at the moment, activating the passive key triggers the toggle key // TODO: at the moment, activating the passive key triggers the toggle key
// study a mechanism to avoid this problem // study a mechanism to avoid this problem
@ -280,7 +279,7 @@ impl<'a, R: MatchReceiver, M: ConfigManager<'a>> super::Matcher for ScrollingMat
*was_previous_char_word_separator = true; *was_previous_char_word_separator = true;
// Disable the "backspace undo" feature // Disable the "backspace undo" feature
let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut(); let mut was_previous_char_a_match = self.was_previous_char_a_match.borrow_mut();
(*was_previous_char_a_match) = false; (*was_previous_char_a_match) = false;
} }
} }

View File

@ -27,7 +27,8 @@ use serde_yaml::Value;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
lazy_static! { lazy_static! {
static ref VAR_REGEX: Regex = Regex::new(r"\{\{\s*((?P<name>\w+)(\.(?P<subname>(\w+)))?)\s*\}\}").unwrap(); static ref VAR_REGEX: Regex =
Regex::new(r"\{\{\s*((?P<name>\w+)(\.(?P<subname>(\w+)))?)\s*\}\}").unwrap();
static ref UNKNOWN_VARIABLE: String = "".to_string(); static ref UNKNOWN_VARIABLE: String = "".to_string();
} }
@ -95,9 +96,8 @@ impl super::Renderer for DefaultRenderer {
target_vars.insert(var_name.to_owned()); target_vars.insert(var_name.to_owned());
} }
let match_variables: HashSet<&String> = content.vars.iter().map(|var| { let match_variables: HashSet<&String> =
&var.name content.vars.iter().map(|var| &var.name).collect();
}).collect();
// Find the global variables that are not specified in the var list // Find the global variables that are not specified in the var list
let mut missing_globals = Vec::new(); let mut missing_globals = Vec::new();
@ -106,7 +106,7 @@ impl super::Renderer for DefaultRenderer {
if target_vars.contains(&global_var.name) { if target_vars.contains(&global_var.name) {
if match_variables.contains(&global_var.name) { if match_variables.contains(&global_var.name) {
specified_globals.insert(global_var.name.clone(), &global_var); specified_globals.insert(global_var.name.clone(), &global_var);
}else { } else {
missing_globals.push(global_var); missing_globals.push(global_var);
} }
} }
@ -120,14 +120,18 @@ impl super::Renderer for DefaultRenderer {
variables.extend(&content.vars); variables.extend(&content.vars);
// Replace variable type "global" with the actual reference // Replace variable type "global" with the actual reference
let variables: Vec<&MatchVariable> = variables.into_iter().map(|variable| { let variables: Vec<&MatchVariable> = variables
if variable.var_type == "global" { .into_iter()
if let Some(actual_variable) = specified_globals.get(&variable.name) { .map(|variable| {
return actual_variable.clone(); if variable.var_type == "global" {
if let Some(actual_variable) = specified_globals.get(&variable.name)
{
return actual_variable.clone();
}
} }
} variable
variable })
}).collect(); .collect();
let mut output_map: HashMap<String, ExtensionResult> = HashMap::new(); let mut output_map: HashMap<String, ExtensionResult> = HashMap::new();
@ -178,11 +182,15 @@ impl super::Renderer for DefaultRenderer {
// Normal extension variables // Normal extension variables
let extension = self.extension_map.get(&variable.var_type); let extension = self.extension_map.get(&variable.var_type);
if let Some(extension) = extension { if let Some(extension) = extension {
let ext_out = extension.calculate(&variable.params, &args, &output_map); let ext_out =
extension.calculate(&variable.params, &args, &output_map);
if let Some(output) = ext_out { if let Some(output) = ext_out {
output_map.insert(variable.name.clone(), output); output_map.insert(variable.name.clone(), output);
} else { } else {
output_map.insert(variable.name.clone(), ExtensionResult::Single("".to_owned())); output_map.insert(
variable.name.clone(),
ExtensionResult::Single("".to_owned()),
);
warn!( warn!(
"Could not generate output for variable: {}", "Could not generate output for variable: {}",
variable.name variable.name
@ -202,28 +210,23 @@ impl super::Renderer for DefaultRenderer {
let var_name = caps.name("name").unwrap().as_str(); let var_name = caps.name("name").unwrap().as_str();
let var_subname = caps.name("subname"); let var_subname = caps.name("subname");
match output_map.get(var_name) { match output_map.get(var_name) {
Some(result) => { Some(result) => match result {
match result { ExtensionResult::Single(output) => output,
ExtensionResult::Single(output) => { ExtensionResult::Multiple(results) => match var_subname {
output Some(var_subname) => {
}, let var_subname = var_subname.as_str();
ExtensionResult::Multiple(results) => { results.get(var_subname).unwrap_or(&UNKNOWN_VARIABLE)
match var_subname { }
Some(var_subname) => { None => {
let var_subname = var_subname.as_str(); error!(
results.get(var_subname).unwrap_or(&UNKNOWN_VARIABLE) "nested name missing from multi-value variable: {}",
}, var_name
None => { );
error!("nested name missing from multi-value variable: {}", var_name); &UNKNOWN_VARIABLE
&UNKNOWN_VARIABLE }
}, },
}
},
}
},
None => {
&UNKNOWN_VARIABLE
}, },
None => &UNKNOWN_VARIABLE,
} }
}); });
@ -793,8 +796,6 @@ mod tests {
verify_render(rendered, "RESULT"); verify_render(rendered, "RESULT");
} }
#[test] #[test]
fn test_render_variable_order() { fn test_render_variable_order() {
let config = get_config_for( let config = get_config_for(

View File

@ -0,0 +1 @@

View File

@ -1,7 +1,7 @@
use crate::config::Configs; use crate::config::Configs;
use std::process::{Command, Child, Output};
use log::{error, info}; use log::{error, info};
use std::io::{Error, Write}; use std::io::{Error, Write};
use std::process::{Child, Command, Output};
pub mod form; pub mod form;
@ -15,9 +15,10 @@ impl ModuloManager {
// Check if the `MODULO_PATH` env variable is configured // Check if the `MODULO_PATH` env variable is configured
if let Some(_modulo_path) = std::env::var_os("MODULO_PATH") { if let Some(_modulo_path) = std::env::var_os("MODULO_PATH") {
modulo_path = Some(_modulo_path.to_string_lossy().to_string()) modulo_path = Some(_modulo_path.to_string_lossy().to_string())
} else if let Some(ref _modulo_path) = config.modulo_path { // Check the configs } else if let Some(ref _modulo_path) = config.modulo_path {
// Check the configs
modulo_path = Some(_modulo_path.to_owned()); modulo_path = Some(_modulo_path.to_owned());
}else{ } else {
// Check in the same directory of espanso // Check in the same directory of espanso
if let Ok(exe_path) = std::env::current_exe() { if let Ok(exe_path) = std::env::current_exe() {
if let Some(parent) = exe_path.parent() { if let Some(parent) = exe_path.parent() {
@ -46,9 +47,7 @@ impl ModuloManager {
info!("Using modulo at {:?}", modulo_path); info!("Using modulo at {:?}", modulo_path);
} }
Self { Self { modulo_path }
modulo_path,
}
} }
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
@ -97,26 +96,26 @@ impl ModuloManager {
} }
return Some(output.to_string()); return Some(output.to_string());
}, }
Err(error) => { Err(error) => {
error!("error while getting output from modulo: {}", error); error!("error while getting output from modulo: {}", error);
}, }
} }
}, }
Err(error) => { Err(error) => {
error!("error while sending body to modulo"); error!("error while sending body to modulo");
}, }
} }
}else{ } else {
error!("unable to open stdin to modulo"); error!("unable to open stdin to modulo");
} }
}, }
Err(error) => { Err(error) => {
error!("error reported when invoking modulo: {}", error); error!("error reported when invoking modulo: {}", error);
}, }
} }
} }
None None
} }
} }