feat(core): introduce basic injection backend switch logic
This commit is contained in:
parent
e532a377b1
commit
3a51efda2c
|
@ -22,7 +22,7 @@ use std::{
|
|||
collections::{HashMap, HashSet},
|
||||
};
|
||||
|
||||
use crate::engine::process::MatchFilter;
|
||||
use crate::engine::{dispatch::ModeProvider, process::MatchFilter};
|
||||
use espanso_config::{
|
||||
config::{AppProperties, Config, ConfigStore},
|
||||
matches::store::{MatchSet, MatchStore},
|
||||
|
@ -89,12 +89,24 @@ impl<'a> MatchFilter for ConfigManager<'a> {
|
|||
|
||||
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()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ModeProvider for ConfigManager<'a> {
|
||||
fn active_mode(&self) -> crate::engine::dispatch::Mode {
|
||||
// TODO: implement the actual active mode detection starting from the active config
|
||||
crate::engine::dispatch::Mode::Auto {
|
||||
clipboard_threshold: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
56
espanso/src/cli/worker/engine/executor/clipboard_injector.rs
Normal file
56
espanso/src/cli/worker/engine/executor/clipboard_injector.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 espanso_inject::{Injector, keys::Key};
|
||||
use espanso_clipboard::Clipboard;
|
||||
|
||||
use crate::engine::{dispatch::TextInjector};
|
||||
|
||||
pub struct ClipboardInjectorAdapter<'a> {
|
||||
injector: &'a dyn Injector,
|
||||
clipboard: &'a dyn Clipboard,
|
||||
}
|
||||
|
||||
impl <'a> ClipboardInjectorAdapter<'a> {
|
||||
pub fn new(injector: &'a dyn Injector, clipboard: &'a dyn Clipboard) -> Self {
|
||||
Self {
|
||||
injector,
|
||||
clipboard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> TextInjector for ClipboardInjectorAdapter<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"clipboard"
|
||||
}
|
||||
|
||||
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
||||
// TODO: handle clipboard restoration
|
||||
self.clipboard.set_text(text)?;
|
||||
|
||||
// TODO: handle delay duration
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
||||
// TODO: handle options
|
||||
self.injector.send_key_combination(&[Key::Control, Key::V], Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -21,11 +21,11 @@ use espanso_inject::Injector;
|
|||
|
||||
use crate::engine::dispatch::TextInjector;
|
||||
|
||||
pub struct TextInjectorAdapter<'a> {
|
||||
pub struct EventInjectorAdapter<'a> {
|
||||
injector: &'a dyn Injector,
|
||||
}
|
||||
|
||||
impl <'a> TextInjectorAdapter<'a> {
|
||||
impl <'a> EventInjectorAdapter<'a> {
|
||||
pub fn new(injector: &'a dyn Injector) -> Self {
|
||||
Self {
|
||||
injector
|
||||
|
@ -33,7 +33,11 @@ impl <'a> TextInjectorAdapter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <'a> TextInjector for TextInjectorAdapter<'a> {
|
||||
impl <'a> TextInjector for EventInjectorAdapter<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"event"
|
||||
}
|
||||
|
||||
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
||||
// TODO: handle injection options
|
||||
self.injector.send_string(text, Default::default())
|
|
@ -17,5 +17,6 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod text_injector;
|
||||
pub mod clipboard_injector;
|
||||
pub mod event_injector;
|
||||
pub mod key_injector;
|
|
@ -19,10 +19,9 @@
|
|||
|
||||
use std::{collections::HashMap, iter::FromIterator};
|
||||
|
||||
use espanso_config::{
|
||||
config::ConfigStore,
|
||||
matches::{store::MatchStore, Match},
|
||||
};
|
||||
use espanso_config::{config::ConfigStore, matches::{Match, MatchEffect, store::MatchStore}};
|
||||
|
||||
use crate::engine::process::MatchInfoProvider;
|
||||
|
||||
use super::{multiplex::MatchProvider, render::MatchIterator};
|
||||
|
||||
|
@ -56,3 +55,14 @@ impl<'a> MatchIterator<'a> for MatchCache<'a> {
|
|||
self.cache.iter().map(|(_, m)| *m).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MatchInfoProvider for MatchCache<'a> {
|
||||
fn get_force_mode(&self, match_id: i32) -> Option<crate::engine::event::text::TextInjectMode> {
|
||||
let m = self.cache.get(&match_id)?;
|
||||
if let MatchEffect::Text(text_effect) = &m.effect {
|
||||
// TODO: read match effect and convert it to the actual injection mode
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use engine::ui::selector::MatchSelectorAdapter;
|
||||
use funnel::Source;
|
||||
use process::Matcher;
|
||||
use engine::ui::selector::MatchSelectorAdapter;
|
||||
|
||||
use crate::engine::{Engine, funnel, process, dispatch};
|
||||
use super::{CliModule, CliModuleArgs};
|
||||
use crate::engine::{dispatch, funnel, process, Engine};
|
||||
|
||||
mod engine;
|
||||
mod config;
|
||||
mod engine;
|
||||
|
||||
pub fn new() -> CliModule {
|
||||
#[allow(clippy::needless_update)]
|
||||
|
@ -40,35 +40,62 @@ pub fn new() -> CliModule {
|
|||
}
|
||||
|
||||
fn worker_main(args: CliModuleArgs) {
|
||||
let config_store = args.config_store.expect("missing config store in worker main");
|
||||
let match_store = args.match_store.expect("missing match store in worker main");
|
||||
let config_store = args
|
||||
.config_store
|
||||
.expect("missing config store in worker main");
|
||||
let match_store = args
|
||||
.match_store
|
||||
.expect("missing match store in worker main");
|
||||
|
||||
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 match_converter = engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
||||
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 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 = engine::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 funnel = funnel::default(&sources);
|
||||
|
||||
let matcher = engine::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<engine::matcher::MatcherState>> = vec![&matcher];
|
||||
let selector = MatchSelectorAdapter::new();
|
||||
let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache);
|
||||
|
||||
// TODO: add extensions
|
||||
let renderer = espanso_render::create(Vec::new());
|
||||
let renderer_adapter = engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
||||
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 mut processor = process::default(
|
||||
&matchers,
|
||||
&config_manager,
|
||||
&selector,
|
||||
&multiplexer,
|
||||
&renderer_adapter,
|
||||
&match_cache,
|
||||
);
|
||||
|
||||
let injector = espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
|
||||
let injector =
|
||||
espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
|
||||
let clipboard = espanso_clipboard::get_clipboard(Default::default())
|
||||
.expect("failed to initialize clipboard module"); // TODO: handle options
|
||||
|
||||
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new(&*injector);
|
||||
let event_injector = engine::executor::event_injector::EventInjectorAdapter::new(&*injector);
|
||||
let clipboard_injector =
|
||||
engine::executor::clipboard_injector::ClipboardInjectorAdapter::new(&*injector, &*clipboard);
|
||||
let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector);
|
||||
let dispatcher = dispatch::default(&text_injector, &key_injector);
|
||||
let dispatcher = dispatch::default(
|
||||
&event_injector,
|
||||
&clipboard_injector,
|
||||
&config_manager,
|
||||
&key_injector,
|
||||
);
|
||||
|
||||
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||
engine.run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,29 +17,40 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::{Dispatcher, Executor, KeyInjector, TextInjector};
|
||||
use super::Event;
|
||||
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector};
|
||||
|
||||
pub struct DefaultDispatcher<'a> {
|
||||
executors: Vec<Box<dyn Executor + 'a>>,
|
||||
}
|
||||
|
||||
impl <'a> DefaultDispatcher<'a> {
|
||||
pub fn new(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> Self {
|
||||
impl<'a> DefaultDispatcher<'a> {
|
||||
pub fn new(
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
key_injector: &'a dyn KeyInjector,
|
||||
) -> Self {
|
||||
Self {
|
||||
executors: vec![
|
||||
Box::new(super::executor::text_inject::TextInjectExecutor::new(text_injector)),
|
||||
Box::new(super::executor::key_inject::KeyInjectExecutor::new(key_injector)),
|
||||
]
|
||||
Box::new(super::executor::text_inject::TextInjectExecutor::new(
|
||||
event_injector,
|
||||
clipboard_injector,
|
||||
mode_provider,
|
||||
)),
|
||||
Box::new(super::executor::key_inject::KeyInjectExecutor::new(
|
||||
key_injector,
|
||||
)),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> Dispatcher for DefaultDispatcher<'a> {
|
||||
impl<'a> Dispatcher for DefaultDispatcher<'a> {
|
||||
fn dispatch(&self, event: Event) {
|
||||
for executor in self.executors.iter() {
|
||||
if executor.execute(&event) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,31 +17,92 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::super::{Event, Executor, TextInjector};
|
||||
use anyhow::Result;
|
||||
use super::super::{Event, Executor};
|
||||
use crate::engine::event::text::TextInjectMode;
|
||||
use log::error;
|
||||
use log::{error, trace};
|
||||
|
||||
pub trait TextInjector {
|
||||
fn name(&self) -> &'static str;
|
||||
fn inject_text(&self, text: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait ModeProvider {
|
||||
fn active_mode(&self) -> Mode;
|
||||
}
|
||||
|
||||
pub enum Mode {
|
||||
Event,
|
||||
Clipboard,
|
||||
Auto {
|
||||
// Maximum size after which the clipboard backend
|
||||
// is used over the event one to speed up the injection.
|
||||
clipboard_threshold: usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextInjectExecutor<'a> {
|
||||
injector: &'a dyn TextInjector,
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
}
|
||||
|
||||
impl<'a> TextInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn TextInjector) -> Self {
|
||||
Self { injector }
|
||||
pub fn new(
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
) -> Self {
|
||||
Self {
|
||||
event_injector,
|
||||
clipboard_injector,
|
||||
mode_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for TextInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let Event::TextInject(inject_event) = event {
|
||||
if let Some(TextInjectMode::Keys) = inject_event.force_mode {
|
||||
if let Err(error) = self.injector.inject_text(&inject_event.text) {
|
||||
error!("text injector reported an error: {:?}", error);
|
||||
let active_mode = self.mode_provider.active_mode();
|
||||
|
||||
let injector = if let Some(force_mode) = &inject_event.force_mode {
|
||||
if let TextInjectMode::Keys = force_mode {
|
||||
self.event_injector
|
||||
} else {
|
||||
self.clipboard_injector
|
||||
}
|
||||
return true;
|
||||
} else if let Mode::Clipboard = active_mode {
|
||||
self.clipboard_injector
|
||||
} else if let Mode::Event = active_mode {
|
||||
self.event_injector
|
||||
} else if let Mode::Auto { clipboard_threshold } = active_mode {
|
||||
if inject_event.text.chars().count() > clipboard_threshold {
|
||||
self.clipboard_injector
|
||||
} else if cfg!(target_os = "linux") {
|
||||
if inject_event.text.chars().all(|c| c.is_ascii()) {
|
||||
self.event_injector
|
||||
} else {
|
||||
self.clipboard_injector
|
||||
}
|
||||
} else {
|
||||
self.event_injector
|
||||
}
|
||||
} else {
|
||||
self.event_injector
|
||||
};
|
||||
|
||||
trace!("using injector: {}", injector.name());
|
||||
|
||||
if let Err(error) = injector.inject_text(&inject_event.text) {
|
||||
error!("text injector ({}) reported an error: {:?}", injector.name(), error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use super::{Event, event::keyboard::Key};
|
||||
|
||||
mod executor;
|
||||
use super::{event::keyboard::Key, Event};
|
||||
|
||||
mod default;
|
||||
mod executor;
|
||||
|
||||
pub trait Executor {
|
||||
fn execute(&self, event: &Event) -> bool;
|
||||
|
@ -31,17 +32,19 @@ pub trait Dispatcher {
|
|||
fn dispatch(&self, event: Event);
|
||||
}
|
||||
|
||||
pub trait TextInjector {
|
||||
fn inject_text(&self, text: &str) -> Result<()>;
|
||||
}
|
||||
// Re-export dependency injection entities
|
||||
pub use executor::text_inject::{ModeProvider, Mode, TextInjector};
|
||||
|
||||
// TODO: move into module
|
||||
pub trait KeyInjector {
|
||||
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub fn default<'a>(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> impl Dispatcher + 'a {
|
||||
default::DefaultDispatcher::new(
|
||||
text_injector,
|
||||
key_injector,
|
||||
)
|
||||
}
|
||||
pub fn default<'a>(
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
key_injector: &'a dyn KeyInjector,
|
||||
) -> impl Dispatcher + 'a {
|
||||
default::DefaultDispatcher::new(event_injector, clipboard_injector, mode_provider, key_injector)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ pub struct RenderingRequestedEvent {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderedEvent {
|
||||
pub match_id: i32,
|
||||
|
||||
pub trigger: String,
|
||||
pub body: String,
|
||||
|
||||
|
|
|
@ -19,13 +19,10 @@
|
|||
|
||||
use log::trace;
|
||||
|
||||
use super::{
|
||||
middleware::{
|
||||
use super::{Event, MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{
|
||||
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
||||
render::RenderMiddleware, action::ActionMiddleware,
|
||||
},
|
||||
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
|
||||
};
|
||||
}};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct DefaultProcessor<'a> {
|
||||
|
@ -40,6 +37,7 @@ impl<'a> DefaultProcessor<'a> {
|
|||
match_selector: &'a dyn MatchSelector,
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
match_info_provider: &'a dyn MatchInfoProvider,
|
||||
) -> DefaultProcessor<'a> {
|
||||
Self {
|
||||
event_queue: VecDeque::new(),
|
||||
|
@ -48,7 +46,7 @@ impl<'a> DefaultProcessor<'a> {
|
|||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||
Box::new(RenderMiddleware::new(renderer)),
|
||||
Box::new(ActionMiddleware::new()),
|
||||
Box::new(ActionMiddleware::new(match_info_provider)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,39 +18,57 @@
|
|||
*/
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::engine::{event::{Event, keyboard::{Key, KeySequenceInjectRequest}, text::{TextInjectMode, TextInjectRequest}}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||
use crate::engine::{
|
||||
dispatch::Mode,
|
||||
event::{
|
||||
keyboard::{Key, KeySequenceInjectRequest},
|
||||
text::{TextInjectMode, TextInjectRequest},
|
||||
Event,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ActionMiddleware {
|
||||
pub trait MatchInfoProvider {
|
||||
fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
|
||||
}
|
||||
|
||||
impl ActionMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
pub struct ActionMiddleware<'a> {
|
||||
match_info_provider: &'a dyn MatchInfoProvider,
|
||||
}
|
||||
|
||||
impl<'a> ActionMiddleware<'a> {
|
||||
pub fn new(match_info_provider: &'a dyn MatchInfoProvider) -> Self {
|
||||
Self {
|
||||
match_info_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for ActionMiddleware {
|
||||
impl<'a> Middleware for ActionMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"action"
|
||||
}
|
||||
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
if let Event::Rendered(m_event) = &event {
|
||||
dispatch(Event::TextInject(TextInjectRequest {
|
||||
text: m_event.body.clone(),
|
||||
force_mode: Some(TextInjectMode::Keys), // TODO: determine this one dynamically
|
||||
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).map(|_| Key::ArrowLeft).collect(),
|
||||
keys: (0..cursor_hint_back_count)
|
||||
.map(|_| Key::ArrowLeft)
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
// This is executed before the dispatched event
|
||||
return Event::KeySequenceInject(KeySequenceInjectRequest {
|
||||
keys: (0..m_event.trigger.chars().count()).map(|_| Key::Backspace).collect()
|
||||
})
|
||||
keys: (0..m_event.trigger.chars().count())
|
||||
.map(|_| Key::Backspace)
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: handle images
|
||||
|
|
|
@ -50,6 +50,7 @@ impl<'a> Middleware for RenderMiddleware<'a> {
|
|||
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,
|
||||
|
|
|
@ -36,6 +36,8 @@ pub trait Processor {
|
|||
|
||||
// Dependency inversion entities
|
||||
|
||||
// TODO: move these traits inside the various modules and then re-export it
|
||||
|
||||
pub trait Matcher<'a, State> {
|
||||
fn process(
|
||||
&'a self,
|
||||
|
@ -66,7 +68,12 @@ pub trait MatchSelector {
|
|||
}
|
||||
|
||||
pub trait Multiplexer {
|
||||
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event>;
|
||||
fn convert(
|
||||
&self,
|
||||
match_id: i32,
|
||||
trigger: String,
|
||||
trigger_args: HashMap<String, String>,
|
||||
) -> Option<Event>;
|
||||
}
|
||||
|
||||
pub trait Renderer<'a> {
|
||||
|
@ -85,12 +92,22 @@ pub enum RendererError {
|
|||
Aborted,
|
||||
}
|
||||
|
||||
pub use middleware::action::MatchInfoProvider;
|
||||
|
||||
pub fn default<'a, MatcherState>(
|
||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||
match_filter: &'a dyn MatchFilter,
|
||||
match_selector: &'a dyn MatchSelector,
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
match_info_provider: &'a dyn MatchInfoProvider,
|
||||
) -> impl Processor + 'a {
|
||||
default::DefaultProcessor::new(matchers, match_filter, match_selector, multiplexer, renderer)
|
||||
default::DefaultProcessor::new(
|
||||
matchers,
|
||||
match_filter,
|
||||
match_selector,
|
||||
multiplexer,
|
||||
renderer,
|
||||
match_info_provider,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user