feat(core): progress on the core pipeline
This commit is contained in:
parent
14fdfe4149
commit
4af4a434a3
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -296,6 +296,7 @@ dependencies = [
|
||||||
"espanso-inject",
|
"espanso-inject",
|
||||||
"espanso-match",
|
"espanso-match",
|
||||||
"espanso-path",
|
"espanso-path",
|
||||||
|
"espanso-render",
|
||||||
"espanso-ui",
|
"espanso-ui",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
@ -324,9 +325,11 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dunce",
|
"dunce",
|
||||||
|
"enum-as-inner",
|
||||||
"glob",
|
"glob",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"ordered-float",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
@ -666,6 +669,15 @@ dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
|
|
|
@ -21,6 +21,7 @@ espanso-config = { path = "../espanso-config" }
|
||||||
espanso-match = { path = "../espanso-match" }
|
espanso-match = { path = "../espanso-match" }
|
||||||
espanso-clipboard = { path = "../espanso-clipboard" }
|
espanso-clipboard = { path = "../espanso-clipboard" }
|
||||||
espanso-info = { path = "../espanso-info" }
|
espanso-info = { path = "../espanso-info" }
|
||||||
|
espanso-render = { path = "../espanso-render" }
|
||||||
espanso-path = { path = "../espanso-path" }
|
espanso-path = { path = "../espanso-path" }
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
simplelog = "0.9.0"
|
simplelog = "0.9.0"
|
||||||
|
|
|
@ -17,7 +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 std::collections::HashSet;
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::engine::process::MatchFilter;
|
use crate::engine::process::MatchFilter;
|
||||||
use espanso_config::{
|
use espanso_config::{
|
||||||
|
@ -27,6 +30,8 @@ use espanso_config::{
|
||||||
use espanso_info::{AppInfo, AppInfoProvider};
|
use espanso_info::{AppInfo, AppInfoProvider};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
|
use super::engine::render::ConfigProvider;
|
||||||
|
|
||||||
pub struct ConfigManager<'a> {
|
pub struct ConfigManager<'a> {
|
||||||
config_store: &'a dyn ConfigStore,
|
config_store: &'a dyn ConfigStore,
|
||||||
match_store: &'a dyn MatchStore,
|
match_store: &'a dyn MatchStore,
|
||||||
|
@ -46,29 +51,16 @@ impl<'a> ConfigManager<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active(&self) -> &'a dyn Config {
|
pub fn active(&self) -> &'a dyn Config {
|
||||||
let current_app = self.app_info_provider.get_info();
|
let current_app = self.app_info_provider.get_info();
|
||||||
let info = to_app_properties(¤t_app);
|
let info = to_app_properties(¤t_app);
|
||||||
self.config_store.active(&info)
|
self.config_store.active(&info)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_match_set(&self) -> MatchSet {
|
pub fn active_context(&self) -> (&'a dyn Config, MatchSet) {
|
||||||
let match_paths = self.active().match_paths();
|
let config = self.active();
|
||||||
self.match_store.query(&match_paths)
|
let match_paths = config.match_paths();
|
||||||
}
|
(config, self.match_store.query(&match_paths))
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> MatchFilter for ConfigManager<'a> {
|
|
||||||
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> {
|
|
||||||
let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied());
|
|
||||||
let match_set = self.active_match_set();
|
|
||||||
|
|
||||||
match_set
|
|
||||||
.matches
|
|
||||||
.iter()
|
|
||||||
.filter(|m| ids_set.contains(&m.id))
|
|
||||||
.map(|m| m.id)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,3 +72,29 @@ fn to_app_properties(info: &AppInfo) -> AppProperties {
|
||||||
exec: info.exec.as_deref(),
|
exec: info.exec.as_deref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> MatchFilter for ConfigManager<'a> {
|
||||||
|
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> {
|
||||||
|
let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied());
|
||||||
|
let (_, match_set) = self.active_context();
|
||||||
|
|
||||||
|
match_set
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.filter(|m| ids_set.contains(&m.id))
|
||||||
|
.map(|m| m.id)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ConfigProvider<'a> for ConfigManager<'a> {
|
||||||
|
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)> {
|
||||||
|
self.config_store.configs().into_iter().map(|config| {
|
||||||
|
(config, self.match_store.query(config.match_paths()))
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active(&self) -> (&'a dyn Config, MatchSet) {
|
||||||
|
self.active_context()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
58
espanso/src/cli/worker/engine/match_cache.rs
Normal file
58
espanso/src/cli/worker/engine/match_cache.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{collections::HashMap, iter::FromIterator};
|
||||||
|
|
||||||
|
use espanso_config::{
|
||||||
|
config::ConfigStore,
|
||||||
|
matches::{store::MatchStore, Match},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{multiplex::MatchProvider, render::MatchIterator};
|
||||||
|
|
||||||
|
pub struct MatchCache<'a> {
|
||||||
|
cache: HashMap<i32, &'a Match>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MatchCache<'a> {
|
||||||
|
pub fn load(config_store: &'a dyn ConfigStore, match_store: &'a dyn MatchStore) -> Self {
|
||||||
|
let mut cache = HashMap::new();
|
||||||
|
|
||||||
|
let paths = config_store.get_all_match_paths();
|
||||||
|
let global_set = match_store.query(&Vec::from_iter(paths.into_iter()));
|
||||||
|
|
||||||
|
for m in global_set.matches {
|
||||||
|
cache.insert(m.id, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { cache }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MatchProvider<'a> for MatchCache<'a> {
|
||||||
|
fn get(&self, match_id: i32) -> Option<&'a Match> {
|
||||||
|
self.cache.get(&match_id).map(|m| *m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MatchIterator<'a> for MatchCache<'a> {
|
||||||
|
fn matches(&self) -> Vec<&'a Match> {
|
||||||
|
self.cache.iter().map(|(_, m)| *m).collect()
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,7 +81,7 @@ impl From<espanso_match::MatchResult<i32>> for MatchResult {
|
||||||
Self {
|
Self {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
trigger: result.trigger,
|
trigger: result.trigger,
|
||||||
vars: result.vars,
|
args: result.vars,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
26
espanso/src/cli/worker/engine/mod.rs
Normal file
26
espanso/src/cli/worker/engine/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 mod ui;
|
||||||
|
pub mod source;
|
||||||
|
pub mod render;
|
||||||
|
pub mod matcher;
|
||||||
|
pub mod executor;
|
||||||
|
pub mod multiplex;
|
||||||
|
pub mod match_cache;
|
57
espanso/src/cli/worker/engine/multiplex.rs
Normal file
57
espanso/src/cli/worker/engine/multiplex.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use espanso_config::matches::{Match, MatchEffect};
|
||||||
|
|
||||||
|
use crate::engine::{
|
||||||
|
event::{render::RenderingRequestedEvent, Event},
|
||||||
|
process::Multiplexer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait MatchProvider<'a> {
|
||||||
|
fn get(&self, match_id: i32) -> Option<&'a Match>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MultiplexAdapter<'a> {
|
||||||
|
provider: &'a dyn MatchProvider<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MultiplexAdapter<'a> {
|
||||||
|
pub fn new(provider: &'a dyn MatchProvider<'a>) -> Self {
|
||||||
|
Self { provider }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Multiplexer for MultiplexAdapter<'a> {
|
||||||
|
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event> {
|
||||||
|
let m = self.provider.get(match_id)?;
|
||||||
|
|
||||||
|
match &m.effect {
|
||||||
|
MatchEffect::Text(_) => Some(Event::RenderingRequested(RenderingRequestedEvent {
|
||||||
|
match_id,
|
||||||
|
trigger,
|
||||||
|
trigger_args,
|
||||||
|
})),
|
||||||
|
// TODO: think about rich text and image
|
||||||
|
MatchEffect::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
228
espanso/src/cli/worker/engine/render.rs
Normal file
228
espanso/src/cli/worker/engine/render.rs
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{cell::RefCell, collections::HashMap, convert::TryInto};
|
||||||
|
|
||||||
|
use espanso_config::{
|
||||||
|
config::Config,
|
||||||
|
matches::{store::MatchSet, Match, MatchCause, MatchEffect},
|
||||||
|
};
|
||||||
|
use espanso_render::{CasingStyle, Context, RenderOptions, Template, Value, Variable};
|
||||||
|
|
||||||
|
use crate::{cli::worker::config::ConfigManager, engine::process::{Renderer, RendererError}};
|
||||||
|
|
||||||
|
pub trait MatchIterator<'a> {
|
||||||
|
fn matches(&self) -> Vec<&'a Match>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ConfigProvider<'a> {
|
||||||
|
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)>;
|
||||||
|
fn active(&self) -> (&'a dyn Config, MatchSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RendererAdapter<'a> {
|
||||||
|
renderer: &'a dyn espanso_render::Renderer,
|
||||||
|
config_provider: &'a dyn ConfigProvider<'a>,
|
||||||
|
|
||||||
|
template_map: HashMap<i32, Option<Template>>,
|
||||||
|
global_vars_map: HashMap<i32, Variable>,
|
||||||
|
|
||||||
|
context_cache: RefCell<HashMap<i32, Context<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RendererAdapter<'a> {
|
||||||
|
pub fn new(
|
||||||
|
match_iterator: &'a dyn MatchIterator,
|
||||||
|
config_provider: &'a dyn ConfigProvider<'a>,
|
||||||
|
renderer: &'a dyn espanso_render::Renderer,
|
||||||
|
) -> Self {
|
||||||
|
let template_map = generate_template_map(match_iterator);
|
||||||
|
let global_vars_map = generate_global_vars_map(config_provider);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
renderer,
|
||||||
|
config_provider,
|
||||||
|
template_map,
|
||||||
|
global_vars_map,
|
||||||
|
context_cache: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
fn generate_template_map(match_iterator: &dyn MatchIterator) -> HashMap<i32, Option<Template>> {
|
||||||
|
let mut template_map = HashMap::new();
|
||||||
|
for m in match_iterator.matches() {
|
||||||
|
let entry = convert_to_template(m);
|
||||||
|
template_map.insert(m.id, entry);
|
||||||
|
}
|
||||||
|
template_map
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
fn generate_global_vars_map(config_provider: &dyn ConfigProvider) -> HashMap<i32, Variable> {
|
||||||
|
let mut global_vars_map = HashMap::new();
|
||||||
|
|
||||||
|
for (_, match_set) in config_provider.configs() {
|
||||||
|
for var in match_set.global_vars.iter() {
|
||||||
|
if !global_vars_map.contains_key(&var.id) {
|
||||||
|
global_vars_map.insert(var.id, convert_var((*var).clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_vars_map
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
fn generate_context<'a>(
|
||||||
|
match_set: &MatchSet,
|
||||||
|
template_map: &'a HashMap<i32, Option<Template>>,
|
||||||
|
global_vars_map: &'a HashMap<i32, Variable>,
|
||||||
|
) -> Context<'a> {
|
||||||
|
let mut templates = Vec::new();
|
||||||
|
let mut global_vars = Vec::new();
|
||||||
|
|
||||||
|
for m in match_set.matches.iter() {
|
||||||
|
if let Some(Some(template)) = template_map.get(&m.id) {
|
||||||
|
templates.push(template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for var in match_set.global_vars.iter() {
|
||||||
|
if let Some(var) = global_vars_map.get(&var.id) {
|
||||||
|
global_vars.push(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context {
|
||||||
|
templates,
|
||||||
|
global_vars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move conversion methods to new file?
|
||||||
|
|
||||||
|
fn convert_to_template(m: &Match) -> Option<Template> {
|
||||||
|
if let MatchEffect::Text(text_effect) = &m.effect {
|
||||||
|
let ids = if let MatchCause::Trigger(cause) = &m.cause {
|
||||||
|
cause.triggers.clone()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Template {
|
||||||
|
ids,
|
||||||
|
body: text_effect.replace.clone(),
|
||||||
|
vars: convert_vars(text_effect.vars.clone()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_vars(vars: Vec<espanso_config::matches::Variable>) -> Vec<espanso_render::Variable> {
|
||||||
|
vars.into_iter().map(convert_var).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_var(var: espanso_config::matches::Variable) -> espanso_render::Variable {
|
||||||
|
Variable {
|
||||||
|
name: var.name,
|
||||||
|
var_type: var.var_type,
|
||||||
|
params: convert_params(var.params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_params(params: espanso_config::matches::Params) -> espanso_render::Params {
|
||||||
|
let mut new_params = espanso_render::Params::new();
|
||||||
|
for (key, value) in params {
|
||||||
|
new_params.insert(key, convert_value(value));
|
||||||
|
}
|
||||||
|
new_params
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_value(value: espanso_config::matches::Value) -> espanso_render::Value {
|
||||||
|
match value {
|
||||||
|
espanso_config::matches::Value::Null => espanso_render::Value::Null,
|
||||||
|
espanso_config::matches::Value::Bool(v) => espanso_render::Value::Bool(v),
|
||||||
|
espanso_config::matches::Value::Number(n) => match n {
|
||||||
|
espanso_config::matches::Number::Integer(i) => {
|
||||||
|
espanso_render::Value::Number(espanso_render::Number::Integer(i))
|
||||||
|
}
|
||||||
|
espanso_config::matches::Number::Float(f) => {
|
||||||
|
espanso_render::Value::Number(espanso_render::Number::Float(f.into_inner()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
espanso_config::matches::Value::String(s) => espanso_render::Value::String(s),
|
||||||
|
espanso_config::matches::Value::Array(v) => {
|
||||||
|
espanso_render::Value::Array(v.into_iter().map(convert_value).collect())
|
||||||
|
}
|
||||||
|
espanso_config::matches::Value::Object(params) => {
|
||||||
|
espanso_render::Value::Object(convert_params(params))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Renderer<'a> for RendererAdapter<'a> {
|
||||||
|
fn render(&'a self, match_id: i32, trigger_vars: HashMap<String, String>) -> anyhow::Result<String> {
|
||||||
|
if let Some(Some(template)) = self.template_map.get(&match_id) {
|
||||||
|
let (config, match_set) = self.config_provider.active();
|
||||||
|
|
||||||
|
let mut context_cache = self.context_cache.borrow_mut();
|
||||||
|
let context = context_cache.entry(config.id()).or_insert(generate_context(&match_set, &self.template_map, &self.global_vars_map));
|
||||||
|
|
||||||
|
// TODO: calculate the casing style instead of hardcoding it
|
||||||
|
let options = RenderOptions {
|
||||||
|
casing_style: CasingStyle::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If some trigger vars are specified, augment the template with them
|
||||||
|
let augmented_template = if !trigger_vars.is_empty() {
|
||||||
|
let mut augmented = template.clone();
|
||||||
|
for (name, value) in trigger_vars {
|
||||||
|
let mut params = espanso_render::Params::new();
|
||||||
|
params.insert("echo".to_string(), Value::String(value));
|
||||||
|
augmented.vars.push(Variable {
|
||||||
|
name,
|
||||||
|
var_type: "echo".to_string(),
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(augmented)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let template = if let Some(augmented) = augmented_template.as_ref() {
|
||||||
|
augmented
|
||||||
|
} else {
|
||||||
|
template
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.renderer.render(template, context, &options) {
|
||||||
|
espanso_render::RenderResult::Success(body) => Ok(body),
|
||||||
|
espanso_render::RenderResult::Aborted => Err(RendererError::Aborted.into()),
|
||||||
|
espanso_render::RenderResult::Error(err) => Err(RendererError::RenderingError(err).into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(RendererError::NotFound.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,16 +19,13 @@
|
||||||
|
|
||||||
use funnel::Source;
|
use funnel::Source;
|
||||||
use process::Matcher;
|
use process::Matcher;
|
||||||
use ui::selector::MatchSelectorAdapter;
|
use engine::ui::selector::MatchSelectorAdapter;
|
||||||
|
|
||||||
use crate::engine::{Engine, funnel, process, dispatch};
|
use crate::engine::{Engine, funnel, process, dispatch};
|
||||||
use super::{CliModule, CliModuleArgs};
|
use super::{CliModule, CliModuleArgs};
|
||||||
|
|
||||||
mod ui;
|
mod engine;
|
||||||
mod config;
|
mod config;
|
||||||
mod source;
|
|
||||||
mod matcher;
|
|
||||||
mod executor;
|
|
||||||
|
|
||||||
pub fn new() -> CliModule {
|
pub fn new() -> CliModule {
|
||||||
#[allow(clippy::needless_update)]
|
#[allow(clippy::needless_update)]
|
||||||
|
@ -48,21 +45,27 @@ fn worker_main(args: CliModuleArgs) {
|
||||||
|
|
||||||
let app_info_provider = espanso_info::get_provider().expect("unable to initialize app info provider");
|
let app_info_provider = espanso_info::get_provider().expect("unable to initialize app info provider");
|
||||||
let config_manager = config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider);
|
let config_manager = config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider);
|
||||||
let match_converter = matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
let match_converter = engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
||||||
|
let match_cache = engine::match_cache::MatchCache::load(&*config_store, &*match_store);
|
||||||
|
|
||||||
let detect_source = source::detect::init_and_spawn().expect("failed to initialize detector module");
|
let detect_source = engine::source::detect::init_and_spawn().expect("failed to initialize detector module");
|
||||||
let sources: Vec<&dyn Source> = vec![&detect_source];
|
let sources: Vec<&dyn Source> = vec![&detect_source];
|
||||||
let funnel = funnel::default(&sources);
|
let funnel = funnel::default(&sources);
|
||||||
|
|
||||||
let matcher = matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
let matcher = engine::matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
||||||
let matchers: Vec<&dyn Matcher<matcher::MatcherState>> = vec![&matcher];
|
let matchers: Vec<&dyn Matcher<engine::matcher::MatcherState>> = vec![&matcher];
|
||||||
let selector = MatchSelectorAdapter::new();
|
let selector = MatchSelectorAdapter::new();
|
||||||
let mut processor = process::default(&matchers, &config_manager, &selector);
|
let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache);
|
||||||
|
|
||||||
let text_injector = executor::text_injector::TextInjectorAdapter::new();
|
// TODO: add extensions
|
||||||
|
let renderer = espanso_render::create(Vec::new());
|
||||||
|
let renderer_adapter = engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
||||||
|
|
||||||
|
let mut processor = process::default(&matchers, &config_manager, &selector, &multiplexer, &renderer_adapter);
|
||||||
|
|
||||||
|
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new();
|
||||||
let dispatcher = dispatch::default(&text_injector);
|
let dispatcher = dispatch::default(&text_injector);
|
||||||
|
|
||||||
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||||
engine.run();
|
engine.run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ impl<'a> TextInjectExecutor<'a> {
|
||||||
impl<'a> Executor for TextInjectExecutor<'a> {
|
impl<'a> Executor for TextInjectExecutor<'a> {
|
||||||
fn execute(&self, event: &Event) -> bool {
|
fn execute(&self, event: &Event) -> bool {
|
||||||
if let Event::TextInject(inject_event) = event {
|
if let Event::TextInject(inject_event) = event {
|
||||||
if inject_event.mode == TextInjectMode::Keys {
|
if let Some(TextInjectMode::Keys) = inject_event.force_mode {
|
||||||
if let Err(error) = self.injector.inject(&inject_event.text) {
|
if let Err(error) = self.injector.inject(&inject_event.text) {
|
||||||
error!("text injector reported an error: {:?}", error);
|
error!("text injector reported an error: {:?}", error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextInjectEvent {
|
pub struct TextInjectRequest {
|
||||||
pub delete_count: i32,
|
pub delete_count: i32,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub mode: TextInjectMode,
|
pub force_mode: Option<TextInjectMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct MatchesDetectedEvent {
|
||||||
pub struct DetectedMatch {
|
pub struct DetectedMatch {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub trigger: String,
|
pub trigger: String,
|
||||||
pub vars: HashMap<String, String>,
|
pub args: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
@ -20,10 +20,12 @@
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod inject;
|
pub mod inject;
|
||||||
pub mod matches;
|
pub mod matches;
|
||||||
|
pub mod render;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
NOOP,
|
NOOP,
|
||||||
|
ProcessingError(String),
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
Keyboard(keyboard::KeyboardEvent),
|
Keyboard(keyboard::KeyboardEvent),
|
||||||
|
@ -32,6 +34,9 @@ pub enum Event {
|
||||||
MatchesDetected(matches::MatchesDetectedEvent),
|
MatchesDetected(matches::MatchesDetectedEvent),
|
||||||
MatchSelected(matches::MatchSelectedEvent),
|
MatchSelected(matches::MatchSelectedEvent),
|
||||||
|
|
||||||
|
RenderingRequested(render::RenderingRequestedEvent),
|
||||||
|
Rendered(render::RenderedEvent),
|
||||||
|
|
||||||
// Effects
|
// Effects
|
||||||
TextInject(inject::TextInjectEvent),
|
TextInject(inject::TextInjectRequest),
|
||||||
}
|
}
|
33
espanso/src/engine/event/render.rs
Normal file
33
espanso/src/engine/event/render.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct RenderingRequestedEvent {
|
||||||
|
pub match_id: i32,
|
||||||
|
pub trigger: String,
|
||||||
|
pub trigger_args: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct RenderedEvent {
|
||||||
|
pub trigger: String,
|
||||||
|
pub body: String,
|
||||||
|
}
|
|
@ -19,7 +19,13 @@
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use super::{Event, MatchFilter, MatchSelector, Matcher, Middleware, Processor, middleware::match_select::MatchSelectMiddleware, middleware::matcher::MatchMiddleware};
|
use super::{
|
||||||
|
middleware::{
|
||||||
|
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
||||||
|
render::RenderMiddleware,
|
||||||
|
},
|
||||||
|
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
|
||||||
|
};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
pub struct DefaultProcessor<'a> {
|
pub struct DefaultProcessor<'a> {
|
||||||
|
@ -32,12 +38,16 @@ impl<'a> DefaultProcessor<'a> {
|
||||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||||
match_filter: &'a dyn MatchFilter,
|
match_filter: &'a dyn MatchFilter,
|
||||||
match_selector: &'a dyn MatchSelector,
|
match_selector: &'a dyn MatchSelector,
|
||||||
|
multiplexer: &'a dyn Multiplexer,
|
||||||
|
renderer: &'a dyn Renderer<'a>,
|
||||||
) -> DefaultProcessor<'a> {
|
) -> DefaultProcessor<'a> {
|
||||||
Self {
|
Self {
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
middleware: vec![
|
middleware: vec![
|
||||||
Box::new(MatchMiddleware::new(matchers)),
|
Box::new(MatchMiddleware::new(matchers)),
|
||||||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||||
|
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||||
|
Box::new(RenderMiddleware::new(renderer)),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl<'a, State> Middleware for MatchMiddleware<'a, State> {
|
||||||
DetectedMatch {
|
DetectedMatch {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
trigger: result.trigger,
|
trigger: result.trigger,
|
||||||
vars: result.vars,
|
args: result.args,
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,5 +17,7 @@
|
||||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
pub mod render;
|
||||||
pub mod matcher;
|
pub mod matcher;
|
||||||
|
pub mod multiplex;
|
||||||
pub mod match_select;
|
pub mod match_select;
|
51
espanso/src/engine/process/middleware/multiplex.rs
Normal file
51
espanso/src/engine/process/middleware/multiplex.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use log::{debug, error};
|
||||||
|
|
||||||
|
use super::super::Middleware;
|
||||||
|
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||||
|
|
||||||
|
pub struct MultiplexMiddleware<'a> {
|
||||||
|
multiplexer: &'a dyn Multiplexer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MultiplexMiddleware<'a> {
|
||||||
|
pub fn new(multiplexer: &'a dyn Multiplexer) -> Self {
|
||||||
|
Self { multiplexer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Middleware for MultiplexMiddleware<'a> {
|
||||||
|
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||||
|
if let Event::MatchSelected(m_event) = event {
|
||||||
|
return match self.multiplexer.convert(m_event.chosen.id, m_event.chosen.trigger, m_event.chosen.args) {
|
||||||
|
Some(event) => event,
|
||||||
|
None => {
|
||||||
|
error!("match multiplexing failed");
|
||||||
|
Event::NOOP
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
63
espanso/src/engine/process/middleware/render.rs
Normal file
63
espanso/src/engine/process/middleware/render.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use log::{debug, error};
|
||||||
|
|
||||||
|
use super::super::Middleware;
|
||||||
|
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent, render::RenderedEvent}, process::{MatchFilter, MatchSelector, Renderer, RendererError}};
|
||||||
|
|
||||||
|
pub struct RenderMiddleware<'a> {
|
||||||
|
renderer: &'a dyn Renderer<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RenderMiddleware<'a> {
|
||||||
|
pub fn new(renderer: &'a dyn Renderer<'a>) -> Self {
|
||||||
|
Self { renderer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Middleware for RenderMiddleware<'a> {
|
||||||
|
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||||
|
if let Event::RenderingRequested(m_event) = event {
|
||||||
|
match self.renderer.render(m_event.match_id, m_event.trigger_args) {
|
||||||
|
Ok(body) => {
|
||||||
|
return Event::Rendered(RenderedEvent {
|
||||||
|
trigger: m_event.trigger,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
match err.downcast_ref::<RendererError>() {
|
||||||
|
Some(RendererError::Aborted) => {
|
||||||
|
return Event::NOOP
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("error during rendering: {}", err);
|
||||||
|
return Event::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
|
@ -17,9 +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 std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{event::keyboard::Key, Event};
|
use super::{event::keyboard::Key, Event};
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
mod default;
|
mod default;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
|
@ -52,7 +53,7 @@ pub enum MatcherEvent {
|
||||||
pub struct MatchResult {
|
pub struct MatchResult {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub trigger: String,
|
pub trigger: String,
|
||||||
pub vars: HashMap<String, String>,
|
pub args: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MatchFilter {
|
pub trait MatchFilter {
|
||||||
|
@ -63,10 +64,32 @@ pub trait MatchSelector {
|
||||||
fn select(&self, matches_ids: &[i32]) -> Option<i32>;
|
fn select(&self, matches_ids: &[i32]) -> Option<i32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Multiplexer {
|
||||||
|
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Renderer<'a> {
|
||||||
|
fn render(&'a self, match_id: i32, trigger_args: HashMap<String, String>) -> Result<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum RendererError {
|
||||||
|
#[error("rendering error")]
|
||||||
|
RenderingError(#[from] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("match not found")]
|
||||||
|
NotFound,
|
||||||
|
|
||||||
|
#[error("aborted")]
|
||||||
|
Aborted,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn default<'a, MatcherState>(
|
pub fn default<'a, MatcherState>(
|
||||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||||
match_filter: &'a dyn MatchFilter,
|
match_filter: &'a dyn MatchFilter,
|
||||||
match_selector: &'a dyn MatchSelector,
|
match_selector: &'a dyn MatchSelector,
|
||||||
|
multiplexer: &'a dyn Multiplexer,
|
||||||
|
renderer: &'a dyn Renderer<'a>,
|
||||||
) -> impl Processor + 'a {
|
) -> impl Processor + 'a {
|
||||||
default::DefaultProcessor::new(matchers, match_filter, match_selector)
|
default::DefaultProcessor::new(matchers, match_filter, match_selector, multiplexer, renderer)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user