feat(core): early structure of the engine

This commit is contained in:
Federico Terzi 2021-04-01 22:13:56 +02:00
parent 5c912777c7
commit a4958ff352
22 changed files with 927 additions and 17 deletions

1
Cargo.lock generated
View File

@ -287,6 +287,7 @@ version = "1.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"crossbeam",
"espanso-clipboard", "espanso-clipboard",
"espanso-config", "espanso-config",
"espanso-detect", "espanso-detect",

View File

@ -29,3 +29,4 @@ anyhow = "1.0.38"
thiserror = "1.0.23" thiserror = "1.0.23"
clap = "2.33.3" clap = "2.33.3"
lazy_static = "1.4.0" lazy_static = "1.4.0"
crossbeam = "0.8.0"

View File

@ -23,6 +23,7 @@ use espanso_path::Paths;
pub mod log; pub mod log;
pub mod path; pub mod path;
pub mod worker;
pub struct CliModule { pub struct CliModule {
pub enable_logs: bool, pub enable_logs: bool,

View 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 crate::engine::{Engine, funnel};
use super::{CliModule, CliModuleArgs};
mod source;
pub fn new() -> CliModule {
#[allow(clippy::needless_update)]
CliModule {
requires_paths: true,
requires_config: true,
enable_logs: true,
subcommand: "worker".to_string(),
entry: worker_main,
..Default::default()
}
}
fn worker_main(args: CliModuleArgs) {
let funnel = funnel::default(vec![
Box::new(),
]);
let engine = Engine::new(funnel);
}

View File

@ -0,0 +1,41 @@
/*
* 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::{Receiver, Select, SelectedOperation};
use espanso_detect::event::InputEvent;
use crate::engine::{event::Event, funnel::Source};
pub struct DetectorSource {
receiver: Receiver<InputEvent>,
}
impl Source for DetectorSource {
fn register(&self, select: &Select) -> i32 {
todo!()
}
fn receive(&self, select: &SelectedOperation) -> Event {
todo!()
}
}
pub fn new() {
todo!()
}

View File

@ -0,0 +1,20 @@
/*
* 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 detect;

View File

@ -0,0 +1,45 @@
/*
* 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::{Dispatcher, Executor, TextInjector};
use super::Event;
pub struct DefaultDispatcher {
executors: Vec<Box<dyn Executor>>,
}
impl DefaultDispatcher {
pub fn new(text_injector: impl TextInjector + 'static) -> Self {
Self {
executors: vec![
Box::new(super::executor::text_inject::TextInjectExecutor::new(text_injector)),
]
}
}
}
impl Dispatcher for DefaultDispatcher {
fn dispatch(&self, event: Event) {
for executor in self.executors.iter() {
if executor.execute(&event) {
break
}
}
}
}

View File

@ -0,0 +1,20 @@
/*
* 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 text_inject;

View File

@ -0,0 +1,47 @@
/*
* 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::{Event, Executor, TextInjector};
use crate::engine::event::inject::TextInjectMode;
use log::error;
pub struct TextInjectExecutor<T: TextInjector> {
injector: T,
}
impl<T: TextInjector> TextInjectExecutor<T> {
pub fn new(injector: T) -> Self {
Self { injector }
}
}
impl<T: TextInjector> Executor for TextInjectExecutor<T> {
fn execute(&self, event: &Event) -> bool {
if let Event::TextInject(inject_event) = event {
if inject_event.mode == TextInjectMode::Keys {
if let Err(error) = self.injector.inject(&inject_event.text) {
error!("text injector reported an error: {:?}", error);
}
return true;
}
}
false
}
}

View File

@ -0,0 +1,42 @@
/*
* 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 super::Event;
mod executor;
mod default;
pub trait Executor {
fn execute(&self, event: &Event) -> bool;
}
pub trait Dispatcher {
fn dispatch(&self, event: Event);
}
pub trait TextInjector {
fn inject(&self, text: &str) -> Result<()>;
}
pub fn default(text_injector: impl TextInjector + 'static) -> impl Dispatcher {
default::DefaultDispatcher::new(
text_injector,
)
}

View File

@ -0,0 +1,30 @@
/*
* 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)]
pub struct TextInjectEvent {
pub text: String,
pub mode: TextInjectMode,
}
#[derive(Debug, PartialEq)]
pub enum TextInjectMode {
Keys,
Clipboard,
}

View File

@ -0,0 +1,95 @@
/*
* 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)]
pub enum Status {
Pressed,
Released,
}
#[derive(Debug, PartialEq)]
pub enum Variant {
Left,
Right,
}
#[derive(Debug, PartialEq)]
pub struct KeyboardEvent {
pub key: Key,
pub value: Option<String>,
pub status: Status,
pub variant: Option<Variant>,
}
#[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),
}

View File

@ -0,0 +1,32 @@
/*
* 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 results: Vec<MatchResult>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchResult {
pub id: i32,
pub trigger: String,
pub vars: HashMap<String, String>,
}

View 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 keyboard;
pub mod inject;
pub mod matches_detected;
#[derive(Debug)]
pub enum Event {
NOOP,
// Inputs
Keyboard(keyboard::KeyboardEvent),
// Internal
MatchesDetected(matches_detected::MatchesDetectedEvent),
// Effects
TextInject(inject::TextInjectEvent),
}

View 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 crossbeam::channel::Select;
use super::{Funnel, FunnelResult, Source};
pub struct DefaultFunnel {
sources: Vec<Box<dyn Source>>,
}
impl DefaultFunnel {
pub fn new(sources: Vec<Box<dyn Source>>) -> Self {
Self {
sources
}
}
}
impl Funnel for DefaultFunnel {
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(&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)
}
}

View 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 super::Event;
mod default;
pub trait Source {
fn register(&self, select: &Select) -> i32;
fn receive(&self, select: &SelectedOperation) -> Event;
}
pub trait Funnel {
fn receive(&self) -> FunnelResult;
}
pub enum FunnelResult {
Event(Event),
EndOfStream,
}
pub fn default(sources: Vec<Box<dyn Source>>) -> impl Funnel {
DefaultFunnel::new(sources)
}

68
espanso/src/engine/mod.rs Normal file
View File

@ -0,0 +1,68 @@
/*
* 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, trace};
use std::collections::VecDeque;
use self::{
dispatch::Dispatcher,
event::Event,
process::Processor,
funnel::{Funnel, FunnelResult},
};
pub mod dispatch;
pub mod event;
pub mod process;
pub mod funnel;
pub struct Engine<TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> {
funnel: TFunnel,
processor: TProcessor,
dispatcher: TDispatcher,
}
impl <TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> Engine<TFunnel, TProcessor, TDispatcher> {
pub fn new(funnel: TFunnel, processor: TProcessor, dispatcher: TDispatcher) -> Self {
Self {
funnel,
processor,
dispatcher,
}
}
pub fn run(&mut self) {
loop {
match self.funnel.receive() {
FunnelResult::Event(event) => {
trace!("received event from stream: {:?}", event);
let processed_events = self.processor.process(event);
for event in processed_events {
self.dispatcher.dispatch(event);
}
}
FunnelResult::EndOfStream => {
debug!("end of stream received");
break;
}
}
}
}
}

View 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::{Event, Matcher, Middleware, Processor, middleware::matcher::MatchMiddleware};
use std::collections::VecDeque;
pub struct DefaultProcessor {
event_queue: VecDeque<Event>,
middleware: Vec<Box<dyn Middleware>>,
}
impl DefaultProcessor {
pub fn new<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> Self {
Self {
event_queue: VecDeque::new(),
middleware: vec![
Box::new(MatchMiddleware::new(matchers)),
]
}
}
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 dispatch = |event: Event| {
// TODO: add tracing information
current_queue.push_front(event);
};
for middleware in self.middleware.iter() {
// TODO: add tracing information
current_event = middleware.next(current_event, &dispatch);
}
while let Some(event) = current_queue.pop_back() {
self.event_queue.push_front(event);
}
Some(current_event)
} else {
None
}
}
}
impl Processor for DefaultProcessor {
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
}
}

View File

@ -0,0 +1,133 @@
/*
* 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::VecDeque};
use super::super::Middleware;
use crate::engine::{event::{Event, keyboard::{Key, Status}, matches_detected::{MatchResult, MatchesDetectedEvent}}, process::{Matcher, MatcherEvent}};
const MAX_HISTORY: usize = 3; // TODO: get as parameter
pub struct MatchMiddleware<State> {
matchers: Vec<Box<dyn Matcher<State>>>,
matcher_states: RefCell<VecDeque<Vec<State>>>,
}
impl<State> MatchMiddleware<State> {
pub fn new(matchers: Vec<Box<dyn Matcher<State>>>) -> Self {
Self {
matchers,
matcher_states: RefCell::new(VecDeque::new()),
}
}
}
impl<State> Middleware for MatchMiddleware<State> {
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
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 Event::Keyboard(keyboard_event) = &event {
// 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) prevent espanso from building
// an accurate key buffer, so we need to invalidate it.
if is_invalidating_key(&keyboard_event.key) {
matcher_states.clear();
return event;
}
}
// TODO: test if the matcher detects a word match when the states are cleared (probably not :( )
let mut all_results = Vec::new();
if let Some(matcher_event) = convert_to_matcher_event(&event) {
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() > MAX_HISTORY {
matcher_states.pop_front();
}
if !all_results.is_empty() {
return Event::MatchesDetected(MatchesDetectedEvent {
results: all_results.into_iter().map(|result | {
MatchResult {
id: result.id,
trigger: result.trigger,
vars: result.vars,
}
}).collect()
})
}
}
event
}
}
fn convert_to_matcher_event(event: &Event) -> Option<MatcherEvent> {
if let Event::Keyboard(keyboard_event) = event {
if keyboard_event.status == Status::Pressed {
return Some(MatcherEvent::Key {
key: keyboard_event.key.clone(),
chars: keyboard_event.value.clone(),
});
}
}
// TODO: mouse event should act as separator
None
}
fn is_invalidating_key(key: &Key) -> bool {
match key {
Key::ArrowDown => true,
Key::ArrowLeft => true,
Key::ArrowRight => true,
Key::ArrowUp => true,
Key::End => true,
Key::Home => true,
Key::PageDown => true,
Key::PageUp => true,
Key::Escape => true,
_ => false,
}
}

View File

@ -0,0 +1,20 @@
/*
* 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 matcher;

View 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 std::collections::HashMap;
use super::{Event, event::keyboard::Key};
mod middleware;
mod default;
pub trait Middleware {
fn next(&self, event: Event, dispatch: &dyn FnMut(Event)) -> Event;
}
pub trait Processor {
fn process(&mut self, event: Event) -> Vec<Event>;
}
// Dependency inversion entities
pub trait Matcher<State> {
fn process(&self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec<MatchResult>);
}
pub enum MatcherEvent {
Key { key: Key, chars: Option<String> },
VirtualSeparator,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchResult {
id: i32,
trigger: String,
vars: HashMap<String, String>,
}
pub fn default<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> impl Processor {
default::DefaultProcessor::new(matchers)
}

View File

@ -28,7 +28,6 @@ use simplelog::{CombinedLogger, ConfigBuilder, LevelFilter, TermLogger, Terminal
mod cli; mod cli;
mod engine; mod engine;
mod logging; mod logging;
mod util;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
const LOG_FILE_NAME: &str = "espanso.log"; const LOG_FILE_NAME: &str = "espanso.log";
@ -37,6 +36,7 @@ lazy_static! {
static ref CLI_HANDLERS: Vec<CliModule> = vec![ static ref CLI_HANDLERS: Vec<CliModule> = vec![
cli::path::new(), cli::path::new(),
cli::log::new(), cli::log::new(),
cli::worker::new(),
]; ];
} }
@ -170,19 +170,19 @@ fn main() {
// ) // )
// ) // )
// Package manager // Package manager
.subcommand(SubCommand::with_name("package") // .subcommand(SubCommand::with_name("package")
.about("Espanso package manager commands") // .about("Espanso package manager commands")
.subcommand(install_subcommand.clone()) // .subcommand(install_subcommand.clone())
.subcommand(uninstall_subcommand.clone()) // .subcommand(uninstall_subcommand.clone())
.subcommand(SubCommand::with_name("list") // .subcommand(SubCommand::with_name("list")
.about("List all installed packages") // .about("List all installed packages")
.arg(Arg::with_name("full") // .arg(Arg::with_name("full")
.help("Print all package info") // .help("Print all package info")
.long("full"))) // .long("full")))
.subcommand(SubCommand::with_name("refresh") // .subcommand(SubCommand::with_name("refresh")
.about("Update espanso package index")) // .about("Update espanso package index"))
) // )
.subcommand(SubCommand::with_name("worker") .subcommand(SubCommand::with_name("worker")
.setting(AppSettings::Hidden) .setting(AppSettings::Hidden)
.arg(Arg::with_name("reload") .arg(Arg::with_name("reload")
@ -190,9 +190,9 @@ fn main() {
.long("reload") .long("reload")
.required(false) .required(false)
.takes_value(false)) .takes_value(false))
) );
.subcommand(install_subcommand) // .subcommand(install_subcommand)
.subcommand(uninstall_subcommand); // .subcommand(uninstall_subcommand);
// TODO: explain that the start and restart commands are only meaningful // TODO: explain that the start and restart commands are only meaningful
// when using the system daemon manager on macOS and Linux // when using the system daemon manager on macOS and Linux