refactor(core): restructure event pipeline to account for match cause compensation
This commit is contained in:
parent
799474a0fc
commit
976e653fc8
|
@ -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<String, String>) -> Option<Event> {
|
||||
fn convert(&self, match_id: i32, trigger: Option<String>, trigger_args: HashMap<String, String>) -> Option<Event> {
|
||||
let m = self.provider.get(match_id)?;
|
||||
|
||||
match &m.effect {
|
||||
|
|
28
espanso/src/engine/event/effect.rs
Normal file
28
espanso/src/engine/event/effect.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TriggerCompensationEvent {
|
||||
pub trigger: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CursorHintCompensationEvent {
|
||||
pub cursor_hint_back_count: usize,
|
||||
}
|
|
@ -17,19 +17,19 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[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<String>,
|
||||
|
@ -37,7 +37,7 @@ pub struct KeyboardEvent {
|
|||
pub variant: Option<Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeySequenceInjectRequest {
|
||||
pub keys: Vec<Key>,
|
||||
}
|
||||
|
|
|
@ -27,11 +27,16 @@ pub struct MatchesDetectedEvent {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DetectedMatch {
|
||||
pub id: i32,
|
||||
pub trigger: String,
|
||||
pub trigger: Option<String>,
|
||||
pub args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MatchSelectedEvent {
|
||||
pub chosen: DetectedMatch,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CauseCompensatedMatchEvent {
|
||||
pub m: DetectedMatch,
|
||||
}
|
|
@ -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),
|
||||
}
|
|
@ -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<String>,
|
||||
pub trigger_args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderedEvent {
|
||||
pub match_id: i32,
|
||||
|
||||
pub trigger: String,
|
||||
pub body: String,
|
||||
|
||||
pub cursor_hint_back_count: Option<usize>,
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TextInjectRequest {
|
||||
pub text: String,
|
||||
pub force_mode: Option<TextInjectMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TextInjectMode {
|
||||
Keys,
|
||||
Clipboard,
|
||||
|
|
|
@ -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)),
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
66
espanso/src/engine/process/middleware/cause.rs
Normal file
66
espanso/src/engine/process/middleware/cause.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
77
espanso/src/engine/process/middleware/cursor_hint.rs
Normal file
77
espanso/src/engine/process/middleware/cursor_hint.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<usize>) {
|
||||
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
|
|
@ -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<VecDeque<Vec<State>>>,
|
||||
}
|
||||
|
||||
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(),
|
||||
|
|
|
@ -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;
|
||||
pub mod render;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<usize>) {
|
||||
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
|
||||
|
|
|
@ -71,7 +71,7 @@ pub trait Multiplexer {
|
|||
fn convert(
|
||||
&self,
|
||||
match_id: i32,
|
||||
trigger: String,
|
||||
trigger: Option<String>,
|
||||
trigger_args: HashMap<String, String>,
|
||||
) -> Option<Event>;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user