diff --git a/espanso/src/cli/worker/engine/multiplex.rs b/espanso/src/cli/worker/engine/multiplex.rs index 5b61ad4..6961d72 100644 --- a/espanso/src/cli/worker/engine/multiplex.rs +++ b/espanso/src/cli/worker/engine/multiplex.rs @@ -41,7 +41,7 @@ impl<'a> MultiplexAdapter<'a> { } impl<'a> Multiplexer for MultiplexAdapter<'a> { - fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap) -> Option { + fn convert(&self, match_id: i32, trigger: Option, trigger_args: HashMap) -> Option { let m = self.provider.get(match_id)?; match &m.effect { diff --git a/espanso/src/engine/event/effect.rs b/espanso/src/engine/event/effect.rs new file mode 100644 index 0000000..8c8c7a6 --- /dev/null +++ b/espanso/src/engine/event/effect.rs @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +#[derive(Debug, Clone, PartialEq)] +pub struct TriggerCompensationEvent { + pub trigger: String, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CursorHintCompensationEvent { + pub cursor_hint_back_count: usize, +} \ No newline at end of file diff --git a/espanso/src/engine/event/keyboard.rs b/espanso/src/engine/event/keyboard.rs index da2d819..8791afa 100644 --- a/espanso/src/engine/event/keyboard.rs +++ b/espanso/src/engine/event/keyboard.rs @@ -17,19 +17,19 @@ * along with espanso. If not, see . */ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Status { Pressed, Released, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Variant { Left, Right, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct KeyboardEvent { pub key: Key, pub value: Option, @@ -37,7 +37,7 @@ pub struct KeyboardEvent { pub variant: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct KeySequenceInjectRequest { pub keys: Vec, } diff --git a/espanso/src/engine/event/matches.rs b/espanso/src/engine/event/matches.rs index 6ca617a..6c68fd8 100644 --- a/espanso/src/engine/event/matches.rs +++ b/espanso/src/engine/event/matches.rs @@ -27,11 +27,16 @@ pub struct MatchesDetectedEvent { #[derive(Debug, Clone, PartialEq)] pub struct DetectedMatch { pub id: i32, - pub trigger: String, + pub trigger: Option, pub args: HashMap, } #[derive(Debug, Clone, PartialEq)] pub struct MatchSelectedEvent { pub chosen: DetectedMatch, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CauseCompensatedMatchEvent { + pub m: DetectedMatch, } \ No newline at end of file diff --git a/espanso/src/engine/event/mod.rs b/espanso/src/engine/event/mod.rs index 3bfc01c..4d9e698 100644 --- a/espanso/src/engine/event/mod.rs +++ b/espanso/src/engine/event/mod.rs @@ -21,23 +21,31 @@ pub mod keyboard; pub mod text; pub mod matches; pub mod render; +pub mod effect; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Event { NOOP, - ProcessingError(String), + ProcessingError(String), // TODO: create dedicated event // Inputs + // TODO: Move to the input mode Keyboard(keyboard::KeyboardEvent), // Internal + // TODO: move to the "internal" mode (maybe, change name?) MatchesDetected(matches::MatchesDetectedEvent), MatchSelected(matches::MatchSelectedEvent), + CauseCompensatedMatch(matches::CauseCompensatedMatchEvent), RenderingRequested(render::RenderingRequestedEvent), Rendered(render::RenderedEvent), // Effects + TriggerCompensation(effect::TriggerCompensationEvent), + CursorHintCompensation(effect::CursorHintCompensationEvent), + + // TODO: move to the "effect" mod KeySequenceInject(keyboard::KeySequenceInjectRequest), TextInject(text::TextInjectRequest), } \ No newline at end of file diff --git a/espanso/src/engine/event/render.rs b/espanso/src/engine/event/render.rs index 2771583..23e2573 100644 --- a/espanso/src/engine/event/render.rs +++ b/espanso/src/engine/event/render.rs @@ -22,16 +22,12 @@ use std::collections::HashMap; #[derive(Debug, Clone, PartialEq)] pub struct RenderingRequestedEvent { pub match_id: i32, - pub trigger: String, + pub trigger: Option, pub trigger_args: HashMap, } #[derive(Debug, Clone, PartialEq)] pub struct RenderedEvent { pub match_id: i32, - - pub trigger: String, pub body: String, - - pub cursor_hint_back_count: Option, } diff --git a/espanso/src/engine/event/text.rs b/espanso/src/engine/event/text.rs index 79db95c..139e2de 100644 --- a/espanso/src/engine/event/text.rs +++ b/espanso/src/engine/event/text.rs @@ -17,13 +17,13 @@ * along with espanso. If not, see . */ -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TextInjectRequest { pub text: String, pub force_mode: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum TextInjectMode { Keys, Clipboard, diff --git a/espanso/src/engine/process/default.rs b/espanso/src/engine/process/default.rs index dec91ea..52bf18a 100644 --- a/espanso/src/engine/process/default.rs +++ b/espanso/src/engine/process/default.rs @@ -20,8 +20,8 @@ use log::trace; use super::{Event, MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{ - match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware, - render::RenderMiddleware, action::ActionMiddleware, + match_select::MatchSelectMiddleware, matcher::MatcherMiddleware, multiplex::MultiplexMiddleware, + render::RenderMiddleware, action::ActionMiddleware, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware, }}; use std::collections::VecDeque; @@ -42,10 +42,12 @@ impl<'a> DefaultProcessor<'a> { Self { event_queue: VecDeque::new(), middleware: vec![ - Box::new(MatchMiddleware::new(matchers)), + Box::new(MatcherMiddleware::new(matchers)), Box::new(MatchSelectMiddleware::new(match_filter, match_selector)), + Box::new(CauseCompensateMiddleware::new()), Box::new(MultiplexMiddleware::new(multiplexer)), Box::new(RenderMiddleware::new(renderer)), + Box::new(CursorHintMiddleware::new()), Box::new(ActionMiddleware::new(match_info_provider)), ], } diff --git a/espanso/src/engine/process/middleware/action.rs b/espanso/src/engine/process/middleware/action.rs index 1b3e26d..e6ef859 100644 --- a/espanso/src/engine/process/middleware/action.rs +++ b/espanso/src/engine/process/middleware/action.rs @@ -49,31 +49,27 @@ impl<'a> Middleware for ActionMiddleware<'a> { } fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { - if let Event::Rendered(m_event) = &event { - dispatch(Event::TextInject(TextInjectRequest { + match &event { + Event::Rendered(m_event) => Event::TextInject(TextInjectRequest { text: m_event.body.clone(), force_mode: self.match_info_provider.get_force_mode(m_event.match_id), - })); - - if let Some(cursor_hint_back_count) = m_event.cursor_hint_back_count { - dispatch(Event::KeySequenceInject(KeySequenceInjectRequest { - keys: (0..cursor_hint_back_count) + }), + Event::CursorHintCompensation(m_event) => { + Event::KeySequenceInject(KeySequenceInjectRequest { + keys: (0..m_event.cursor_hint_back_count) .map(|_| Key::ArrowLeft) .collect(), - })) + }) } - - // This is executed before the dispatched event - return Event::KeySequenceInject(KeySequenceInjectRequest { + Event::TriggerCompensation(m_event) => Event::KeySequenceInject(KeySequenceInjectRequest { keys: (0..m_event.trigger.chars().count()) .map(|_| Key::Backspace) .collect(), - }); + }), + _ => event, } // TODO: handle images - - event } } diff --git a/espanso/src/engine/process/middleware/cause.rs b/espanso/src/engine/process/middleware/cause.rs new file mode 100644 index 0000000..cf65731 --- /dev/null +++ b/espanso/src/engine/process/middleware/cause.rs @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +use super::super::Middleware; +use crate::engine::{ + dispatch::Mode, + event::{ + effect::TriggerCompensationEvent, + keyboard::{Key, KeySequenceInjectRequest}, + matches::CauseCompensatedMatchEvent, + text::{TextInjectMode, TextInjectRequest}, + Event, + }, +}; + +pub struct CauseCompensateMiddleware {} + +impl CauseCompensateMiddleware { + pub fn new() -> Self { + Self {} + } +} + +impl Middleware for CauseCompensateMiddleware { + fn name(&self) -> &'static str { + "cause_compensate" + } + + fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { + if let Event::MatchSelected(m_event) = &event { + let compensated_event = + Event::CauseCompensatedMatch(CauseCompensatedMatchEvent { m: m_event.chosen.clone() }); + + if let Some(trigger) = &m_event.chosen.trigger { + dispatch(compensated_event); + + // Before the event, place a trigger compensation + return Event::TriggerCompensation(TriggerCompensationEvent { + trigger: trigger.clone(), + }); + } else { + return compensated_event; + } + } + + event + } +} + +// TODO: test diff --git a/espanso/src/engine/process/middleware/cursor_hint.rs b/espanso/src/engine/process/middleware/cursor_hint.rs new file mode 100644 index 0000000..f1f1758 --- /dev/null +++ b/espanso/src/engine/process/middleware/cursor_hint.rs @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +use super::super::Middleware; +use crate::engine::{dispatch::Mode, event::{Event, effect::CursorHintCompensationEvent, keyboard::{Key, KeySequenceInjectRequest}, render::RenderedEvent, text::{TextInjectMode, TextInjectRequest}}}; + +pub struct CursorHintMiddleware {} + +impl CursorHintMiddleware { + pub fn new() -> Self { + Self {} + } +} + +impl Middleware for CursorHintMiddleware { + fn name(&self) -> &'static str { + "cursor_hint" + } + + fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event { + if let Event::Rendered(m_event) = event { + let (body, cursor_hint_back_count) = process_cursor_hint(m_event.body); + + if let Some(cursor_hint_back_count) = cursor_hint_back_count { + dispatch(Event::CursorHintCompensation(CursorHintCompensationEvent { + cursor_hint_back_count, + })) + } + + // Alter the rendered event to remove the cursor hint from the body + return Event::Rendered(RenderedEvent { + body, + ..m_event + }) + } + + event + } +} + +// TODO: test +fn process_cursor_hint(body: String) -> (String, Option) { + if let Some(index) = body.find("$|$") { + // Convert the byte index to a char index + let char_str = &body[0..index]; + let char_index = char_str.chars().count(); + let total_size = body.chars().count(); + + // Remove the $|$ placeholder + let body = body.replace("$|$", ""); + + // Calculate the amount of rewind moves needed (LEFT ARROW). + // Subtract also 3, equal to the number of chars of the placeholder "$|$" + let moves = total_size - char_index - 3; + (body, Some(moves)) + } else { + (body, None) + } +} + +// TODO: test diff --git a/espanso/src/engine/process/middleware/matcher.rs b/espanso/src/engine/process/middleware/matcher.rs index abcea53..0923754 100644 --- a/espanso/src/engine/process/middleware/matcher.rs +++ b/espanso/src/engine/process/middleware/matcher.rs @@ -32,13 +32,13 @@ use crate::engine::{ const MAX_HISTORY: usize = 3; // TODO: get as parameter -pub struct MatchMiddleware<'a, State> { +pub struct MatcherMiddleware<'a, State> { matchers: &'a [&'a dyn Matcher<'a, State>], matcher_states: RefCell>>, } -impl<'a, State> MatchMiddleware<'a, State> { +impl<'a, State> MatcherMiddleware<'a, State> { pub fn new(matchers: &'a [&'a dyn Matcher<'a, State>]) -> Self { Self { matchers, @@ -47,9 +47,9 @@ impl<'a, State> MatchMiddleware<'a, State> { } } -impl<'a, State> Middleware for MatchMiddleware<'a, State> { +impl<'a, State> Middleware for MatcherMiddleware<'a, State> { fn name(&self) -> &'static str { - "match" + "matcher" } fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event { @@ -104,7 +104,7 @@ impl<'a, State> Middleware for MatchMiddleware<'a, State> { .into_iter() .map(|result| DetectedMatch { id: result.id, - trigger: result.trigger, + trigger: Some(result.trigger), args: result.args, }) .collect(), diff --git a/espanso/src/engine/process/middleware/mod.rs b/espanso/src/engine/process/middleware/mod.rs index 082bb0f..91d530f 100644 --- a/espanso/src/engine/process/middleware/mod.rs +++ b/espanso/src/engine/process/middleware/mod.rs @@ -18,7 +18,9 @@ */ pub mod action; -pub mod render; +pub mod cause; +pub mod cursor_hint; +pub mod match_select; pub mod matcher; pub mod multiplex; -pub mod match_select; \ No newline at end of file +pub mod render; diff --git a/espanso/src/engine/process/middleware/multiplex.rs b/espanso/src/engine/process/middleware/multiplex.rs index 649566c..ff9d9cc 100644 --- a/espanso/src/engine/process/middleware/multiplex.rs +++ b/espanso/src/engine/process/middleware/multiplex.rs @@ -38,8 +38,8 @@ impl<'a> Middleware for MultiplexMiddleware<'a> { } fn next(&self, event: Event, _: &mut 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) { + if let Event::CauseCompensatedMatch(m_event) = event { + return match self.multiplexer.convert(m_event.m.id, m_event.m.trigger, m_event.m.args) { Some(event) => event, None => { error!("match multiplexing failed"); diff --git a/espanso/src/engine/process/middleware/render.rs b/espanso/src/engine/process/middleware/render.rs index 9dba523..ccbb889 100644 --- a/espanso/src/engine/process/middleware/render.rs +++ b/espanso/src/engine/process/middleware/render.rs @@ -17,11 +17,12 @@ * along with espanso. If not, see . */ -use log::{error}; +use log::error; use super::super::Middleware; use crate::engine::{ event::{ + effect::{CursorHintCompensationEvent, TriggerCompensationEvent}, render::RenderedEvent, Event, }, @@ -47,13 +48,9 @@ impl<'a> Middleware for RenderMiddleware<'a> { if let Event::RenderingRequested(m_event) = event { match self.renderer.render(m_event.match_id, m_event.trigger_args) { Ok(body) => { - let (body, cursor_hint_back_count) = process_cursor_hint(body); - return Event::Rendered(RenderedEvent { match_id: m_event.match_id, - trigger: m_event.trigger, body, - cursor_hint_back_count, }); } Err(err) => { @@ -73,23 +70,3 @@ impl<'a> Middleware for RenderMiddleware<'a> { } // TODO: test -fn process_cursor_hint(body: String) -> (String, Option) { - if let Some(index) = body.find("$|$") { - // Convert the byte index to a char index - let char_str = &body[0..index]; - let char_index = char_str.chars().count(); - let total_size = body.chars().count(); - - // Remove the $|$ placeholder - let body = body.replace("$|$", ""); - - // Calculate the amount of rewind moves needed (LEFT ARROW). - // Subtract also 3, equal to the number of chars of the placeholder "$|$" - let moves = total_size - char_index - 3; - (body, Some(moves)) - } else { - (body, None) - } -} - -// TODO: test diff --git a/espanso/src/engine/process/mod.rs b/espanso/src/engine/process/mod.rs index b8fa285..e4937d3 100644 --- a/espanso/src/engine/process/mod.rs +++ b/espanso/src/engine/process/mod.rs @@ -71,7 +71,7 @@ pub trait Multiplexer { fn convert( &self, match_id: i32, - trigger: String, + trigger: Option, trigger_args: HashMap, ) -> Option; }