From b87e32e91da9405cc6b07b946cd1433d83b020eb Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Sun, 14 Jun 2020 18:01:35 +0200
Subject: [PATCH 01/11] Update WSL call to use bash instead of wsl command

---
 src/extension/shell.rs | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/extension/shell.rs b/src/extension/shell.rs
index 7ffcffa..09ec1ad 100644
--- a/src/extension/shell.rs
+++ b/src/extension/shell.rs
@@ -34,6 +34,7 @@ pub enum Shell {
     Cmd,
     Powershell,
     WSL,
+    WSL2,
     Bash,
     Sh,
 }
@@ -52,6 +53,11 @@ impl Shell {
                 command
             },
             Shell::WSL => {
+                let mut command = Command::new("bash");
+                command.args(&["-c", &cmd]);
+                command
+            },
+            Shell::WSL2 => {
                 let mut command = Command::new("wsl");
                 command.args(&["bash", "-c", &cmd]);
                 command
@@ -79,6 +85,7 @@ impl Shell {
             "cmd" => Some(Shell::Cmd),
             "powershell" => Some(Shell::Powershell),
             "wsl" => Some(Shell::WSL),
+            "wsl2" => Some(Shell::WSL2),
             "bash" => Some(Shell::Bash),
             "sh" => Some(Shell::Sh),
             _ => None,

From adbf1fe432a7104902f53e288e044422f6b982cc Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 22 Jun 2020 18:23:23 +0200
Subject: [PATCH 02/11] Version bump 0.6.3

---
 Cargo.lock     | 2 +-
 Cargo.toml     | 2 +-
 snapcraft.yaml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 6e97ecb..81fdede 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -371,7 +371,7 @@ dependencies = [
 
 [[package]]
 name = "espanso"
-version = "0.6.2"
+version = "0.6.3"
 dependencies = [
  "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index 8824c5a..0ed1f99 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "espanso"
-version = "0.6.2"
+version = "0.6.3"
 authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
 license = "GPL-3.0"
 description = "Cross-platform Text Expander written in Rust"
diff --git a/snapcraft.yaml b/snapcraft.yaml
index 275a752..4d783d1 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -1,5 +1,5 @@
 name: espanso
-version: 0.6.2
+version: 0.6.3
 summary: A Cross-platform Text Expander written in Rust
 description: |
   espanso is a Cross-platform, Text Expander written in Rust.

From 34c0a524553ca18b55f45933169001037ea67eb7 Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 22 Jun 2020 18:31:30 +0200
Subject: [PATCH 03/11] Remove trailing .git from repository url. Fix #326

---
 src/main.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index 5f394df..90cdbcd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1027,7 +1027,13 @@ fn install_main(_config_set: ConfigSet, matches: &ArgMatches) {
         exit(1);
     });
 
-    let repository = matches.value_of("repository_url").unwrap_or("hub");
+    let mut repository = matches.value_of("repository_url").unwrap_or("hub");
+
+    // Remove trailing .git string if present
+    // See: https://github.com/federico-terzi/espanso/issues/326
+    if repository.ends_with(".git") {
+        repository = repository.trim_end_matches(".git")
+    }
 
     let package_resolver = Box::new(ZipPackageResolver::new());
 

From 6766d91af32bb7265ba4362317ed497cf523412a Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 22 Jun 2020 19:09:51 +0200
Subject: [PATCH 04/11] Prevent espanso crash when an X11 exception occurs. Fix
 #312

---
 native/liblinuxbridge/bridge.cpp | 14 ++++++++++++++
 native/liblinuxbridge/bridge.h   | 12 +++++++++++-
 src/bridge/linux.rs              |  3 +++
 src/context/linux.rs             | 12 +++++++++++-
 4 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/native/liblinuxbridge/bridge.cpp b/native/liblinuxbridge/bridge.cpp
index 99f13e9..ec31d09 100644
--- a/native/liblinuxbridge/bridge.cpp
+++ b/native/liblinuxbridge/bridge.cpp
@@ -77,14 +77,20 @@ xdo_t * xdo_context;
 
 // Callback invoked when a new key event occur.
 void event_callback (XPointer, XRecordInterceptData*);
+int error_callback(Display *display, XErrorEvent *error);
 
 KeypressCallback keypress_callback;
+X11ErrorCallback x11_error_callback;
 void * context_instance;
 
 void register_keypress_callback(KeypressCallback callback) {
     keypress_callback = callback;
 }
 
+void register_error_callback(X11ErrorCallback callback) {
+    x11_error_callback = callback;
+}
+
 int32_t check_x11() {
     Display *check_disp = XOpenDisplay(NULL);
 
@@ -156,6 +162,9 @@ int32_t initialize(void * _context_instance) {
 
     xdo_context = xdo_new(NULL);
 
+    // Setup a custom error handler
+    XSetErrorHandler(&error_callback);
+
     /**
      * Note: We might never get a MappingNotify event if the
      * modifier and keymap information was never cached in Xlib.
@@ -272,6 +281,11 @@ void event_callback(XPointer p, XRecordInterceptData *hook)
     XRecordFreeData(hook);
 }
 
+int error_callback(Display *display, XErrorEvent *error) {
+    x11_error_callback(context_instance, error->error_code, error->request_code, error->minor_code);
+    return 0;
+}
+
 void release_all_keys() {
     char keys[32];
     XQueryKeymap(xdo_context->xdpy, keys);  // Get the current status of the keyboard
diff --git a/native/liblinuxbridge/bridge.h b/native/liblinuxbridge/bridge.h
index 5e2c359..5102d0c 100644
--- a/native/liblinuxbridge/bridge.h
+++ b/native/liblinuxbridge/bridge.h
@@ -49,7 +49,6 @@ extern "C" void cleanup();
  * while the second is the size of the array.
  */
 typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t event_type, int32_t key_code);
-
 extern KeypressCallback keypress_callback;
 
 /*
@@ -57,6 +56,17 @@ extern KeypressCallback keypress_callback;
  */
 extern "C" void register_keypress_callback(KeypressCallback callback);
 
+/*
+ * Called when a X11 error occurs
+ */
+typedef void (*X11ErrorCallback)(void * self, char error_code, char request_code, char minor_code);
+extern X11ErrorCallback x11_error_callback;
+
+/*
+ * Register the callback that will be called when an X11 error occurs
+ */
+extern "C" void register_error_callback(X11ErrorCallback callback);
+
 /*
  * Type the given string by simulating Key Presses
  */
diff --git a/src/bridge/linux.rs b/src/bridge/linux.rs
index ef04d27..d7ceede 100644
--- a/src/bridge/linux.rs
+++ b/src/bridge/linux.rs
@@ -32,6 +32,9 @@ extern "C" {
     pub fn get_active_window_class(buffer: *mut c_char, size: i32) -> i32;
     pub fn get_active_window_executable(buffer: *mut c_char, size: i32) -> i32;
     pub fn is_current_window_special() -> i32;
+    pub fn register_error_callback(
+        cb: extern "C" fn(_self: *mut c_void, error_code: c_char, request_code: c_char, minor_code: c_char),
+    );
 
     // Keyboard
     pub fn register_keypress_callback(
diff --git a/src/context/linux.rs b/src/context/linux.rs
index 6a4ddb1..c1acae9 100644
--- a/src/context/linux.rs
+++ b/src/context/linux.rs
@@ -21,7 +21,7 @@ use crate::bridge::linux::*;
 use crate::config::Configs;
 use crate::event::KeyModifier::*;
 use crate::event::*;
-use log::{debug, error};
+use log::{debug, error, warn};
 use std::ffi::CStr;
 use std::os::raw::{c_char, c_void};
 use std::process::exit;
@@ -59,6 +59,7 @@ impl LinuxContext {
             let context_ptr = &*context as *const LinuxContext as *const c_void;
 
             register_keypress_callback(keypress_callback);
+            register_error_callback(error_callback);
 
             let res = initialize(context_ptr);
             if res <= 0 {
@@ -155,3 +156,12 @@ extern "C" fn keypress_callback(
         }
     }
 }
+
+extern "C" fn error_callback(
+    _self: *mut c_void,
+    error_code: c_char,
+    request_code: c_char,
+    minor_code: c_char,
+) {
+    warn!("X11 reported an error code: {}, request_code: {} and minor_code: {}", error_code, request_code, minor_code);
+}

From 968ef578c1d4699d501d7987fa34b1ca104f3a9c Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 22 Jun 2020 21:21:35 +0200
Subject: [PATCH 05/11] Add CLI option to list matches and exec a trigger. Fix
 #263

---
 src/cli.rs          | 84 +++++++++++++++++++++++++++++++++++++++++++++
 src/engine.rs       | 59 +++++++++++++++++++++++--------
 src/event/mod.rs    |  3 ++
 src/main.rs         | 57 ++++++++++++++++++++++++++++++
 src/protocol/mod.rs | 14 ++++++++
 5 files changed, 202 insertions(+), 15 deletions(-)
 create mode 100644 src/cli.rs

diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..601b033
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,84 @@
+/*
+ * This file is part of espanso.
+ *
+ * Copyright (C) 2020 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 serde::Serialize;
+use crate::config::ConfigSet;
+use crate::matcher::{Match, MatchContentType};
+
+pub fn list_matches(config_set: ConfigSet, onlytriggers: bool) {
+    let matches = filter_matches(config_set);
+
+    for m in matches {
+        for trigger in m.triggers.iter() {
+            if onlytriggers {
+                println!("{}", trigger);
+            }else {
+                match m.content {
+                    MatchContentType::Text(ref text) => {
+                        println!("{} - {}", trigger, text.replace)
+                    },
+                    MatchContentType::Image(_) => {
+                        // Skip image matches for now
+                    },
+                }
+            }
+        }
+    }
+}
+
+#[derive(Debug, Serialize)]
+struct JsonMatchEntry {
+    triggers: Vec<String>,
+    replace: String,
+}
+
+pub fn list_matches_as_json(config_set: ConfigSet) {
+    let matches = filter_matches(config_set);
+
+    let mut entries = Vec::new();
+
+    for m in matches {
+        match m.content {
+            MatchContentType::Text(ref text) => {
+                entries.push(JsonMatchEntry {
+                    triggers: m.triggers,
+                    replace: text.replace.clone(),
+                })
+            },
+            MatchContentType::Image(_) => {
+                // Skip image matches for now
+            },
+        }
+    }
+
+    let output = serde_json::to_string(&entries);
+
+    println!("{}", output.unwrap_or_default())
+}
+
+fn filter_matches(config_set: ConfigSet) -> Vec<Match> {
+    let mut output = Vec::new();
+    output.extend(config_set.default.matches);
+
+    // TODO: consider specific matches by class, title or exe path
+//    for specific in config_set.specific {
+//        output.extend(specific.matches)
+//    }
+    output
+}
\ No newline at end of file
diff --git a/src/engine.rs b/src/engine.rs
index f349847..2a4d060 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -132,22 +132,20 @@ impl<
             None
         }
     }
-}
 
-lazy_static! {
-    static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(?P<name>\\w+)\\s*\\}\\}").unwrap();
-}
+    fn find_match_by_trigger(&self, trigger: &str) -> Option<Match> {
+        let config = self.config_manager.active_config();
 
-impl<
-        'a,
-        S: KeyboardManager,
-        C: ClipboardManager,
-        M: ConfigManager<'a>,
-        U: UIManager,
-        R: Renderer,
-    > MatchReceiver for Engine<'a, S, C, M, U, R>
-{
-    fn on_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize) {
+        if let Some(m) = config.matches.iter().find(|m|
+            m.triggers.iter().any(|t| t == trigger)
+        ) {
+            Some(m.clone())
+        }else{
+            None
+        }
+    }
+
+    fn inject_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize, skip_delete: bool) {
         let config = self.config_manager.active_config();
 
         if !config.enable_active {
@@ -163,7 +161,9 @@ impl<
             m.triggers[trigger_offset].chars().count() as i32 + 1 // Count also the separator
         };
 
-        self.keyboard_manager.delete_string(&config, char_count);
+        if !skip_delete {
+            self.keyboard_manager.delete_string(&config, char_count);
+        }
 
         let mut previous_clipboard_content: Option<String> = None;
 
@@ -287,6 +287,24 @@ impl<
         // Re-allow espanso to interpret actions
         self.is_injecting.store(false, Release);
     }
+}
+
+lazy_static! {
+    static ref VAR_REGEX: Regex = Regex::new("\\{\\{\\s*(?P<name>\\w+)\\s*\\}\\}").unwrap();
+}
+
+impl<
+        'a,
+        S: KeyboardManager,
+        C: ClipboardManager,
+        M: ConfigManager<'a>,
+        U: UIManager,
+        R: Renderer,
+    > MatchReceiver for Engine<'a, S, C, M, U, R>
+{
+    fn on_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize) {
+        self.inject_match(m, trailing_separator, trigger_offset, false);
+    }
 
     fn on_enable_update(&self, status: bool) {
         let message = if status {
@@ -432,6 +450,17 @@ impl<
                     self.ui_manager.notify(&message);
                 }
             }
+            SystemEvent::Trigger(trigger) => {
+                let m = self.find_match_by_trigger(&trigger);
+                match m {
+                    Some(m) => {
+                        self.inject_match(&m, None, 0, true);
+                    },
+                    None => {
+                        warn!("No match found with trigger: {}", trigger)
+                    },
+                }
+            }
         }
     }
 }
diff --git a/src/event/mod.rs b/src/event/mod.rs
index 600c5b6..a85f317 100644
--- a/src/event/mod.rs
+++ b/src/event/mod.rs
@@ -130,6 +130,9 @@ pub enum SystemEvent {
 
     // Notification
     NotifyRequest(String),
+
+    // Trigger an expansion from IPC
+    Trigger(String),
 }
 
 // Receivers
diff --git a/src/main.rs b/src/main.rs
index 90cdbcd..f7b3b18 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -69,6 +69,7 @@ mod render;
 mod sysdaemon;
 mod system;
 mod ui;
+mod cli;
 mod utils;
 
 const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -158,6 +159,32 @@ fn main() {
             .subcommand(SubCommand::with_name("default")
                 .about("Print the default configuration file path."))
         )
+        .subcommand(SubCommand::with_name("match")
+            .about("List and execute matches from the CLI")
+            .subcommand(SubCommand::with_name("list")
+                .about("Print all matches to standard output")
+                .arg(Arg::with_name("json")
+                    .short("j")
+                    .long("json")
+                    .help("Return the matches as json")
+                    .required(false)
+                    .takes_value(false)
+                )
+                .arg(Arg::with_name("onlytriggers")
+                    .short("t")
+                    .long("onlytriggers")
+                    .help("Print only triggers without replacement")
+                    .required(false)
+                    .takes_value(false)
+                )
+            )
+            .subcommand(SubCommand::with_name("exec")
+                .about("Triggers the expansion of the given match")
+                .arg(Arg::with_name("trigger")
+                    .help("The trigger of the match to be expanded")
+                )
+            )
+        )
         // Package manager
         .subcommand(SubCommand::with_name("package")
             .about("Espanso package manager commands")
@@ -274,6 +301,11 @@ fn main() {
         return;
     }
 
+    if let Some(matches) = matches.subcommand_matches("match") {
+        match_main(config_set, matches);
+        return;
+    }
+
     if let Some(matches) = matches.subcommand_matches("package") {
         if let Some(matches) = matches.subcommand_matches("install") {
             install_main(config_set, matches);
@@ -1229,6 +1261,31 @@ fn path_main(_config_set: ConfigSet, matches: &ArgMatches) {
     }
 }
 
+
+fn match_main(config_set: ConfigSet, matches: &ArgMatches) {
+    if let Some(matches) = matches.subcommand_matches("list") {
+        let json = matches.is_present("json");
+        let onlytriggers = matches.is_present("onlytriggers");
+
+        if !json {
+            crate::cli::list_matches(config_set, onlytriggers);
+        }else{
+            crate::cli::list_matches_as_json(config_set);
+        }
+    }else if let Some(matches) = matches.subcommand_matches("exec") {
+        let trigger = matches.value_of("trigger").unwrap_or_else(|| {
+            eprintln!("missing trigger");
+            exit(1);
+        });
+
+        send_command_or_warn(
+            Service::Worker,
+            config_set.default.clone(),
+            IPCCommand::trigger(trigger),
+        );
+    }
+}
+
 fn edit_main(matches: &ArgMatches) {
     // Determine which is the file to edit
     let config = matches.value_of("config").unwrap_or("default");
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index dae371b..d719dac 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -67,6 +67,9 @@ impl IPCCommand {
             "notify" => Some(Event::System(SystemEvent::NotifyRequest(
                 self.payload.clone(),
             ))),
+            "trigger" => Some(Event::System(SystemEvent::Trigger(
+                self.payload.clone(),
+            ))),
             _ => None,
         }
     }
@@ -101,6 +104,10 @@ impl IPCCommand {
                 id: "notify".to_owned(),
                 payload: message,
             }),
+            Event::System(SystemEvent::Trigger(trigger)) => Some(IPCCommand {
+                id: "trigger".to_owned(),
+                payload: trigger,
+            }),
             _ => None,
         }
     }
@@ -125,6 +132,13 @@ impl IPCCommand {
             payload: "".to_owned(),
         }
     }
+
+    pub fn trigger(trigger: &str) -> IPCCommand {
+        Self {
+            id: "trigger".to_owned(),
+            payload: trigger.to_owned(),
+        }
+    }
 }
 
 fn process_event<R: Read, E: Error>(event_channel: &Sender<Event>, stream: Result<R, E>) {

From 32d7dbc88ed8f27f4a1730c416f0a38a410bf0aa Mon Sep 17 00:00:00 2001
From: Federico Terzi <federicoterzi96@gmail.com>
Date: Mon, 22 Jun 2020 21:42:50 +0200
Subject: [PATCH 06/11] Add option to preserve newlines in match list

---
 src/cli.rs  |  9 +++++++--
 src/main.rs | 10 +++++++++-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/cli.rs b/src/cli.rs
index 601b033..645d64d 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -21,7 +21,7 @@ use serde::Serialize;
 use crate::config::ConfigSet;
 use crate::matcher::{Match, MatchContentType};
 
-pub fn list_matches(config_set: ConfigSet, onlytriggers: bool) {
+pub fn list_matches(config_set: ConfigSet, onlytriggers: bool, preserve_newlines: bool) {
     let matches = filter_matches(config_set);
 
     for m in matches {
@@ -31,7 +31,12 @@ pub fn list_matches(config_set: ConfigSet, onlytriggers: bool) {
             }else {
                 match m.content {
                     MatchContentType::Text(ref text) => {
-                        println!("{} - {}", trigger, text.replace)
+                        let replace = if preserve_newlines {
+                            text.replace.to_owned()
+                        }else{
+                            text.replace.replace("\n", " ")
+                        };
+                        println!("{} - {}", trigger, replace)
                     },
                     MatchContentType::Image(_) => {
                         // Skip image matches for now
diff --git a/src/main.rs b/src/main.rs
index f7b3b18..3becc92 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -177,6 +177,13 @@ fn main() {
                     .required(false)
                     .takes_value(false)
                 )
+                .arg(Arg::with_name("preservenewlines")
+                    .short("n")
+                    .long("preservenewlines")
+                    .help("Preserve newlines when printing replacements")
+                    .required(false)
+                    .takes_value(false)
+                )
             )
             .subcommand(SubCommand::with_name("exec")
                 .about("Triggers the expansion of the given match")
@@ -1266,9 +1273,10 @@ fn match_main(config_set: ConfigSet, matches: &ArgMatches) {
     if let Some(matches) = matches.subcommand_matches("list") {
         let json = matches.is_present("json");
         let onlytriggers = matches.is_present("onlytriggers");
+        let preserve_newlines = matches.is_present("preservenewlines");
 
         if !json {
-            crate::cli::list_matches(config_set, onlytriggers);
+            crate::cli::list_matches(config_set, onlytriggers, preserve_newlines);
         }else{
             crate::cli::list_matches_as_json(config_set);
         }

From bb2cc41c4d11e64ef516ad99cf3272223608dc2a Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Wed, 24 Jun 2020 20:32:21 +0200
Subject: [PATCH 07/11] Include VC redist check in CI pipeline. Fix #336

---
 packager.py | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/packager.py b/packager.py
index 358f1f9..1f2c371 100644
--- a/packager.py
+++ b/packager.py
@@ -82,11 +82,24 @@ def build_windows(package_info):
     msvc_dirs = glob.glob("C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\*\\VC\\Redist\\MSVC\\*")
     print("Found Redists: ", msvc_dirs)
 
+    print("Determining best redist...")
+
     if len(msvc_dirs) == 0:
         raise Exception("Cannot find redistributable dlls")
 
-    msvc_dir = msvc_dirs[-1]  # Take the most recent version of the toolchain
-    print("Using: ",msvc_dir)
+    msvc_dir = None
+
+    for curr_dir in msvc_dirs:
+        dll_files = glob.glob(curr_dir + "\\x64\\*CRT\\*.dll")
+        print("Found dlls", dll_files, "in", curr_dir)
+        if any("vcruntime140_1.dll" in x.lower() for x in dll_files):
+            msvc_dir = curr_dir
+            break
+
+    if msvc_dir is None:
+        raise Exception("Cannot find redist with VCRUNTIME140_1.dll")
+
+    print("Using: ", msvc_dir)
 
     dll_files = glob.glob(msvc_dir + "\\x64\\*CRT\\*.dll")
 

From 889e2b8f8c438d2939273a8570208b7b2130537c Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Wed, 24 Jun 2020 21:23:03 +0200
Subject: [PATCH 08/11] Refactor Windows IPC to use named pipes instead of
 localhost

---
 Cargo.lock              | 10 +++++++++
 Cargo.toml              |  3 +++
 src/protocol/mod.rs     |  8 +++----
 src/protocol/windows.rs | 49 +++++++++++++++++++++++------------------
 4 files changed, 44 insertions(+), 26 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 81fdede..9a891de 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -384,6 +384,7 @@ dependencies = [
  "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "named_pipe 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -802,6 +803,14 @@ dependencies = [
  "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "named_pipe"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "native-tls"
 version = "0.2.3"
@@ -1911,6 +1920,7 @@ dependencies = [
 "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23"
 "checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum named_pipe 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b"
 "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
 "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
 "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
diff --git a/Cargo.toml b/Cargo.toml
index 0ed1f99..6e49975 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,9 @@ notify = "4.0.13"
 libc = "0.2.62"
 signal-hook = "0.1.15"
 
+[target.'cfg(windows)'.dependencies]
+named_pipe = "0.4.1"
+
 [build-dependencies]
 cmake = "0.1.31"
 
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index d719dac..563e560 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -214,13 +214,13 @@ pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
 #[cfg(target_os = "windows")]
 pub fn get_ipc_server(
     service: Service,
-    config: Configs,
+    _: Configs,
     event_channel: Sender<Event>,
 ) -> impl IPCServer {
-    windows::WindowsIPCServer::new(service, config, event_channel)
+    windows::WindowsIPCServer::new(service, event_channel)
 }
 
 #[cfg(target_os = "windows")]
-pub fn get_ipc_client(service: Service, config: Configs) -> impl IPCClient {
-    windows::WindowsIPCClient::new(service, config)
+pub fn get_ipc_client(service: Service, _: Configs) -> impl IPCClient {
+    windows::WindowsIPCClient::new(service)
 }
diff --git a/src/protocol/windows.rs b/src/protocol/windows.rs
index 67b2125..9cf239c 100644
--- a/src/protocol/windows.rs
+++ b/src/protocol/windows.rs
@@ -25,30 +25,35 @@ use std::sync::mpsc::Sender;
 use crate::config::Configs;
 use crate::event::*;
 use crate::protocol::{process_event, send_command, Service};
+use named_pipe::{PipeOptions, PipeServer, PipeClient};
+use crate::context;
+use std::io::Error;
+use std::path::PathBuf;
+
+const DAEMON_WIN_PIPE_NAME: &str = "\\\\.\\pipe\\espansodaemon";
+const WORKER_WIN_PIPE_NAME: &str = "\\\\.\\pipe\\espansoworker";
+const CLIENT_TIMEOUT: u32 = 2000;
 
 pub struct WindowsIPCServer {
     service: Service,
-    config: Configs,
     event_channel: Sender<Event>,
 }
 
-fn to_port(config: &Configs, service: &Service) -> u16 {
-    let port = match service {
-        Service::Daemon => config.ipc_server_port,
-        Service::Worker => config.worker_ipc_server_port,
-    };
-    port as u16
+fn get_pipe_name(service: &Service) -> String {
+    match service {
+        Service::Daemon => DAEMON_WIN_PIPE_NAME.to_owned(),
+        Service::Worker => WORKER_WIN_PIPE_NAME.to_owned(),
+    }
 }
 
+
 impl WindowsIPCServer {
     pub fn new(
         service: Service,
-        config: Configs,
         event_channel: Sender<Event>,
     ) -> WindowsIPCServer {
         WindowsIPCServer {
             service,
-            config,
             event_channel,
         }
     }
@@ -57,20 +62,21 @@ impl WindowsIPCServer {
 impl super::IPCServer for WindowsIPCServer {
     fn start(&self) {
         let event_channel = self.event_channel.clone();
-        let server_port = to_port(&self.config, &self.service);
+        let pipe_name = get_pipe_name(&self.service);
         std::thread::Builder::new()
             .name("ipc_server".to_string())
             .spawn(move || {
-                let listener = TcpListener::bind(format!("127.0.0.1:{}", server_port))
-                    .expect("Error binding to IPC server port");
+                let options = PipeOptions::new(&pipe_name);
 
                 info!(
-                    "Binded to IPC tcp socket: {}",
-                    listener.local_addr().unwrap().to_string()
+                    "Binding to named pipe: {}",
+                    pipe_name
                 );
 
-                for stream in listener.incoming() {
-                    process_event(&event_channel, stream);
+                loop {
+                    let server = options.single().expect("unable to initialize IPC named pipe");
+                    let pipe_server = server.wait();
+                    process_event(&event_channel, pipe_server);
                 }
             })
             .expect("Unable to spawn IPC server thread");
@@ -79,20 +85,19 @@ impl super::IPCServer for WindowsIPCServer {
 
 pub struct WindowsIPCClient {
     service: Service,
-    config: Configs,
 }
 
 impl WindowsIPCClient {
-    pub fn new(service: Service, config: Configs) -> WindowsIPCClient {
-        WindowsIPCClient { service, config }
+    pub fn new(service: Service) -> WindowsIPCClient {
+        WindowsIPCClient { service }
     }
 }
 
 impl super::IPCClient for WindowsIPCClient {
     fn send_command(&self, command: IPCCommand) -> Result<(), String> {
-        let port = to_port(&self.config, &self.service);
-        let stream = TcpStream::connect(("127.0.0.1", port));
+        let pipe_name = get_pipe_name(&self.service);
+        let client = PipeClient::connect_ms(pipe_name, CLIENT_TIMEOUT);
 
-        send_command(command, stream)
+        send_command(command, client)
     }
 }

From 0cd245153f097ad9087e59598f4c4e2ebd11890b Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Wed, 24 Jun 2020 21:44:29 +0200
Subject: [PATCH 09/11] Add Ctrl+Shift+V shortcut on Windows. Fix #333

---
 native/libwinbridge/bridge.cpp | 32 ++++++++++++++++++++++++++++++++
 native/libwinbridge/bridge.h   |  5 +++++
 src/bridge/windows.rs          |  1 +
 src/keyboard/windows.rs        |  3 +++
 4 files changed, 41 insertions(+)

diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp
index febcb60..01024c1 100644
--- a/native/libwinbridge/bridge.cpp
+++ b/native/libwinbridge/bridge.cpp
@@ -566,6 +566,38 @@ void send_multi_vkey_with_delay(int32_t vk, int32_t count, int32_t delay) {
     }
 }
 
+
+void trigger_shift_paste() {
+    std::vector<INPUT> vec;
+
+    INPUT input = { 0 };
+
+    input.type = INPUT_KEYBOARD;
+    input.ki.wScan = 0;
+    input.ki.time = 0;
+    input.ki.dwExtraInfo = 0;
+    input.ki.wVk = VK_CONTROL;
+    input.ki.dwFlags = 0; // 0 for key press
+    vec.push_back(input);
+
+    input.ki.wVk = VK_SHIFT;  // SHIFT KEY
+    vec.push_back(input);
+
+    input.ki.wVk = 0x56;  // V KEY
+    vec.push_back(input);
+
+    input.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
+    vec.push_back(input);
+
+    input.ki.wVk = VK_SHIFT;  // SHIFT KEY
+    vec.push_back(input);
+
+    input.ki.wVk = VK_CONTROL;
+    vec.push_back(input);
+
+    SendInput(vec.size(), vec.data(), sizeof(INPUT));
+}
+
 void trigger_paste() {
     std::vector<INPUT> vec;
 
diff --git a/native/libwinbridge/bridge.h b/native/libwinbridge/bridge.h
index 8b7984b..f0ef9eb 100644
--- a/native/libwinbridge/bridge.h
+++ b/native/libwinbridge/bridge.h
@@ -87,6 +87,11 @@ extern "C" void delete_string(int32_t count, int32_t delay);
  */
 extern "C" void trigger_paste();
 
+/*
+ * Send the Paste keyboard shortcut (CTRL+SHIFT+V)
+ */
+extern "C" void trigger_shift_paste();
+
 /*
  * Send the copy keyboard shortcut (CTRL+C)
  */
diff --git a/src/bridge/windows.rs b/src/bridge/windows.rs
index 2f75254..35cec74 100644
--- a/src/bridge/windows.rs
+++ b/src/bridge/windows.rs
@@ -65,6 +65,7 @@ extern "C" {
     pub fn send_multi_vkey(vk: i32, count: i32);
     pub fn delete_string(count: i32, delay: i32);
     pub fn trigger_paste();
+    pub fn trigger_shift_paste();
     pub fn trigger_copy();
 
     // PROCESSES
diff --git a/src/keyboard/windows.rs b/src/keyboard/windows.rs
index fce6718..be2cbb2 100644
--- a/src/keyboard/windows.rs
+++ b/src/keyboard/windows.rs
@@ -51,6 +51,9 @@ impl super::KeyboardManager for WindowsKeyboardManager {
                         trigger_paste();
                     }
                 },
+                PasteShortcut::CtrlShiftV => {
+                    trigger_shift_paste();
+                },
                 _ => {
                     error!("Windows backend does not support this Paste Shortcut, please open an issue on GitHub if you need it.")
                 }

From 958d0669e919b7b588d7812e14b24df718ce8d4a Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Wed, 24 Jun 2020 22:10:19 +0200
Subject: [PATCH 10/11] Handle modifiers on Release instead of Press events on
 Windows. Fix #328

---
 src/context/windows.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/context/windows.rs b/src/context/windows.rs
index ec92b57..07eeffa 100644
--- a/src/context/windows.rs
+++ b/src/context/windows.rs
@@ -173,8 +173,7 @@ extern "C" fn keypress_callback(
             }
         } else if event_type == 1 {
             // Modifier event
-            if is_key_down == 1 {
-                // Keyup event
+            if is_key_down == 0 {
                 let modifier: Option<KeyModifier> = match (key_code, variant) {
                     (0x5B, _) => Some(LEFT_META),
                     (0x5C, _) => Some(RIGHT_META),

From 45bcaee54be50bb6034d3facec1e08ec727e976d Mon Sep 17 00:00:00 2001
From: Federico Terzi <federico-terzi@users.noreply.github.com>
Date: Wed, 24 Jun 2020 22:11:01 +0200
Subject: [PATCH 11/11] Fix formatting

---
 src/bridge/linux.rs     |  7 ++++++-
 src/cli.rs              | 30 ++++++++++++++----------------
 src/config/mod.rs       |  2 +-
 src/context/linux.rs    |  5 ++++-
 src/engine.rs           | 24 +++++++++++++++---------
 src/extension/script.rs | 10 ++++------
 src/extension/shell.rs  | 14 +++++++-------
 src/main.rs             | 15 +++++----------
 src/protocol/mod.rs     |  4 +---
 src/protocol/windows.rs | 19 +++++++------------
 src/system/macos.rs     | 17 +++++++++--------
 11 files changed, 73 insertions(+), 74 deletions(-)

diff --git a/src/bridge/linux.rs b/src/bridge/linux.rs
index d7ceede..74562ea 100644
--- a/src/bridge/linux.rs
+++ b/src/bridge/linux.rs
@@ -33,7 +33,12 @@ extern "C" {
     pub fn get_active_window_executable(buffer: *mut c_char, size: i32) -> i32;
     pub fn is_current_window_special() -> i32;
     pub fn register_error_callback(
-        cb: extern "C" fn(_self: *mut c_void, error_code: c_char, request_code: c_char, minor_code: c_char),
+        cb: extern "C" fn(
+            _self: *mut c_void,
+            error_code: c_char,
+            request_code: c_char,
+            minor_code: c_char,
+        ),
     );
 
     // Keyboard
diff --git a/src/cli.rs b/src/cli.rs
index 645d64d..f2fc91b 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -17,9 +17,9 @@
  * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use serde::Serialize;
 use crate::config::ConfigSet;
 use crate::matcher::{Match, MatchContentType};
+use serde::Serialize;
 
 pub fn list_matches(config_set: ConfigSet, onlytriggers: bool, preserve_newlines: bool) {
     let matches = filter_matches(config_set);
@@ -28,19 +28,19 @@ pub fn list_matches(config_set: ConfigSet, onlytriggers: bool, preserve_newlines
         for trigger in m.triggers.iter() {
             if onlytriggers {
                 println!("{}", trigger);
-            }else {
+            } else {
                 match m.content {
                     MatchContentType::Text(ref text) => {
                         let replace = if preserve_newlines {
                             text.replace.to_owned()
-                        }else{
+                        } else {
                             text.replace.replace("\n", " ")
                         };
                         println!("{} - {}", trigger, replace)
-                    },
+                    }
                     MatchContentType::Image(_) => {
                         // Skip image matches for now
-                    },
+                    }
                 }
             }
         }
@@ -60,15 +60,13 @@ pub fn list_matches_as_json(config_set: ConfigSet) {
 
     for m in matches {
         match m.content {
-            MatchContentType::Text(ref text) => {
-                entries.push(JsonMatchEntry {
-                    triggers: m.triggers,
-                    replace: text.replace.clone(),
-                })
-            },
+            MatchContentType::Text(ref text) => entries.push(JsonMatchEntry {
+                triggers: m.triggers,
+                replace: text.replace.clone(),
+            }),
             MatchContentType::Image(_) => {
                 // Skip image matches for now
-            },
+            }
         }
     }
 
@@ -82,8 +80,8 @@ fn filter_matches(config_set: ConfigSet) -> Vec<Match> {
     output.extend(config_set.default.matches);
 
     // TODO: consider specific matches by class, title or exe path
-//    for specific in config_set.specific {
-//        output.extend(specific.matches)
-//    }
+    //    for specific in config_set.specific {
+    //        output.extend(specific.matches)
+    //    }
     output
-}
\ No newline at end of file
+}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 645e7c7..3e68ea1 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -129,7 +129,7 @@ fn default_show_notifications() -> bool {
     true
 }
 fn default_auto_restart() -> bool {
-   true
+    true
 }
 fn default_show_icon() -> bool {
     true
diff --git a/src/context/linux.rs b/src/context/linux.rs
index c1acae9..2d0fe54 100644
--- a/src/context/linux.rs
+++ b/src/context/linux.rs
@@ -163,5 +163,8 @@ extern "C" fn error_callback(
     request_code: c_char,
     minor_code: c_char,
 ) {
-    warn!("X11 reported an error code: {}, request_code: {} and minor_code: {}", error_code, request_code, minor_code);
+    warn!(
+        "X11 reported an error code: {}, request_code: {} and minor_code: {}",
+        error_code, request_code, minor_code
+    );
 }
diff --git a/src/engine.rs b/src/engine.rs
index 2a4d060..f48c810 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -136,16 +136,24 @@ impl<
     fn find_match_by_trigger(&self, trigger: &str) -> Option<Match> {
         let config = self.config_manager.active_config();
 
-        if let Some(m) = config.matches.iter().find(|m|
-            m.triggers.iter().any(|t| t == trigger)
-        ) {
+        if let Some(m) = config
+            .matches
+            .iter()
+            .find(|m| m.triggers.iter().any(|t| t == trigger))
+        {
             Some(m.clone())
-        }else{
+        } else {
             None
         }
     }
 
-    fn inject_match(&self, m: &Match, trailing_separator: Option<char>, trigger_offset: usize, skip_delete: bool) {
+    fn inject_match(
+        &self,
+        m: &Match,
+        trailing_separator: Option<char>,
+        trigger_offset: usize,
+        skip_delete: bool,
+    ) {
         let config = self.config_manager.active_config();
 
         if !config.enable_active {
@@ -455,10 +463,8 @@ impl<
                 match m {
                     Some(m) => {
                         self.inject_match(&m, None, 0, true);
-                    },
-                    None => {
-                        warn!("No match found with trigger: {}", trigger)
-                    },
+                    }
+                    None => warn!("No match found with trigger: {}", trigger),
                 }
             }
         }
diff --git a/src/extension/script.rs b/src/extension/script.rs
index 8d68c46..6144854 100644
--- a/src/extension/script.rs
+++ b/src/extension/script.rs
@@ -88,7 +88,8 @@ impl super::Extension for ScriptExtension {
 
             match output {
                 Ok(output) => {
-                    let mut output_str = String::from_utf8_lossy(output.stdout.as_slice()).to_string();
+                    let mut output_str =
+                        String::from_utf8_lossy(output.stdout.as_slice()).to_string();
                     let error_str = String::from_utf8_lossy(output.stderr.as_slice());
                     let error_str = error_str.to_string();
                     let error_str = error_str.trim();
@@ -103,7 +104,7 @@ impl super::Extension for ScriptExtension {
                     let should_trim = if let Some(value) = trim_opt {
                         let val = value.as_bool();
                         val.unwrap_or(true)
-                    }else{
+                    } else {
                         true
                     };
 
@@ -154,10 +155,7 @@ mod tests {
             Value::from("args"),
             Value::from(vec!["echo", "hello world"]),
         );
-        params.insert(
-            Value::from("trim"),
-            Value::from(false),
-        );
+        params.insert(Value::from("trim"), Value::from(false));
 
         let extension = ScriptExtension::new();
         let output = extension.calculate(&params, &vec![]);
diff --git a/src/extension/shell.rs b/src/extension/shell.rs
index 09ec1ad..f1b9da4 100644
--- a/src/extension/shell.rs
+++ b/src/extension/shell.rs
@@ -46,32 +46,32 @@ impl Shell {
                 let mut command = Command::new("cmd");
                 command.args(&["/C", &cmd]);
                 command
-            },
+            }
             Shell::Powershell => {
                 let mut command = Command::new("powershell");
                 command.args(&["-Command", &cmd]);
                 command
-            },
+            }
             Shell::WSL => {
                 let mut command = Command::new("bash");
                 command.args(&["-c", &cmd]);
                 command
-            },
+            }
             Shell::WSL2 => {
                 let mut command = Command::new("wsl");
                 command.args(&["bash", "-c", &cmd]);
                 command
-            },
+            }
             Shell::Bash => {
                 let mut command = Command::new("bash");
                 command.args(&["-c", &cmd]);
                 command
-            },
+            }
             Shell::Sh => {
                 let mut command = Command::new("sh");
                 command.args(&["-c", &cmd]);
                 command
-            },
+            }
         };
 
         // Inject the $CONFIG variable
@@ -176,7 +176,7 @@ impl super::Extension for ShellExtension {
                 let should_trim = if let Some(value) = trim_opt {
                     let val = value.as_bool();
                     val.unwrap_or(true)
-                }else{
+                } else {
                     true
                 };
 
diff --git a/src/main.rs b/src/main.rs
index 3becc92..12f602d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -53,6 +53,7 @@ use crate::ui::UIManager;
 
 mod bridge;
 mod check;
+mod cli;
 mod clipboard;
 mod config;
 mod context;
@@ -69,7 +70,6 @@ mod render;
 mod sysdaemon;
 mod system;
 mod ui;
-mod cli;
 mod utils;
 
 const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -519,18 +519,14 @@ fn register_signals(_: Configs) {}
 fn register_signals(config: Configs) {
     // On Unix, also listen for signals so that we can terminate the
     // worker if the daemon receives a signal
-    use signal_hook::{iterator::Signals, SIGTERM, SIGINT};
+    use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
     let signals = Signals::new(&[SIGTERM, SIGINT]).expect("unable to register for signals");
     thread::Builder::new()
         .name("signal monitor".to_string())
         .spawn(move || {
             for signal in signals.forever() {
                 info!("Received signal: {:?}, terminating worker", signal);
-                send_command_or_warn(
-                    Service::Worker,
-                    config,
-                    IPCCommand::exit_worker(),
-                );
+                send_command_or_warn(Service::Worker, config, IPCCommand::exit_worker());
 
                 std::thread::sleep(Duration::from_millis(200));
 
@@ -1268,7 +1264,6 @@ fn path_main(_config_set: ConfigSet, matches: &ArgMatches) {
     }
 }
 
-
 fn match_main(config_set: ConfigSet, matches: &ArgMatches) {
     if let Some(matches) = matches.subcommand_matches("list") {
         let json = matches.is_present("json");
@@ -1277,10 +1272,10 @@ fn match_main(config_set: ConfigSet, matches: &ArgMatches) {
 
         if !json {
             crate::cli::list_matches(config_set, onlytriggers, preserve_newlines);
-        }else{
+        } else {
             crate::cli::list_matches_as_json(config_set);
         }
-    }else if let Some(matches) = matches.subcommand_matches("exec") {
+    } else if let Some(matches) = matches.subcommand_matches("exec") {
         let trigger = matches.value_of("trigger").unwrap_or_else(|| {
             eprintln!("missing trigger");
             exit(1);
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 563e560..a609160 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -67,9 +67,7 @@ impl IPCCommand {
             "notify" => Some(Event::System(SystemEvent::NotifyRequest(
                 self.payload.clone(),
             ))),
-            "trigger" => Some(Event::System(SystemEvent::Trigger(
-                self.payload.clone(),
-            ))),
+            "trigger" => Some(Event::System(SystemEvent::Trigger(self.payload.clone()))),
             _ => None,
         }
     }
diff --git a/src/protocol/windows.rs b/src/protocol/windows.rs
index 9cf239c..10ff042 100644
--- a/src/protocol/windows.rs
+++ b/src/protocol/windows.rs
@@ -23,10 +23,10 @@ use std::net::{TcpListener, TcpStream};
 use std::sync::mpsc::Sender;
 
 use crate::config::Configs;
+use crate::context;
 use crate::event::*;
 use crate::protocol::{process_event, send_command, Service};
-use named_pipe::{PipeOptions, PipeServer, PipeClient};
-use crate::context;
+use named_pipe::{PipeClient, PipeOptions, PipeServer};
 use std::io::Error;
 use std::path::PathBuf;
 
@@ -46,12 +46,8 @@ fn get_pipe_name(service: &Service) -> String {
     }
 }
 
-
 impl WindowsIPCServer {
-    pub fn new(
-        service: Service,
-        event_channel: Sender<Event>,
-    ) -> WindowsIPCServer {
+    pub fn new(service: Service, event_channel: Sender<Event>) -> WindowsIPCServer {
         WindowsIPCServer {
             service,
             event_channel,
@@ -68,13 +64,12 @@ impl super::IPCServer for WindowsIPCServer {
             .spawn(move || {
                 let options = PipeOptions::new(&pipe_name);
 
-                info!(
-                    "Binding to named pipe: {}",
-                    pipe_name
-                );
+                info!("Binding to named pipe: {}", pipe_name);
 
                 loop {
-                    let server = options.single().expect("unable to initialize IPC named pipe");
+                    let server = options
+                        .single()
+                        .expect("unable to initialize IPC named pipe");
                     let pipe_server = server.wait();
                     process_event(&event_channel, pipe_server);
                 }
diff --git a/src/system/macos.rs b/src/system/macos.rs
index 584c26e..f6f6ec0 100644
--- a/src/system/macos.rs
+++ b/src/system/macos.rs
@@ -106,11 +106,12 @@ impl MacSystemManager {
                     if let Ok(path) = string {
                         if !path.trim().is_empty() {
                             let process = path.trim().to_string();
-                            let app_name = if let Some(name) = Self::get_app_name_from_path(&process) {
-                                name
-                            } else {
-                                process.to_owned()
-                            };
+                            let app_name =
+                                if let Some(name) = Self::get_app_name_from_path(&process) {
+                                    name
+                                } else {
+                                    process.to_owned()
+                                };
 
                             return Some((app_name, process));
                         }
@@ -138,14 +139,15 @@ impl MacSystemManager {
     }
 }
 
-
 #[cfg(test)]
 mod tests {
     use super::*;
 
     #[test]
     fn test_get_app_name_from_path() {
-        let app_name = MacSystemManager::get_app_name_from_path("/Applications/iTerm.app/Contents/MacOS/iTerm2");
+        let app_name = MacSystemManager::get_app_name_from_path(
+            "/Applications/iTerm.app/Contents/MacOS/iTerm2",
+        );
         assert_eq!(app_name.unwrap(), "iTerm")
     }
 
@@ -161,4 +163,3 @@ mod tests {
         assert_eq!(app_name.unwrap(), "SecurityAgent")
     }
 }
-