diff --git a/espanso/src/cli/cmd.rs b/espanso/src/cli/cmd.rs new file mode 100644 index 0000000..1dcd462 --- /dev/null +++ b/espanso/src/cli/cmd.rs @@ -0,0 +1,72 @@ +/* + * 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 . + */ + +use std::path::Path; + +use crate::{ + ipc::{create_ipc_client_to_worker, IPCEvent}, + lock::acquire_worker_lock, +}; + +use super::{CliModule, CliModuleArgs}; +use anyhow::{bail, Result}; +use espanso_ipc::IPCClient; + +pub fn new() -> CliModule { + CliModule { + requires_paths: true, + subcommand: "cmd".to_string(), + entry: cmd_main, + ..Default::default() + } +} + +fn cmd_main(args: CliModuleArgs) -> i32 { + let cli_args = args.cli_args.expect("missing cli_args"); + let paths = args.paths.expect("missing paths"); + + let event = if cli_args.subcommand_matches("enable").is_some() { + IPCEvent::EnableRequest + } else if cli_args.subcommand_matches("disable").is_some() { + IPCEvent::DisableRequest + } else if cli_args.subcommand_matches("toggle").is_some() { + IPCEvent::ToggleRequest + } else if cli_args.subcommand_matches("search").is_some() { + IPCEvent::OpenSearchBar + } else { + eprintln!("unknown command, please run `espanso cmd --help` to see a list of valid ones."); + return 1; + }; + + if let Err(error) = send_event_to_worker(&paths.runtime, event) { + eprintln!("unable to send command, error: {:?}", error); + return 2; + } + + 0 +} + +fn send_event_to_worker(runtime_path: &Path, event: IPCEvent) -> Result<()> { + if acquire_worker_lock(runtime_path).is_some() { + bail!("Worker process is not running, please start Espanso first.") + } + + let mut client = create_ipc_client_to_worker(runtime_path)?; + client.send_async(event) +} diff --git a/espanso/src/cli/mod.rs b/espanso/src/cli/mod.rs index 6c3fb99..f7a3768 100644 --- a/espanso/src/cli/mod.rs +++ b/espanso/src/cli/mod.rs @@ -23,6 +23,7 @@ use clap::ArgMatches; use espanso_config::{config::ConfigStore, error::NonFatalErrorSet, matches::store::MatchStore}; use espanso_path::Paths; +pub mod cmd; pub mod daemon; pub mod edit; pub mod env_path; diff --git a/espanso/src/cli/worker/engine/funnel/ipc.rs b/espanso/src/cli/worker/engine/funnel/ipc.rs index 3efc97f..0708358 100644 --- a/espanso/src/cli/worker/engine/funnel/ipc.rs +++ b/espanso/src/cli/worker/engine/funnel/ipc.rs @@ -68,5 +68,12 @@ impl<'a> funnel::Source<'a> for IpcEventSource<'a> { } fn is_event_type_allowed(event: &EventType) -> bool { - matches!(event, EventType::MatchExecRequest(_)) + matches!( + event, + EventType::MatchExecRequest(_) + | EventType::ShowSearchBar + | EventType::DisableRequest + | EventType::EnableRequest + | EventType::ToggleRequest + ) } diff --git a/espanso/src/cli/worker/ipc.rs b/espanso/src/cli/worker/ipc.rs index 9c06c03..1ae545e 100644 --- a/espanso/src/cli/worker/ipc.rs +++ b/espanso/src/cli/worker/ipc.rs @@ -59,21 +59,17 @@ pub fn initialize_and_spawn( EventHandlerResponse::NoResponse } - IPCEvent::RequestMatchExpansion(payload) => { - if let Err(err) = - event_notify.send(EventType::MatchExecRequest(MatchExecRequestEvent { - trigger: payload.trigger, - args: payload.args, - })) - { - error!( - "experienced error while sending event signal from worker ipc handler: {}", - err - ); - } - - EventHandlerResponse::NoResponse - } + IPCEvent::DisableRequest => send_event(&event_notify, EventType::DisableRequest), + IPCEvent::EnableRequest => send_event(&event_notify, EventType::EnableRequest), + IPCEvent::ToggleRequest => send_event(&event_notify, EventType::ToggleRequest), + IPCEvent::OpenSearchBar => send_event(&event_notify, EventType::ShowSearchBar), + IPCEvent::RequestMatchExpansion(payload) => send_event( + &event_notify, + EventType::MatchExecRequest(MatchExecRequestEvent { + trigger: payload.trigger, + args: payload.args, + }), + ), #[allow(unreachable_patterns)] unexpected_event => { warn!( @@ -89,3 +85,17 @@ pub fn initialize_and_spawn( Ok(()) } + +fn send_event( + event_notify: &Sender, + event: EventType, +) -> EventHandlerResponse { + if let Err(err) = event_notify.send(event) { + error!( + "experienced error while sending event signal from worker ipc handler: {}", + err + ); + } + + EventHandlerResponse::NoResponse +} diff --git a/espanso/src/ipc.rs b/espanso/src/ipc.rs index 1e1f6b1..5e8d3b4 100644 --- a/espanso/src/ipc.rs +++ b/espanso/src/ipc.rs @@ -27,6 +27,11 @@ pub enum IPCEvent { Exit, ExitAllProcesses, + EnableRequest, + DisableRequest, + ToggleRequest, + OpenSearchBar, + RequestMatchExpansion(RequestMatchExpansionPayload), } diff --git a/espanso/src/main.rs b/espanso/src/main.rs index d066ab6..58cf84f 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -72,6 +72,7 @@ lazy_static! { cli::workaround::new(), cli::package::new(), cli::match_cli::new(), + cli::cmd::new(), ]; static ref ALIASES: Vec = vec![ CliAlias { @@ -220,17 +221,17 @@ fn main() { .subcommand(SubCommand::with_name("unregister").about("Remove 'espanso' command from PATH")) .about("Add or remove the 'espanso' command from the PATH"), ) - // .subcommand(SubCommand::with_name("cmd") - // .about("Send a command to the espanso daemon.") - // .subcommand(SubCommand::with_name("exit") - // .about("Terminate the daemon.")) - // .subcommand(SubCommand::with_name("enable") - // .about("Enable the espanso replacement engine.")) - // .subcommand(SubCommand::with_name("disable") - // .about("Disable the espanso replacement engine.")) - // .subcommand(SubCommand::with_name("toggle") - // .about("Toggle the status of the espanso replacement engine.")) - // ) + .subcommand(SubCommand::with_name("cmd") + .about("Send a command to the espanso daemon.") + .subcommand(SubCommand::with_name("enable") + .about("Enable expansions.")) + .subcommand(SubCommand::with_name("disable") + .about("Disable expansions.")) + .subcommand(SubCommand::with_name("toggle") + .about("Enable/Disable expansions.")) + .subcommand(SubCommand::with_name("search") + .about("Open the Espanso's search bar.")) + ) .subcommand(SubCommand::with_name("edit") .about("Shortcut to open the default text editor to edit config files") .arg(Arg::with_name("target_file") @@ -252,10 +253,6 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#)) .setting(AppSettings::Hidden) .about("Start the daemon without spawning a new process."), ) - // .subcommand(SubCommand::with_name("register") - // .about("MacOS and Linux only. Register espanso in the system daemon manager.")) - // .subcommand(SubCommand::with_name("unregister") - // .about("MacOS and Linux only. Unregister espanso from the system daemon manager.")) .subcommand(SubCommand::with_name("launcher").setting(AppSettings::Hidden)) .subcommand(SubCommand::with_name("log").about("Print the daemon logs.")) .subcommand( @@ -306,8 +303,6 @@ For example, specifying 'email' is equivalent to 'match/email.yml'."#)) ), ), ) - // .subcommand(SubCommand::with_name("status") - // .about("Check if the espanso daemon is running or not.")) .subcommand( SubCommand::with_name("path") .about("Prints all the espanso directory paths to easily locate configuration and matches.")