feat(engine): move engine into separate module
This commit is contained in:
parent
de8879e03c
commit
974e405b23
17
espanso-engine/Cargo.toml
Normal file
17
espanso-engine/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "espanso-engine"
|
||||
version = "0.1.0"
|
||||
authors = ["Federico Terzi <federico-terzi@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
anyhow = "1.0.38"
|
||||
thiserror = "1.0.23"
|
||||
crossbeam = "0.8.0"
|
||||
markdown = "0.3.0"
|
||||
html2text = "0.2.1"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.7"
|
78
espanso-engine/src/dispatch/default.rs
Normal file
78
espanso-engine/src/dispatch/default.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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::{ContextMenuHandler, Event, IconHandler, ImageInjector, SecureInputManager};
|
||||
use super::{Dispatcher, Executor, HtmlInjector, KeyInjector, ModeProvider, TextInjector};
|
||||
|
||||
pub struct DefaultDispatcher<'a> {
|
||||
executors: Vec<Box<dyn Executor + 'a>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
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,
|
||||
html_injector: &'a dyn HtmlInjector,
|
||||
image_injector: &'a dyn ImageInjector,
|
||||
context_menu_handler: &'a dyn ContextMenuHandler,
|
||||
icon_handler: &'a dyn IconHandler,
|
||||
secure_input_manager: &'a dyn SecureInputManager,
|
||||
) -> Self {
|
||||
Self {
|
||||
executors: vec![
|
||||
Box::new(super::executor::text_inject::TextInjectExecutor::new(
|
||||
event_injector,
|
||||
clipboard_injector,
|
||||
mode_provider,
|
||||
)),
|
||||
Box::new(super::executor::key_inject::KeyInjectExecutor::new(
|
||||
key_injector,
|
||||
)),
|
||||
Box::new(super::executor::html_inject::HtmlInjectExecutor::new(
|
||||
html_injector,
|
||||
)),
|
||||
Box::new(super::executor::image_inject::ImageInjectExecutor::new(
|
||||
image_injector,
|
||||
)),
|
||||
Box::new(super::executor::context_menu::ContextMenuExecutor::new(
|
||||
context_menu_handler,
|
||||
)),
|
||||
Box::new(super::executor::icon_update::IconUpdateExecutor::new(
|
||||
icon_handler,
|
||||
)),
|
||||
Box::new(super::executor::secure_input::SecureInputExecutor::new(
|
||||
secure_input_manager,
|
||||
)),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dispatcher for DefaultDispatcher<'a> {
|
||||
fn dispatch(&self, event: Event) {
|
||||
for executor in self.executors.iter() {
|
||||
if executor.execute(&event) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
espanso-engine/src/dispatch/executor/context_menu.rs
Normal file
53
espanso-engine/src/dispatch/executor/context_menu.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 crate::event::{ui::MenuItem, EventType};
|
||||
use crate::{dispatch::Executor, event::Event};
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
pub trait ContextMenuHandler {
|
||||
fn show_context_menu(&self, items: &[MenuItem]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct ContextMenuExecutor<'a> {
|
||||
handler: &'a dyn ContextMenuHandler,
|
||||
}
|
||||
|
||||
impl<'a> ContextMenuExecutor<'a> {
|
||||
pub fn new(handler: &'a dyn ContextMenuHandler) -> Self {
|
||||
Self { handler }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for ContextMenuExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::ShowContextMenu(context_menu_event) = &event.etype {
|
||||
if let Err(error) = self.handler.show_context_menu(&context_menu_event.items) {
|
||||
error!("context menu handler reported an error: {:?}", error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
64
espanso-engine/src/dispatch/executor/html_inject.rs
Normal file
64
espanso-engine/src/dispatch/executor/html_inject.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
dispatch::Executor,
|
||||
event::{Event, EventType},
|
||||
};
|
||||
|
||||
pub trait HtmlInjector {
|
||||
fn inject_html(&self, html: &str, fallback: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct HtmlInjectExecutor<'a> {
|
||||
injector: &'a dyn HtmlInjector,
|
||||
}
|
||||
|
||||
impl<'a> HtmlInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn HtmlInjector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for HtmlInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::HtmlInject(inject_event) = &event.etype {
|
||||
// Render the text fallback for those applications that don't support HTML clipboard
|
||||
let decorator = html2text::render::text_renderer::TrivialDecorator::new();
|
||||
let fallback_text =
|
||||
html2text::from_read_with_decorator(inject_event.html.as_bytes(), 1000000, decorator);
|
||||
|
||||
if let Err(error) = self
|
||||
.injector
|
||||
.inject_html(&inject_event.html, &fallback_text)
|
||||
{
|
||||
error!("html injector reported an error: {:?}", error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
56
espanso-engine/src/dispatch/executor/icon_update.rs
Normal file
56
espanso-engine/src/dispatch/executor/icon_update.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 anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
dispatch::Executor,
|
||||
event::{ui::IconStatus, Event, EventType},
|
||||
};
|
||||
|
||||
pub trait IconHandler {
|
||||
fn update_icon(&self, status: &IconStatus) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct IconUpdateExecutor<'a> {
|
||||
handler: &'a dyn IconHandler,
|
||||
}
|
||||
|
||||
impl<'a> IconUpdateExecutor<'a> {
|
||||
pub fn new(handler: &'a dyn IconHandler) -> Self {
|
||||
Self { handler }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for IconUpdateExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::IconStatusChange(m_event) = &event.etype {
|
||||
if let Err(error) = self.handler.update_icon(&m_event.status) {
|
||||
error!("icon handler reported an error: {:?}", error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
56
espanso-engine/src/dispatch/executor/image_inject.rs
Normal file
56
espanso-engine/src/dispatch/executor/image_inject.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 anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
dispatch::Executor,
|
||||
event::{Event, EventType},
|
||||
};
|
||||
|
||||
pub trait ImageInjector {
|
||||
fn inject_image(&self, path: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct ImageInjectExecutor<'a> {
|
||||
injector: &'a dyn ImageInjector,
|
||||
}
|
||||
|
||||
impl<'a> ImageInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn ImageInjector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for ImageInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::ImageInject(inject_event) = &event.etype {
|
||||
if let Err(error) = self.injector.inject_image(&inject_event.image_path) {
|
||||
error!("image injector reported an error: {:?}", error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
52
espanso-engine/src/dispatch/executor/key_inject.rs
Normal file
52
espanso-engine/src/dispatch/executor/key_inject.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 crate::{
|
||||
dispatch::Executor,
|
||||
event::{input::Key, Event, EventType},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
pub trait KeyInjector {
|
||||
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct KeyInjectExecutor<'a> {
|
||||
injector: &'a dyn KeyInjector,
|
||||
}
|
||||
|
||||
impl<'a> KeyInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn KeyInjector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for KeyInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::KeySequenceInject(inject_event) = &event.etype {
|
||||
if let Err(error) = self.injector.inject_sequence(&inject_event.keys) {
|
||||
error!("key injector reported an error: {}", error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
26
espanso-engine/src/dispatch/executor/mod.rs
Normal file
26
espanso-engine/src/dispatch/executor/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 context_menu;
|
||||
pub mod html_inject;
|
||||
pub mod icon_update;
|
||||
pub mod image_inject;
|
||||
pub mod key_inject;
|
||||
pub mod secure_input;
|
||||
pub mod text_inject;
|
59
espanso-engine/src/dispatch/executor/secure_input.rs
Normal file
59
espanso-engine/src/dispatch/executor/secure_input.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
dispatch::Executor,
|
||||
event::{Event, EventType},
|
||||
};
|
||||
|
||||
pub trait SecureInputManager {
|
||||
fn display_secure_input_troubleshoot(&self) -> Result<()>;
|
||||
fn launch_secure_input_autofix(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct SecureInputExecutor<'a> {
|
||||
manager: &'a dyn SecureInputManager,
|
||||
}
|
||||
|
||||
impl<'a> SecureInputExecutor<'a> {
|
||||
pub fn new(manager: &'a dyn SecureInputManager) -> Self {
|
||||
Self { manager }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for SecureInputExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let EventType::DisplaySecureInputTroubleshoot = &event.etype {
|
||||
if let Err(error) = self.manager.display_secure_input_troubleshoot() {
|
||||
error!("unable to display secure input troubleshoot: {}", error);
|
||||
}
|
||||
return true;
|
||||
} else if let EventType::LaunchSecureInputAutoFix = &event.etype {
|
||||
if let Err(error) = self.manager.launch_secure_input_autofix() {
|
||||
error!("unable to launch secure input autofix: {}", error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
117
espanso-engine/src/dispatch/executor/text_inject.rs
Normal file
117
espanso-engine/src/dispatch/executor/text_inject.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 crate::{
|
||||
dispatch::Executor,
|
||||
event::{effect::TextInjectMode, Event, EventType},
|
||||
};
|
||||
use anyhow::Result;
|
||||
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> {
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
}
|
||||
|
||||
impl<'a> TextInjectExecutor<'a> {
|
||||
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 EventType::TextInject(inject_event) = &event.etype {
|
||||
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
|
||||
}
|
||||
} 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
|
65
espanso-engine/src/dispatch/mod.rs
Normal file
65
espanso-engine/src/dispatch/mod.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 crate::event::Event;
|
||||
|
||||
mod default;
|
||||
mod executor;
|
||||
|
||||
pub trait Executor {
|
||||
fn execute(&self, event: &Event) -> bool;
|
||||
}
|
||||
|
||||
pub trait Dispatcher {
|
||||
fn dispatch(&self, event: Event);
|
||||
}
|
||||
|
||||
// Re-export dependency injection entities
|
||||
pub use executor::context_menu::ContextMenuHandler;
|
||||
pub use executor::html_inject::HtmlInjector;
|
||||
pub use executor::icon_update::IconHandler;
|
||||
pub use executor::image_inject::ImageInjector;
|
||||
pub use executor::key_inject::KeyInjector;
|
||||
pub use executor::secure_input::SecureInputManager;
|
||||
pub use executor::text_inject::{Mode, ModeProvider, TextInjector};
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn default<'a>(
|
||||
event_injector: &'a dyn TextInjector,
|
||||
clipboard_injector: &'a dyn TextInjector,
|
||||
mode_provider: &'a dyn ModeProvider,
|
||||
key_injector: &'a dyn KeyInjector,
|
||||
html_injector: &'a dyn HtmlInjector,
|
||||
image_injector: &'a dyn ImageInjector,
|
||||
context_menu_handler: &'a dyn ContextMenuHandler,
|
||||
icon_handler: &'a dyn IconHandler,
|
||||
secure_input_manager: &'a dyn SecureInputManager,
|
||||
) -> impl Dispatcher + 'a {
|
||||
default::DefaultDispatcher::new(
|
||||
event_injector,
|
||||
clipboard_injector,
|
||||
mode_provider,
|
||||
key_injector,
|
||||
html_injector,
|
||||
image_injector,
|
||||
context_menu_handler,
|
||||
icon_handler,
|
||||
secure_input_manager,
|
||||
)
|
||||
}
|
63
espanso-engine/src/event/effect.rs
Normal file
63
espanso-engine/src/event/effect.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 super::input::Key;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TriggerCompensationEvent {
|
||||
pub trigger: String,
|
||||
pub left_separator: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CursorHintCompensationEvent {
|
||||
pub cursor_hint_back_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TextInjectRequest {
|
||||
pub text: String,
|
||||
pub force_mode: Option<TextInjectMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MarkdownInjectRequest {
|
||||
pub markdown: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HtmlInjectRequest {
|
||||
pub html: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TextInjectMode {
|
||||
Keys,
|
||||
Clipboard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KeySequenceInjectRequest {
|
||||
pub keys: Vec<Key>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageInjectRequest {
|
||||
pub image_path: String,
|
||||
}
|
123
espanso-engine/src/event/input.rs
Normal file
123
espanso-engine/src/event/input.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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, PartialEq, Clone)]
|
||||
pub enum Status {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Variant {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct KeyboardEvent {
|
||||
pub key: Key,
|
||||
pub value: Option<String>,
|
||||
pub status: Status,
|
||||
pub variant: Option<Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Button1,
|
||||
Button2,
|
||||
Button3,
|
||||
Button4,
|
||||
Button5,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MouseEvent {
|
||||
pub button: MouseButton,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Key {
|
||||
// Modifiers
|
||||
Alt,
|
||||
CapsLock,
|
||||
Control,
|
||||
Meta,
|
||||
NumLock,
|
||||
Shift,
|
||||
|
||||
// Whitespace
|
||||
Enter,
|
||||
Tab,
|
||||
Space,
|
||||
|
||||
// Navigation
|
||||
ArrowDown,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUp,
|
||||
End,
|
||||
Home,
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
// UI
|
||||
Escape,
|
||||
|
||||
// Editing keys
|
||||
Backspace,
|
||||
|
||||
// Function keys
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
|
||||
// Other keys, includes the raw code provided by the operating system
|
||||
Other(i32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ContextMenuClickedEvent {
|
||||
pub context_item_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct HotKeyEvent {
|
||||
pub hotkey_id: i32,
|
||||
}
|
91
espanso-engine/src/event/internal.rs
Normal file
91
espanso-engine/src/event/internal.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 MatchesDetectedEvent {
|
||||
pub matches: Vec<DetectedMatch>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct DetectedMatch {
|
||||
pub id: i32,
|
||||
pub trigger: Option<String>,
|
||||
pub left_separator: Option<String>,
|
||||
pub right_separator: 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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderingRequestedEvent {
|
||||
pub match_id: i32,
|
||||
pub trigger: Option<String>,
|
||||
pub left_separator: Option<String>,
|
||||
pub right_separator: Option<String>,
|
||||
pub trigger_args: HashMap<String, String>,
|
||||
pub format: TextFormat,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TextFormat {
|
||||
Plain,
|
||||
Markdown,
|
||||
Html,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ImageRequestedEvent {
|
||||
pub match_id: i32,
|
||||
pub image_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ImageResolvedEvent {
|
||||
pub image_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderedEvent {
|
||||
pub match_id: i32,
|
||||
pub body: String,
|
||||
pub format: TextFormat,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DiscardPreviousEvent {
|
||||
// All Events with a source_id smaller than this one will be discarded
|
||||
pub minimum_source_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SecureInputEnabledEvent {
|
||||
pub app_name: String,
|
||||
pub app_path: String,
|
||||
}
|
107
espanso-engine/src/event/mod.rs
Normal file
107
espanso-engine/src/event/mod.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 effect;
|
||||
pub mod input;
|
||||
pub mod internal;
|
||||
pub mod ui;
|
||||
|
||||
pub type SourceId = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
// The source id is a unique, monothonically increasing number
|
||||
// that is given to each event by the source and is propagated
|
||||
// to all consequential events.
|
||||
// For example, if a keyboard event with source_id = 5 generates
|
||||
// a detected match event, this event will have source_id = 5
|
||||
pub source_id: SourceId,
|
||||
pub etype: EventType,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn caused_by(cause_id: SourceId, event_type: EventType) -> Event {
|
||||
Event {
|
||||
source_id: cause_id,
|
||||
etype: event_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum EventType {
|
||||
NOOP,
|
||||
ProcessingError(String),
|
||||
ExitRequested(ExitMode),
|
||||
Exit(ExitMode),
|
||||
Heartbeat,
|
||||
|
||||
// Inputs
|
||||
Keyboard(input::KeyboardEvent),
|
||||
Mouse(input::MouseEvent),
|
||||
HotKey(input::HotKeyEvent),
|
||||
TrayIconClicked,
|
||||
ContextMenuClicked(input::ContextMenuClickedEvent),
|
||||
|
||||
// Internal
|
||||
MatchesDetected(internal::MatchesDetectedEvent),
|
||||
MatchSelected(internal::MatchSelectedEvent),
|
||||
CauseCompensatedMatch(internal::CauseCompensatedMatchEvent),
|
||||
|
||||
RenderingRequested(internal::RenderingRequestedEvent),
|
||||
ImageRequested(internal::ImageRequestedEvent),
|
||||
Rendered(internal::RenderedEvent),
|
||||
ImageResolved(internal::ImageResolvedEvent),
|
||||
MatchInjected,
|
||||
DiscardPrevious(internal::DiscardPreviousEvent),
|
||||
|
||||
Disabled,
|
||||
Enabled,
|
||||
DisableRequest,
|
||||
EnableRequest,
|
||||
SecureInputEnabled(internal::SecureInputEnabledEvent),
|
||||
SecureInputDisabled,
|
||||
|
||||
// Effects
|
||||
TriggerCompensation(effect::TriggerCompensationEvent),
|
||||
CursorHintCompensation(effect::CursorHintCompensationEvent),
|
||||
|
||||
KeySequenceInject(effect::KeySequenceInjectRequest),
|
||||
TextInject(effect::TextInjectRequest),
|
||||
MarkdownInject(effect::MarkdownInjectRequest),
|
||||
HtmlInject(effect::HtmlInjectRequest),
|
||||
ImageInject(effect::ImageInjectRequest),
|
||||
|
||||
// UI
|
||||
ShowContextMenu(ui::ShowContextMenuEvent),
|
||||
IconStatusChange(ui::IconStatusChangeEvent),
|
||||
DisplaySecureInputTroubleshoot,
|
||||
ShowSearchBar,
|
||||
|
||||
// Other
|
||||
LaunchSecureInputAutoFix,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExitMode {
|
||||
Exit,
|
||||
ExitAllProcesses,
|
||||
RestartWorker,
|
||||
}
|
54
espanso-engine/src/event/ui.rs
Normal file
54
espanso-engine/src/event/ui.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 ShowContextMenuEvent {
|
||||
pub items: Vec<MenuItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MenuItem {
|
||||
Simple(SimpleMenuItem),
|
||||
Sub(SubMenuItem),
|
||||
Separator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SimpleMenuItem {
|
||||
pub id: u32,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SubMenuItem {
|
||||
pub label: String,
|
||||
pub items: Vec<MenuItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct IconStatusChangeEvent {
|
||||
pub status: IconStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum IconStatus {
|
||||
Enabled,
|
||||
Disabled,
|
||||
SecureInputDisabled,
|
||||
}
|
54
espanso-engine/src/funnel/default.rs
Normal file
54
espanso-engine/src/funnel/default.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 crossbeam::channel::Select;
|
||||
|
||||
use super::{Funnel, FunnelResult, Source};
|
||||
|
||||
pub struct DefaultFunnel<'a> {
|
||||
sources: &'a [&'a dyn Source<'a>],
|
||||
}
|
||||
|
||||
impl<'a> DefaultFunnel<'a> {
|
||||
pub fn new(sources: &'a [&'a dyn Source<'a>]) -> Self {
|
||||
Self { sources }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Funnel for DefaultFunnel<'a> {
|
||||
fn receive(&self) -> FunnelResult {
|
||||
let mut select = Select::new();
|
||||
|
||||
// First register all the sources to the select operation
|
||||
for source in self.sources.iter() {
|
||||
source.register(&mut select);
|
||||
}
|
||||
|
||||
// Wait for the first source (blocking operation)
|
||||
let op = select.select();
|
||||
let source = self
|
||||
.sources
|
||||
.get(op.index())
|
||||
.expect("invalid source index returned by select operation");
|
||||
|
||||
// Receive (and convert) the event
|
||||
let event = source.receive(op);
|
||||
FunnelResult::Event(event)
|
||||
}
|
||||
}
|
44
espanso-engine/src/funnel/mod.rs
Normal file
44
espanso-engine/src/funnel/mod.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 crossbeam::{channel::Select, channel::SelectedOperation};
|
||||
|
||||
use self::default::DefaultFunnel;
|
||||
|
||||
use crate::event::Event;
|
||||
|
||||
mod default;
|
||||
|
||||
pub trait Source<'a> {
|
||||
fn register(&'a self, select: &mut Select<'a>) -> usize;
|
||||
fn receive(&'a self, op: SelectedOperation) -> Event;
|
||||
}
|
||||
|
||||
pub trait Funnel {
|
||||
fn receive(&self) -> FunnelResult;
|
||||
}
|
||||
|
||||
pub enum FunnelResult {
|
||||
Event(Event),
|
||||
EndOfStream,
|
||||
}
|
||||
|
||||
pub fn default<'a>(sources: &'a [&'a dyn Source<'a>]) -> impl Funnel + 'a {
|
||||
DefaultFunnel::new(sources)
|
||||
}
|
74
espanso-engine/src/lib.rs
Normal file
74
espanso-engine/src/lib.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
use self::{
|
||||
dispatch::Dispatcher,
|
||||
event::{Event, EventType, ExitMode},
|
||||
funnel::{Funnel, FunnelResult},
|
||||
process::Processor,
|
||||
};
|
||||
|
||||
pub mod dispatch;
|
||||
pub mod event;
|
||||
pub mod funnel;
|
||||
pub mod process;
|
||||
|
||||
pub struct Engine<'a> {
|
||||
funnel: &'a dyn Funnel,
|
||||
processor: &'a mut dyn Processor,
|
||||
dispatcher: &'a dyn Dispatcher,
|
||||
}
|
||||
|
||||
impl<'a> Engine<'a> {
|
||||
pub fn new(
|
||||
funnel: &'a dyn Funnel,
|
||||
processor: &'a mut dyn Processor,
|
||||
dispatcher: &'a dyn Dispatcher,
|
||||
) -> Self {
|
||||
Self {
|
||||
funnel,
|
||||
processor,
|
||||
dispatcher,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> ExitMode {
|
||||
loop {
|
||||
match self.funnel.receive() {
|
||||
FunnelResult::Event(event) => {
|
||||
let processed_events = self.processor.process(event);
|
||||
for event in processed_events {
|
||||
if let EventType::Exit(mode) = &event.etype {
|
||||
debug!("exit event received with mode {:?}, exiting engine", mode);
|
||||
return mode.clone();
|
||||
}
|
||||
|
||||
self.dispatcher.dispatch(event);
|
||||
}
|
||||
}
|
||||
FunnelResult::EndOfStream => {
|
||||
debug!("end of stream received");
|
||||
return ExitMode::Exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
155
espanso-engine/src/process/default.rs
Normal file
155
espanso-engine/src/process/default.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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::trace;
|
||||
|
||||
use super::{
|
||||
middleware::{
|
||||
action::{ActionMiddleware, EventSequenceProvider},
|
||||
cause::CauseCompensateMiddleware,
|
||||
cursor_hint::CursorHintMiddleware,
|
||||
delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider},
|
||||
markdown::MarkdownMiddleware,
|
||||
match_select::MatchSelectMiddleware,
|
||||
matcher::MatcherMiddleware,
|
||||
multiplex::MultiplexMiddleware,
|
||||
past_discard::PastEventsDiscardMiddleware,
|
||||
render::RenderMiddleware,
|
||||
},
|
||||
DisableOptions, MatchFilter, MatchInfoProvider, MatchProvider, MatchSelector, Matcher,
|
||||
MatcherMiddlewareConfigProvider, Middleware, Multiplexer, PathProvider, Processor, Renderer,
|
||||
};
|
||||
use crate::{
|
||||
event::{Event, EventType},
|
||||
process::middleware::{
|
||||
context_menu::ContextMenuMiddleware, disable::DisableMiddleware, exit::ExitMiddleware,
|
||||
hotkey::HotKeyMiddleware, icon_status::IconStatusMiddleware,
|
||||
image_resolve::ImageResolverMiddleware, search::SearchMiddleware,
|
||||
},
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct DefaultProcessor<'a> {
|
||||
event_queue: VecDeque<Event>,
|
||||
middleware: Vec<Box<dyn Middleware + 'a>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
impl<'a> DefaultProcessor<'a> {
|
||||
pub fn new<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,
|
||||
modifier_status_provider: &'a dyn ModifierStatusProvider,
|
||||
event_sequence_provider: &'a dyn EventSequenceProvider,
|
||||
path_provider: &'a dyn PathProvider,
|
||||
disable_options: DisableOptions,
|
||||
matcher_options_provider: &'a dyn MatcherMiddlewareConfigProvider,
|
||||
match_provider: &'a dyn MatchProvider,
|
||||
) -> DefaultProcessor<'a> {
|
||||
Self {
|
||||
event_queue: VecDeque::new(),
|
||||
middleware: vec![
|
||||
Box::new(PastEventsDiscardMiddleware::new()),
|
||||
Box::new(DisableMiddleware::new(disable_options)),
|
||||
Box::new(IconStatusMiddleware::new()),
|
||||
Box::new(MatcherMiddleware::new(matchers, matcher_options_provider)),
|
||||
Box::new(ContextMenuMiddleware::new()),
|
||||
Box::new(HotKeyMiddleware::new()),
|
||||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||
Box::new(CauseCompensateMiddleware::new()),
|
||||
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||
Box::new(RenderMiddleware::new(renderer)),
|
||||
Box::new(ImageResolverMiddleware::new(path_provider)),
|
||||
Box::new(CursorHintMiddleware::new()),
|
||||
Box::new(ExitMiddleware::new()),
|
||||
Box::new(ActionMiddleware::new(
|
||||
match_info_provider,
|
||||
event_sequence_provider,
|
||||
)),
|
||||
Box::new(SearchMiddleware::new(match_provider)),
|
||||
Box::new(MarkdownMiddleware::new()),
|
||||
Box::new(DelayForModifierReleaseMiddleware::new(
|
||||
modifier_status_provider,
|
||||
)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn process_one(&mut self) -> Option<Event> {
|
||||
if let Some(event) = self.event_queue.pop_back() {
|
||||
let mut current_event = event;
|
||||
|
||||
let mut current_queue = VecDeque::new();
|
||||
let mut dispatch = |event: Event| {
|
||||
trace!("dispatched event: {:?}", event);
|
||||
current_queue.push_front(event);
|
||||
};
|
||||
|
||||
trace!("--------------- new event -----------------");
|
||||
for middleware in self.middleware.iter() {
|
||||
trace!(
|
||||
"middleware '{}' received event: {:?}",
|
||||
middleware.name(),
|
||||
current_event
|
||||
);
|
||||
|
||||
current_event = middleware.next(current_event, &mut dispatch);
|
||||
|
||||
trace!(
|
||||
"middleware '{}' produced event: {:?}",
|
||||
middleware.name(),
|
||||
current_event
|
||||
);
|
||||
|
||||
if let EventType::NOOP = current_event.etype {
|
||||
trace!("interrupting chain as the event is NOOP");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(event) = current_queue.pop_back() {
|
||||
self.event_queue.push_front(event);
|
||||
}
|
||||
|
||||
Some(current_event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Processor for DefaultProcessor<'a> {
|
||||
fn process(&mut self, event: Event) -> Vec<Event> {
|
||||
self.event_queue.push_front(event);
|
||||
|
||||
let mut processed_events = Vec::new();
|
||||
|
||||
while !self.event_queue.is_empty() {
|
||||
if let Some(event) = self.process_one() {
|
||||
processed_events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
processed_events
|
||||
}
|
||||
}
|
136
espanso-engine/src/process/middleware/action.rs
Normal file
136
espanso-engine/src/process/middleware/action.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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::event::{
|
||||
effect::{
|
||||
HtmlInjectRequest, ImageInjectRequest, KeySequenceInjectRequest, MarkdownInjectRequest,
|
||||
TextInjectMode, TextInjectRequest,
|
||||
},
|
||||
input::Key,
|
||||
internal::{DiscardPreviousEvent, TextFormat},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub trait MatchInfoProvider {
|
||||
fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
|
||||
}
|
||||
|
||||
pub trait EventSequenceProvider {
|
||||
fn get_next_id(&self) -> u32;
|
||||
}
|
||||
|
||||
pub struct ActionMiddleware<'a> {
|
||||
match_info_provider: &'a dyn MatchInfoProvider,
|
||||
event_sequence_provider: &'a dyn EventSequenceProvider,
|
||||
}
|
||||
|
||||
impl<'a> ActionMiddleware<'a> {
|
||||
pub fn new(
|
||||
match_info_provider: &'a dyn MatchInfoProvider,
|
||||
event_sequence_provider: &'a dyn EventSequenceProvider,
|
||||
) -> Self {
|
||||
Self {
|
||||
match_info_provider,
|
||||
event_sequence_provider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for ActionMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"action"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
match &event.etype {
|
||||
EventType::Rendered(_) | EventType::ImageResolved(_) => {
|
||||
dispatch(Event::caused_by(event.source_id, EventType::MatchInjected));
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::DiscardPrevious(DiscardPreviousEvent {
|
||||
minimum_source_id: self.event_sequence_provider.get_next_id(),
|
||||
}),
|
||||
));
|
||||
|
||||
match &event.etype {
|
||||
EventType::Rendered(m_event) => Event::caused_by(
|
||||
event.source_id,
|
||||
match m_event.format {
|
||||
TextFormat::Plain => EventType::TextInject(TextInjectRequest {
|
||||
text: m_event.body.clone(),
|
||||
force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
|
||||
}),
|
||||
TextFormat::Html => EventType::HtmlInject(HtmlInjectRequest {
|
||||
html: m_event.body.clone(),
|
||||
}),
|
||||
TextFormat::Markdown => EventType::MarkdownInject(MarkdownInjectRequest {
|
||||
markdown: m_event.body.clone(),
|
||||
}),
|
||||
},
|
||||
),
|
||||
EventType::ImageResolved(m_event) => Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::ImageInject(ImageInjectRequest {
|
||||
image_path: m_event.image_path.clone(),
|
||||
}),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
EventType::CursorHintCompensation(m_event) => {
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::DiscardPrevious(DiscardPreviousEvent {
|
||||
minimum_source_id: self.event_sequence_provider.get_next_id(),
|
||||
}),
|
||||
));
|
||||
|
||||
Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::KeySequenceInject(KeySequenceInjectRequest {
|
||||
keys: (0..m_event.cursor_hint_back_count)
|
||||
.map(|_| Key::ArrowLeft)
|
||||
.collect(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
EventType::TriggerCompensation(m_event) => {
|
||||
let mut backspace_count = m_event.trigger.chars().count();
|
||||
|
||||
// We want to preserve the left separator if present
|
||||
if let Some(left_separator) = &m_event.left_separator {
|
||||
backspace_count -= left_separator.chars().count();
|
||||
}
|
||||
|
||||
Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::KeySequenceInject(KeySequenceInjectRequest {
|
||||
keys: (0..backspace_count).map(|_| Key::Backspace).collect(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
_ => event,
|
||||
}
|
||||
|
||||
// TODO: handle images
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
58
espanso-engine/src/process/middleware/cause.rs
Normal file
58
espanso-engine/src/process/middleware/cause.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 super::super::Middleware;
|
||||
use crate::{event::{Event, EventType, effect::TriggerCompensationEvent, internal::CauseCompensatedMatchEvent}};
|
||||
|
||||
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 EventType::MatchSelected(m_event) = &event.etype {
|
||||
let compensated_event =
|
||||
Event::caused_by(event.source_id, EventType::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::caused_by(event.source_id, EventType::TriggerCompensation(TriggerCompensationEvent {
|
||||
trigger: trigger.clone(),
|
||||
left_separator: m_event.chosen.left_separator.clone(),
|
||||
}));
|
||||
} else {
|
||||
return compensated_event;
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
185
espanso-engine/src/process/middleware/context_menu.rs
Normal file
185
espanso-engine/src/process/middleware/context_menu.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright id: (), label: () id: (), label: () id: (), label: ()(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;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{
|
||||
ui::{MenuItem, ShowContextMenuEvent, SimpleMenuItem},
|
||||
Event, EventType, ExitMode,
|
||||
};
|
||||
|
||||
const CONTEXT_ITEM_EXIT: u32 = 0;
|
||||
const CONTEXT_ITEM_RELOAD: u32 = 1;
|
||||
const CONTEXT_ITEM_ENABLE: u32 = 2;
|
||||
const CONTEXT_ITEM_DISABLE: u32 = 3;
|
||||
const CONTEXT_ITEM_SECURE_INPUT_EXPLAIN: u32 = 4;
|
||||
const CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND: u32 = 5;
|
||||
const CONTEXT_ITEM_OPEN_SEARCH: u32 = 6;
|
||||
|
||||
pub struct ContextMenuMiddleware {
|
||||
is_enabled: RefCell<bool>,
|
||||
is_secure_input_enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl ContextMenuMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_enabled: RefCell::new(true),
|
||||
is_secure_input_enabled: RefCell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_return)]
|
||||
impl Middleware for ContextMenuMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"context_menu"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
let mut is_enabled = self.is_enabled.borrow_mut();
|
||||
let mut is_secure_input_enabled = self.is_secure_input_enabled.borrow_mut();
|
||||
|
||||
match &event.etype {
|
||||
EventType::TrayIconClicked => {
|
||||
// TODO: fetch top matches for the active config to be added
|
||||
|
||||
let mut items = vec![
|
||||
MenuItem::Simple(if *is_enabled {
|
||||
SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_DISABLE,
|
||||
label: "Disable".to_string(),
|
||||
}
|
||||
} else {
|
||||
SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_ENABLE,
|
||||
label: "Enable".to_string(),
|
||||
}
|
||||
}),
|
||||
MenuItem::Simple(SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_OPEN_SEARCH,
|
||||
label: "Open search bar".to_string(),
|
||||
}),
|
||||
MenuItem::Separator,
|
||||
MenuItem::Simple(SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_RELOAD,
|
||||
label: "Reload config".to_string(),
|
||||
}),
|
||||
MenuItem::Separator,
|
||||
MenuItem::Simple(SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_EXIT,
|
||||
label: "Exit espanso".to_string(),
|
||||
}),
|
||||
];
|
||||
|
||||
if *is_secure_input_enabled {
|
||||
items.insert(
|
||||
0,
|
||||
MenuItem::Simple(SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_SECURE_INPUT_EXPLAIN,
|
||||
label: "Why is espanso not working?".to_string(),
|
||||
}),
|
||||
);
|
||||
items.insert(
|
||||
1,
|
||||
MenuItem::Simple(SimpleMenuItem {
|
||||
id: CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND,
|
||||
label: "Launch SecureInput auto-fix".to_string(),
|
||||
}),
|
||||
);
|
||||
items.insert(2, MenuItem::Separator);
|
||||
}
|
||||
|
||||
// TODO: my idea is to use a set of reserved u32 ids for built-in
|
||||
// actions such as Exit, Open Editor etc
|
||||
// then we need some u32 for the matches, so we need to create
|
||||
// a mapping structure match_id <-> context-menu-id
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::ShowContextMenu(ShowContextMenuEvent {
|
||||
// TODO: add actual entries
|
||||
items,
|
||||
}),
|
||||
);
|
||||
}
|
||||
EventType::ContextMenuClicked(context_click_event) => {
|
||||
match context_click_event.context_item_id {
|
||||
CONTEXT_ITEM_EXIT => Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::ExitRequested(ExitMode::ExitAllProcesses),
|
||||
),
|
||||
CONTEXT_ITEM_RELOAD => Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::ExitRequested(ExitMode::RestartWorker),
|
||||
),
|
||||
CONTEXT_ITEM_ENABLE => {
|
||||
dispatch(Event::caused_by(event.source_id, EventType::EnableRequest));
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
CONTEXT_ITEM_DISABLE => {
|
||||
dispatch(Event::caused_by(event.source_id, EventType::DisableRequest));
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
CONTEXT_ITEM_SECURE_INPUT_EXPLAIN => {
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::DisplaySecureInputTroubleshoot,
|
||||
));
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
CONTEXT_ITEM_SECURE_INPUT_TRIGGER_WORKAROUND => {
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::LaunchSecureInputAutoFix,
|
||||
));
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
CONTEXT_ITEM_OPEN_SEARCH => {
|
||||
dispatch(Event::caused_by(event.source_id, EventType::ShowSearchBar));
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
_ => {
|
||||
// TODO: handle dynamic items
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
EventType::Disabled => {
|
||||
*is_enabled = false;
|
||||
event
|
||||
}
|
||||
EventType::Enabled => {
|
||||
*is_enabled = true;
|
||||
event
|
||||
}
|
||||
EventType::SecureInputEnabled(_) => {
|
||||
*is_secure_input_enabled = true;
|
||||
event
|
||||
}
|
||||
EventType::SecureInputDisabled => {
|
||||
*is_secure_input_enabled = false;
|
||||
event
|
||||
}
|
||||
_ => event,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
82
espanso-engine/src/process/middleware/cursor_hint.rs
Normal file
82
espanso-engine/src/process/middleware/cursor_hint.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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::event::{
|
||||
effect::CursorHintCompensationEvent, internal::RenderedEvent, Event, EventType,
|
||||
};
|
||||
|
||||
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 EventType::Rendered(m_event) = event.etype {
|
||||
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::caused_by(
|
||||
event.source_id,
|
||||
EventType::CursorHintCompensation(CursorHintCompensationEvent {
|
||||
cursor_hint_back_count,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
// Alter the rendered event to remove the cursor hint from the body
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::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
|
84
espanso-engine/src/process/middleware/delay_modifiers.rs
Normal file
84
espanso-engine/src/process/middleware/delay_modifiers.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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::time::{Duration, Instant};
|
||||
|
||||
use log::{trace, warn};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{Event, EventType};
|
||||
|
||||
/// Maximum time to wait for modifiers being released before
|
||||
/// giving up.
|
||||
const MODIFIER_DELAY_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
|
||||
pub trait ModifierStatusProvider {
|
||||
fn is_any_conflicting_modifier_pressed(&self) -> bool;
|
||||
}
|
||||
|
||||
/// This middleware is used to delay the injection of text until
|
||||
/// all modifiers have been released. This is needed as otherwise,
|
||||
/// injections might misbehave as pressed modifiers might alter
|
||||
/// the keys being injected.
|
||||
pub struct DelayForModifierReleaseMiddleware<'a> {
|
||||
provider: &'a dyn ModifierStatusProvider,
|
||||
}
|
||||
|
||||
impl<'a> DelayForModifierReleaseMiddleware<'a> {
|
||||
pub fn new(provider: &'a dyn ModifierStatusProvider) -> Self {
|
||||
Self { provider }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for DelayForModifierReleaseMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"delay_modifiers"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if is_injection_event(&event.etype) {
|
||||
let start = Instant::now();
|
||||
while self.provider.is_any_conflicting_modifier_pressed() {
|
||||
if Instant::now().duration_since(start) > MODIFIER_DELAY_TIMEOUT {
|
||||
warn!("injection delay has timed out, please release the modifier keys (SHIFT, CTRL, ALT, CMD) to trigger an expansion");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: here we might show a popup window to tell the users to release those keys
|
||||
|
||||
trace!("delaying injection event as some modifiers are pressed");
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fn is_injection_event(event_type: &EventType) -> bool {
|
||||
matches!(
|
||||
event_type,
|
||||
EventType::TriggerCompensation(_)
|
||||
| EventType::CursorHintCompensation(_)
|
||||
| EventType::KeySequenceInject(_)
|
||||
| EventType::TextInject(_)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: test
|
138
espanso-engine/src/process/middleware/disable.rs
Normal file
138
espanso-engine/src/process/middleware/disable.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use log::info;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{
|
||||
input::{Key, KeyboardEvent, Status, Variant},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub struct DisableOptions {
|
||||
pub toggle_key: Option<Key>,
|
||||
pub toggle_key_variant: Option<Variant>,
|
||||
pub toggle_key_maximum_window: Duration,
|
||||
// TODO: toggle shortcut?
|
||||
}
|
||||
|
||||
pub struct DisableMiddleware {
|
||||
enabled: RefCell<bool>,
|
||||
last_toggle_press: RefCell<Option<Instant>>,
|
||||
options: DisableOptions,
|
||||
}
|
||||
|
||||
impl DisableMiddleware {
|
||||
pub fn new(options: DisableOptions) -> Self {
|
||||
Self {
|
||||
enabled: RefCell::new(true),
|
||||
last_toggle_press: RefCell::new(None),
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for DisableMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"disable"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
let mut has_status_changed = false;
|
||||
let mut enabled = self.enabled.borrow_mut();
|
||||
|
||||
match &event.etype {
|
||||
EventType::Keyboard(m_event) => {
|
||||
if is_toggle_key(m_event, &self.options) {
|
||||
let mut last_toggle_press = self.last_toggle_press.borrow_mut();
|
||||
if let Some(previous_press) = *last_toggle_press {
|
||||
if previous_press.elapsed() < self.options.toggle_key_maximum_window {
|
||||
*enabled = !*enabled;
|
||||
*last_toggle_press = None;
|
||||
has_status_changed = true;
|
||||
} else {
|
||||
*last_toggle_press = Some(Instant::now());
|
||||
}
|
||||
} else {
|
||||
*last_toggle_press = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
EventType::EnableRequest => {
|
||||
*enabled = true;
|
||||
has_status_changed = true;
|
||||
}
|
||||
EventType::DisableRequest => {
|
||||
*enabled = false;
|
||||
has_status_changed = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if has_status_changed {
|
||||
info!("toggled enabled state, is_enabled = {}", *enabled);
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
if *enabled {
|
||||
EventType::Enabled
|
||||
} else {
|
||||
EventType::Disabled
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
// Block keyboard events when disabled
|
||||
if let EventType::Keyboard(_) = &event.etype {
|
||||
if !*enabled {
|
||||
return Event::caused_by(event.source_id, EventType::NOOP);
|
||||
}
|
||||
}
|
||||
// TODO: also ignore hotkey and mouse events
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fn is_toggle_key(event: &KeyboardEvent, options: &DisableOptions) -> bool {
|
||||
if event.status != Status::Released {
|
||||
return false;
|
||||
}
|
||||
|
||||
if options
|
||||
.toggle_key
|
||||
.as_ref()
|
||||
.map(|key| key == &event.key)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let (Some(variant), Some(e_variant)) = (&options.toggle_key_variant, &event.variant) {
|
||||
variant == e_variant
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
51
espanso-engine/src/process/middleware/exit.rs
Normal file
51
espanso-engine/src/process/middleware/exit.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;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{Event, EventType};
|
||||
|
||||
pub struct ExitMiddleware {}
|
||||
|
||||
impl ExitMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for ExitMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"exit"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::ExitRequested(mode) = &event.etype {
|
||||
debug!(
|
||||
"received ExitRequested event with mode: {:?}, dispatching exit",
|
||||
mode
|
||||
);
|
||||
return Event::caused_by(event.source_id, EventType::Exit(mode.clone()));
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
56
espanso-engine/src/process/middleware/hotkey.rs
Normal file
56
espanso-engine/src/process/middleware/hotkey.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* This file is part of espanso id: (), trigger: (), trigger: (), left_separator: (), right_separator: (), args: () left_separator: (), right_separator: (), args: () id: (), trigger: (), left_separator: (), right_separator: (), args: ().
|
||||
*
|
||||
* 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::event::{
|
||||
internal::{DetectedMatch, MatchesDetectedEvent},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub struct HotKeyMiddleware {}
|
||||
|
||||
impl HotKeyMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for HotKeyMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"hotkey"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::HotKey(m_event) = &event.etype {
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::MatchesDetected(MatchesDetectedEvent {
|
||||
matches: vec![DetectedMatch {
|
||||
id: m_event.hotkey_id,
|
||||
..Default::default()
|
||||
}],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
81
espanso-engine/src/process/middleware/icon_status.rs
Normal file
81
espanso-engine/src/process/middleware/icon_status.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{
|
||||
ui::{IconStatus, IconStatusChangeEvent},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub struct IconStatusMiddleware {
|
||||
enabled: RefCell<bool>,
|
||||
secure_input_enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
impl IconStatusMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enabled: RefCell::new(true),
|
||||
secure_input_enabled: RefCell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for IconStatusMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"icon_status"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
let mut enabled = self.enabled.borrow_mut();
|
||||
let mut secure_input_enabled = self.secure_input_enabled.borrow_mut();
|
||||
|
||||
let mut did_update = true;
|
||||
match &event.etype {
|
||||
EventType::Enabled => *enabled = true,
|
||||
EventType::Disabled => *enabled = false,
|
||||
EventType::SecureInputEnabled(_) => *secure_input_enabled = true,
|
||||
EventType::SecureInputDisabled => *secure_input_enabled = false,
|
||||
_ => did_update = false,
|
||||
}
|
||||
|
||||
if did_update {
|
||||
let status = if *enabled {
|
||||
if *secure_input_enabled {
|
||||
IconStatus::SecureInputDisabled
|
||||
} else {
|
||||
IconStatus::Enabled
|
||||
}
|
||||
} else {
|
||||
IconStatus::Disabled
|
||||
};
|
||||
|
||||
dispatch(Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::IconStatusChange(IconStatusChangeEvent { status }),
|
||||
));
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
81
espanso-engine/src/process/middleware/image_resolve.rs
Normal file
81
espanso-engine/src/process/middleware/image_resolve.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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::path::Path;
|
||||
|
||||
use log::error;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{internal::ImageResolvedEvent, Event, EventType};
|
||||
|
||||
pub trait PathProvider {
|
||||
fn get_config_path(&self) -> &Path;
|
||||
}
|
||||
|
||||
pub struct ImageResolverMiddleware<'a> {
|
||||
provider: &'a dyn PathProvider,
|
||||
}
|
||||
|
||||
impl<'a> ImageResolverMiddleware<'a> {
|
||||
pub fn new(provider: &'a dyn PathProvider) -> Self {
|
||||
Self { provider }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for ImageResolverMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"image_resolve"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::ImageRequested(m_event) = &event.etype {
|
||||
// On Windows, we have to replace the forward / with the backslash \ in the path
|
||||
let path = if cfg!(target_os = "windows") {
|
||||
m_event.image_path.replace("/", "\\")
|
||||
} else {
|
||||
m_event.image_path.to_owned()
|
||||
};
|
||||
|
||||
let path = if path.contains("$CONFIG") {
|
||||
let config_path = match self.provider.get_config_path().canonicalize() {
|
||||
Ok(path) => path,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"unable to canonicalize the config path into the image resolver: {}",
|
||||
err
|
||||
);
|
||||
self.provider.get_config_path().to_owned()
|
||||
}
|
||||
};
|
||||
path.replace("$CONFIG", &config_path.to_string_lossy())
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::ImageResolved(ImageResolvedEvent { image_path: path }),
|
||||
);
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
63
espanso-engine/src/process/middleware/markdown.rs
Normal file
63
espanso-engine/src/process/middleware/markdown.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 super::super::Middleware;
|
||||
use crate::event::{effect::HtmlInjectRequest, Event, EventType};
|
||||
|
||||
// Convert markdown injection requests to HTML on the fly
|
||||
pub struct MarkdownMiddleware {}
|
||||
|
||||
impl MarkdownMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for MarkdownMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"markdown"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::MarkdownInject(m_event) = &event.etype {
|
||||
// Render the markdown into HTML
|
||||
let html = markdown::to_html(&m_event.markdown);
|
||||
let mut html = html.trim();
|
||||
|
||||
// Remove the surrounding paragraph
|
||||
if html.starts_with("<p>") {
|
||||
html = html.trim_start_matches("<p>");
|
||||
}
|
||||
if html.ends_with("</p>") {
|
||||
html = html.trim_end_matches("</p>");
|
||||
}
|
||||
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::HtmlInject(HtmlInjectRequest {
|
||||
html: html.to_owned(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
102
espanso-engine/src/process/middleware/match_select.rs
Normal file
102
espanso-engine/src/process/middleware/match_select.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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::event::{internal::MatchSelectedEvent, Event, EventType};
|
||||
|
||||
pub trait MatchFilter {
|
||||
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32>;
|
||||
}
|
||||
|
||||
pub trait MatchSelector {
|
||||
fn select(&self, matches_ids: &[i32]) -> Option<i32>;
|
||||
}
|
||||
|
||||
pub struct MatchSelectMiddleware<'a> {
|
||||
match_filter: &'a dyn MatchFilter,
|
||||
match_selector: &'a dyn MatchSelector,
|
||||
}
|
||||
|
||||
impl<'a> MatchSelectMiddleware<'a> {
|
||||
pub fn new(match_filter: &'a dyn MatchFilter, match_selector: &'a dyn MatchSelector) -> Self {
|
||||
Self {
|
||||
match_filter,
|
||||
match_selector,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for MatchSelectMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"match_select"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::MatchesDetected(m_event) = event.etype {
|
||||
let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect();
|
||||
|
||||
// Find the matches that are actually valid in the current context
|
||||
let valid_ids = self.match_filter.filter_active(&matches_ids);
|
||||
|
||||
return match valid_ids.len() {
|
||||
0 => Event::caused_by(event.source_id, EventType::NOOP), // No valid matches, consume the event
|
||||
1 => {
|
||||
// Only one match, no need to show a selection dialog
|
||||
let m = m_event
|
||||
.matches
|
||||
.into_iter()
|
||||
.find(|m| m.id == *valid_ids.first().unwrap());
|
||||
if let Some(m) = m {
|
||||
Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::MatchSelected(MatchSelectedEvent { chosen: m }),
|
||||
)
|
||||
} else {
|
||||
error!("MatchSelectMiddleware could not find the correspondent match");
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Multiple matches, we need to ask the user which one to use
|
||||
if let Some(selected_id) = self.match_selector.select(&valid_ids) {
|
||||
let m = m_event.matches.into_iter().find(|m| m.id == selected_id);
|
||||
if let Some(m) = m {
|
||||
Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::MatchSelected(MatchSelectedEvent { chosen: m }),
|
||||
)
|
||||
} else {
|
||||
error!("MatchSelectMiddleware could not find the correspondent match");
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
} else {
|
||||
debug!("MatchSelectMiddleware did not receive any match selection");
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
207
espanso-engine/src/process/middleware/matcher.rs
Normal file
207
espanso-engine/src/process/middleware/matcher.rs
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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::trace;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{
|
||||
input::{Key, Status},
|
||||
internal::{DetectedMatch, MatchesDetectedEvent},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub trait Matcher<'a, State> {
|
||||
fn process(
|
||||
&'a self,
|
||||
prev_state: Option<&State>,
|
||||
event: &MatcherEvent,
|
||||
) -> (State, Vec<MatchResult>);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MatcherEvent {
|
||||
Key { key: Key, chars: Option<String> },
|
||||
VirtualSeparator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MatchResult {
|
||||
pub id: i32,
|
||||
pub trigger: String,
|
||||
pub left_separator: Option<String>,
|
||||
pub right_separator: Option<String>,
|
||||
pub args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub trait MatcherMiddlewareConfigProvider {
|
||||
fn max_history_size(&self) -> usize;
|
||||
}
|
||||
|
||||
pub struct MatcherMiddleware<'a, State> {
|
||||
matchers: &'a [&'a dyn Matcher<'a, State>],
|
||||
|
||||
matcher_states: RefCell<VecDeque<Vec<State>>>,
|
||||
|
||||
max_history_size: usize,
|
||||
}
|
||||
|
||||
impl<'a, State> MatcherMiddleware<'a, State> {
|
||||
pub fn new(
|
||||
matchers: &'a [&'a dyn Matcher<'a, State>],
|
||||
options_provider: &'a dyn MatcherMiddlewareConfigProvider,
|
||||
) -> Self {
|
||||
let max_history_size = options_provider.max_history_size();
|
||||
|
||||
Self {
|
||||
matchers,
|
||||
matcher_states: RefCell::new(VecDeque::new()),
|
||||
max_history_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, State> Middleware for MatcherMiddleware<'a, State> {
|
||||
fn name(&self) -> &'static str {
|
||||
"matcher"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if is_event_of_interest(&event.etype) {
|
||||
let mut matcher_states = self.matcher_states.borrow_mut();
|
||||
let prev_states = if !matcher_states.is_empty() {
|
||||
matcher_states.get(matcher_states.len() - 1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let EventType::Keyboard(keyboard_event) = &event.etype {
|
||||
// Backspace handling
|
||||
if keyboard_event.key == Key::Backspace {
|
||||
trace!("popping the last matcher state");
|
||||
matcher_states.pop_back();
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
// Some keys (such as the arrow keys) and mouse clicks prevent espanso from building
|
||||
// an accurate key buffer, so we need to invalidate it.
|
||||
if is_invalidating_event(&event.etype) {
|
||||
trace!("invalidating event detected, clearing matching state");
|
||||
matcher_states.clear();
|
||||
return event;
|
||||
}
|
||||
|
||||
let mut all_results = Vec::new();
|
||||
|
||||
if let Some(matcher_event) = convert_to_matcher_event(&event.etype) {
|
||||
let mut new_states = Vec::new();
|
||||
for (i, matcher) in self.matchers.iter().enumerate() {
|
||||
let prev_state = prev_states.and_then(|states| states.get(i));
|
||||
|
||||
let (state, results) = matcher.process(prev_state, &matcher_event);
|
||||
all_results.extend(results);
|
||||
|
||||
new_states.push(state);
|
||||
}
|
||||
|
||||
matcher_states.push_back(new_states);
|
||||
if matcher_states.len() > self.max_history_size {
|
||||
matcher_states.pop_front();
|
||||
}
|
||||
|
||||
if !all_results.is_empty() {
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::MatchesDetected(MatchesDetectedEvent {
|
||||
matches: all_results
|
||||
.into_iter()
|
||||
.map(|result| DetectedMatch {
|
||||
id: result.id,
|
||||
trigger: Some(result.trigger),
|
||||
right_separator: result.right_separator,
|
||||
left_separator: result.left_separator,
|
||||
args: result.args,
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fn is_event_of_interest(event_type: &EventType) -> bool {
|
||||
match event_type {
|
||||
EventType::Keyboard(keyboard_event) => {
|
||||
if keyboard_event.status != Status::Pressed {
|
||||
// Skip non-press events
|
||||
false
|
||||
} else {
|
||||
// Skip modifier keys
|
||||
!matches!(
|
||||
keyboard_event.key,
|
||||
Key::Alt | Key::Shift | Key::CapsLock | Key::Meta | Key::NumLock | Key::Control
|
||||
)
|
||||
}
|
||||
}
|
||||
EventType::Mouse(mouse_event) => mouse_event.status == Status::Pressed,
|
||||
EventType::MatchInjected => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_matcher_event(event_type: &EventType) -> Option<MatcherEvent> {
|
||||
match event_type {
|
||||
EventType::Keyboard(keyboard_event) => Some(MatcherEvent::Key {
|
||||
key: keyboard_event.key.clone(),
|
||||
chars: keyboard_event.value.clone(),
|
||||
}),
|
||||
EventType::Mouse(_) => Some(MatcherEvent::VirtualSeparator),
|
||||
EventType::MatchInjected => Some(MatcherEvent::VirtualSeparator),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_invalidating_event(event_type: &EventType) -> bool {
|
||||
match event_type {
|
||||
EventType::Keyboard(keyboard_event) => matches!(
|
||||
keyboard_event.key,
|
||||
Key::ArrowDown
|
||||
| Key::ArrowLeft
|
||||
| Key::ArrowRight
|
||||
| Key::ArrowUp
|
||||
| Key::End
|
||||
| Key::Home
|
||||
| Key::PageDown
|
||||
| Key::PageUp
|
||||
| Key::Escape
|
||||
),
|
||||
EventType::Mouse(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
36
espanso-engine/src/process/middleware/mod.rs
Normal file
36
espanso-engine/src/process/middleware/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 action;
|
||||
pub mod cause;
|
||||
pub mod context_menu;
|
||||
pub mod cursor_hint;
|
||||
pub mod delay_modifiers;
|
||||
pub mod disable;
|
||||
pub mod exit;
|
||||
pub mod hotkey;
|
||||
pub mod icon_status;
|
||||
pub mod image_resolve;
|
||||
pub mod markdown;
|
||||
pub mod match_select;
|
||||
pub mod matcher;
|
||||
pub mod multiplex;
|
||||
pub mod past_discard;
|
||||
pub mod render;
|
||||
pub mod search;
|
59
espanso-engine/src/process/middleware/multiplex.rs
Normal file
59
espanso-engine/src/process/middleware/multiplex.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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::error;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{internal::DetectedMatch, Event, EventType};
|
||||
|
||||
pub trait Multiplexer {
|
||||
fn convert(&self, m: DetectedMatch) -> Option<EventType>;
|
||||
}
|
||||
|
||||
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 name(&self) -> &'static str {
|
||||
"multiplex"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::CauseCompensatedMatch(m_event) = event.etype {
|
||||
return match self.multiplexer.convert(m_event.m) {
|
||||
Some(new_event) => Event::caused_by(event.source_id, new_event),
|
||||
None => {
|
||||
error!("match multiplexing failed");
|
||||
Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
69
espanso-engine/src/process/middleware/past_discard.rs
Normal file
69
espanso-engine/src/process/middleware/past_discard.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
use log::trace;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{Event, EventType, SourceId};
|
||||
|
||||
/// This middleware discards all events that have a source_id smaller than its
|
||||
/// configured threshold. This useful to discard past events that might have
|
||||
/// been stuck in the event queue for too long.
|
||||
pub struct PastEventsDiscardMiddleware {
|
||||
source_id_threshold: RefCell<SourceId>,
|
||||
}
|
||||
|
||||
impl PastEventsDiscardMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
source_id_threshold: RefCell::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for PastEventsDiscardMiddleware {
|
||||
fn name(&self) -> &'static str {
|
||||
"past_discard"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
let mut source_id_threshold = self.source_id_threshold.borrow_mut();
|
||||
|
||||
// Filter out previous events
|
||||
if event.source_id < *source_id_threshold {
|
||||
trace!("discarding previous event: {:?}", event);
|
||||
return Event::caused_by(event.source_id, EventType::NOOP);
|
||||
}
|
||||
|
||||
// Update the minimum threshold
|
||||
if let EventType::DiscardPrevious(m_event) = &event.etype {
|
||||
trace!(
|
||||
"updating minimum source id threshold for events to: {}",
|
||||
m_event.minimum_source_id
|
||||
);
|
||||
*source_id_threshold = m_event.minimum_source_id;
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
104
espanso-engine/src/process/middleware/render.rs
Normal file
104
espanso-engine/src/process/middleware/render.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 log::error;
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::event::{internal::RenderedEvent, Event, EventType};
|
||||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
pub trait Renderer<'a> {
|
||||
fn render(
|
||||
&'a self,
|
||||
match_id: i32,
|
||||
trigger: Option<&str>,
|
||||
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 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 name(&self) -> &'static str {
|
||||
"render"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::RenderingRequested(m_event) = event.etype {
|
||||
match self.renderer.render(
|
||||
m_event.match_id,
|
||||
m_event.trigger.as_deref(),
|
||||
m_event.trigger_args,
|
||||
) {
|
||||
Ok(body) => {
|
||||
let body = if let Some(right_separator) = m_event.right_separator {
|
||||
format!("{}{}", body, right_separator)
|
||||
} else {
|
||||
body
|
||||
};
|
||||
|
||||
return Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::Rendered(RenderedEvent {
|
||||
match_id: m_event.match_id,
|
||||
body,
|
||||
format: m_event.format,
|
||||
}),
|
||||
);
|
||||
}
|
||||
Err(err) => match err.downcast_ref::<RendererError>() {
|
||||
Some(RendererError::Aborted) => {
|
||||
return Event::caused_by(event.source_id, EventType::NOOP)
|
||||
}
|
||||
_ => {
|
||||
error!("error during rendering: {}", err);
|
||||
return Event::caused_by(event.source_id, EventType::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string()));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
75
espanso-engine/src/process/middleware/search.rs
Normal file
75
espanso-engine/src/process/middleware/search.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 super::super::Middleware;
|
||||
use crate::event::{
|
||||
internal::{DetectedMatch, MatchesDetectedEvent},
|
||||
Event, EventType,
|
||||
};
|
||||
|
||||
pub trait MatchProvider {
|
||||
fn get_all_matches_ids(&self) -> Vec<i32>;
|
||||
}
|
||||
|
||||
pub struct SearchMiddleware<'a> {
|
||||
match_provider: &'a dyn MatchProvider,
|
||||
}
|
||||
|
||||
impl<'a> SearchMiddleware<'a> {
|
||||
pub fn new(match_provider: &'a dyn MatchProvider) -> Self {
|
||||
Self { match_provider }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for SearchMiddleware<'a> {
|
||||
fn name(&self) -> &'static str {
|
||||
"search"
|
||||
}
|
||||
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
if let EventType::ShowSearchBar = event.etype {
|
||||
let detected_matches = Event::caused_by(
|
||||
event.source_id,
|
||||
EventType::MatchesDetected(MatchesDetectedEvent {
|
||||
matches: self
|
||||
.match_provider
|
||||
.get_all_matches_ids()
|
||||
.into_iter()
|
||||
.map(|id| DetectedMatch {
|
||||
id,
|
||||
trigger: None,
|
||||
left_separator: None,
|
||||
right_separator: None,
|
||||
args: HashMap::new(),
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
);
|
||||
dispatch(detected_matches);
|
||||
|
||||
return Event::caused_by(event.source_id, EventType::NOOP);
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
77
espanso-engine/src/process/mod.rs
Normal file
77
espanso-engine/src/process/mod.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::Event;
|
||||
|
||||
mod default;
|
||||
mod middleware;
|
||||
|
||||
pub trait Middleware {
|
||||
fn name(&self) -> &'static str;
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event;
|
||||
}
|
||||
|
||||
pub trait Processor {
|
||||
fn process(&mut self, event: Event) -> Vec<Event>;
|
||||
}
|
||||
|
||||
// Dependency inversion entities
|
||||
|
||||
pub use middleware::action::{EventSequenceProvider, MatchInfoProvider};
|
||||
pub use middleware::delay_modifiers::ModifierStatusProvider;
|
||||
pub use middleware::disable::DisableOptions;
|
||||
pub use middleware::image_resolve::PathProvider;
|
||||
pub use middleware::match_select::{MatchFilter, MatchSelector};
|
||||
pub use middleware::matcher::{
|
||||
MatchResult, Matcher, MatcherEvent, MatcherMiddlewareConfigProvider,
|
||||
};
|
||||
pub use middleware::multiplex::Multiplexer;
|
||||
pub use middleware::render::{Renderer, RendererError};
|
||||
pub use middleware::search::MatchProvider;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
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,
|
||||
modifier_status_provider: &'a dyn ModifierStatusProvider,
|
||||
event_sequence_provider: &'a dyn EventSequenceProvider,
|
||||
path_provider: &'a dyn PathProvider,
|
||||
disable_options: DisableOptions,
|
||||
matcher_options_provider: &'a dyn MatcherMiddlewareConfigProvider,
|
||||
match_provider: &'a dyn MatchProvider,
|
||||
) -> impl Processor + 'a {
|
||||
default::DefaultProcessor::new(
|
||||
matchers,
|
||||
match_filter,
|
||||
match_selector,
|
||||
multiplexer,
|
||||
renderer,
|
||||
match_info_provider,
|
||||
modifier_status_provider,
|
||||
event_sequence_provider,
|
||||
path_provider,
|
||||
disable_options,
|
||||
matcher_options_provider,
|
||||
match_provider,
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user