feat(core): early structure of the engine
This commit is contained in:
parent
5c912777c7
commit
a4958ff352
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -287,6 +287,7 @@ version = "1.0.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"crossbeam",
|
||||
"espanso-clipboard",
|
||||
"espanso-config",
|
||||
"espanso-detect",
|
||||
|
|
|
@ -28,4 +28,5 @@ log = "0.4.14"
|
|||
anyhow = "1.0.38"
|
||||
thiserror = "1.0.23"
|
||||
clap = "2.33.3"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
crossbeam = "0.8.0"
|
|
@ -23,6 +23,7 @@ use espanso_path::Paths;
|
|||
|
||||
pub mod log;
|
||||
pub mod path;
|
||||
pub mod worker;
|
||||
|
||||
pub struct CliModule {
|
||||
pub enable_logs: bool,
|
||||
|
|
44
espanso/src/cli/worker/mod.rs
Normal file
44
espanso/src/cli/worker/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 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);
|
||||
}
|
||||
|
41
espanso/src/cli/worker/source/detect.rs
Normal file
41
espanso/src/cli/worker/source/detect.rs
Normal 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!()
|
||||
}
|
20
espanso/src/cli/worker/source/mod.rs
Normal file
20
espanso/src/cli/worker/source/mod.rs
Normal 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;
|
45
espanso/src/engine/dispatch/default.rs
Normal file
45
espanso/src/engine/dispatch/default.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
espanso/src/engine/dispatch/executor/mod.rs
Normal file
20
espanso/src/engine/dispatch/executor/mod.rs
Normal 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;
|
47
espanso/src/engine/dispatch/executor/text_inject.rs
Normal file
47
espanso/src/engine/dispatch/executor/text_inject.rs
Normal 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
|
||||
}
|
||||
}
|
42
espanso/src/engine/dispatch/mod.rs
Normal file
42
espanso/src/engine/dispatch/mod.rs
Normal 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,
|
||||
)
|
||||
}
|
30
espanso/src/engine/event/inject.rs
Normal file
30
espanso/src/engine/event/inject.rs
Normal 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,
|
||||
}
|
95
espanso/src/engine/event/keyboard.rs
Normal file
95
espanso/src/engine/event/keyboard.rs
Normal 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),
|
||||
}
|
32
espanso/src/engine/event/matches_detected.rs
Normal file
32
espanso/src/engine/event/matches_detected.rs
Normal 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>,
|
||||
}
|
36
espanso/src/engine/event/mod.rs
Normal file
36
espanso/src/engine/event/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 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),
|
||||
}
|
56
espanso/src/engine/funnel/default.rs
Normal file
56
espanso/src/engine/funnel/default.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 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)
|
||||
}
|
||||
}
|
44
espanso/src/engine/funnel/mod.rs
Normal file
44
espanso/src/engine/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 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
68
espanso/src/engine/mod.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
78
espanso/src/engine/process/default.rs
Normal file
78
espanso/src/engine/process/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::{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
|
||||
}
|
||||
}
|
133
espanso/src/engine/process/middleware/matcher.rs
Normal file
133
espanso/src/engine/process/middleware/matcher.rs
Normal 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,
|
||||
}
|
||||
}
|
20
espanso/src/engine/process/middleware/mod.rs
Normal file
20
espanso/src/engine/process/middleware/mod.rs
Normal 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;
|
56
espanso/src/engine/process/mod.rs
Normal file
56
espanso/src/engine/process/mod.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 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)
|
||||
}
|
|
@ -28,7 +28,6 @@ use simplelog::{CombinedLogger, ConfigBuilder, LevelFilter, TermLogger, Terminal
|
|||
mod cli;
|
||||
mod engine;
|
||||
mod logging;
|
||||
mod util;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const LOG_FILE_NAME: &str = "espanso.log";
|
||||
|
@ -37,6 +36,7 @@ lazy_static! {
|
|||
static ref CLI_HANDLERS: Vec<CliModule> = vec![
|
||||
cli::path::new(),
|
||||
cli::log::new(),
|
||||
cli::worker::new(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -170,19 +170,19 @@ fn main() {
|
|||
// )
|
||||
// )
|
||||
// Package manager
|
||||
.subcommand(SubCommand::with_name("package")
|
||||
.about("Espanso package manager commands")
|
||||
.subcommand(install_subcommand.clone())
|
||||
.subcommand(uninstall_subcommand.clone())
|
||||
.subcommand(SubCommand::with_name("list")
|
||||
.about("List all installed packages")
|
||||
.arg(Arg::with_name("full")
|
||||
.help("Print all package info")
|
||||
.long("full")))
|
||||
// .subcommand(SubCommand::with_name("package")
|
||||
// .about("Espanso package manager commands")
|
||||
// .subcommand(install_subcommand.clone())
|
||||
// .subcommand(uninstall_subcommand.clone())
|
||||
// .subcommand(SubCommand::with_name("list")
|
||||
// .about("List all installed packages")
|
||||
// .arg(Arg::with_name("full")
|
||||
// .help("Print all package info")
|
||||
// .long("full")))
|
||||
|
||||
.subcommand(SubCommand::with_name("refresh")
|
||||
.about("Update espanso package index"))
|
||||
)
|
||||
// .subcommand(SubCommand::with_name("refresh")
|
||||
// .about("Update espanso package index"))
|
||||
// )
|
||||
.subcommand(SubCommand::with_name("worker")
|
||||
.setting(AppSettings::Hidden)
|
||||
.arg(Arg::with_name("reload")
|
||||
|
@ -190,9 +190,9 @@ fn main() {
|
|||
.long("reload")
|
||||
.required(false)
|
||||
.takes_value(false))
|
||||
)
|
||||
.subcommand(install_subcommand)
|
||||
.subcommand(uninstall_subcommand);
|
||||
);
|
||||
// .subcommand(install_subcommand)
|
||||
// .subcommand(uninstall_subcommand);
|
||||
|
||||
// TODO: explain that the start and restart commands are only meaningful
|
||||
// when using the system daemon manager on macOS and Linux
|
||||
|
|
Loading…
Reference in New Issue
Block a user