From d2a119eb2900486d5cb28ce09bbeb9291aba9d2b Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Thu, 24 Sep 2020 17:53:15 +0200
Subject: [PATCH 01/19] Fix incorrect modulo app stub template
---
src/res/mac/modulo.plist | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/res/mac/modulo.plist b/src/res/mac/modulo.plist
index 9883bd4..3bf440a 100644
--- a/src/res/mac/modulo.plist
+++ b/src/res/mac/modulo.plist
@@ -9,7 +9,7 @@
CFBundleIconFile
AppIcon
CFBundleIconName
- AppIcon>
+ AppIcon
CFBundleIdentifier
com.federicoterzi.modulo
CFBundleInfoDictionaryVersion
From 48c7535d28b1f77700156af4a4138388d1720402 Mon Sep 17 00:00:00 2001
From: Yasuhiro Matsumoto
Date: Thu, 1 Oct 2020 23:00:41 +0900
Subject: [PATCH 02/19] Fix build on mingw64 gcc compiler
---
build.rs | 4 ++++
native/libwinbridge/bridge.cpp | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/build.rs b/build.rs
index ca8571f..433fb3e 100644
--- a/build.rs
+++ b/build.rs
@@ -28,6 +28,10 @@ fn get_config() -> PathBuf {
fn print_config() {
println!("cargo:rustc-link-lib=static=winbridge");
println!("cargo:rustc-link-lib=dylib=user32");
+ #[cfg(target_env = "gnu")]
+ println!("cargo:rustc-link-lib=dylib=gdiplus");
+ #[cfg(target_env = "gnu")]
+ println!("cargo:rustc-link-lib=dylib=stdc++");
}
#[cfg(target_os = "linux")]
diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp
index ede2528..91ff5a4 100644
--- a/native/libwinbridge/bridge.cpp
+++ b/native/libwinbridge/bridge.cpp
@@ -27,7 +27,15 @@
#define UNICODE
+#ifdef __MINGW32__
+# ifndef WINVER
+# define WINVER 0x0606
+# endif
+# define STRSAFE_NO_DEPRECATE
+#endif
+
#include
+#include
#include
#include
From 50f1fe9f1971e3e6279adaf20860713cd11a8141 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Thu, 8 Oct 2020 21:19:28 +0200
Subject: [PATCH 03/19] Version bump 0.7.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 1aa2556..fbabf9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -371,7 +371,7 @@ dependencies = [
[[package]]
name = "espanso"
-version = "0.7.2"
+version = "0.7.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 c9d4457..b6baaf3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "espanso"
-version = "0.7.2"
+version = "0.7.3"
authors = ["Federico Terzi "]
license = "GPL-3.0"
description = "Cross-platform Text Expander written in Rust"
diff --git a/snapcraft.yaml b/snapcraft.yaml
index 43e79e5..9723f54 100644
--- a/snapcraft.yaml
+++ b/snapcraft.yaml
@@ -1,5 +1,5 @@
name: espanso
-version: 0.7.2
+version: 0.7.3
summary: A Cross-platform Text Expander written in Rust
description: |
espanso is a Cross-platform, Text Expander written in Rust.
From 185d06e8e944881ddf2c03e80fdde8fecfbe1fdf Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Thu, 8 Oct 2020 23:17:01 +0200
Subject: [PATCH 04/19] Implement Rich text on Windows
---
Cargo.lock | 209 +++++++++++++++++++++++++++++++++
Cargo.toml | 2 +
native/libwinbridge/bridge.cpp | 34 ++++++
native/libwinbridge/bridge.h | 7 ++
src/bridge/windows.rs | 3 +-
src/clipboard/mod.rs | 1 +
src/clipboard/windows.rs | 60 +++++++++-
src/engine.rs | 25 ++--
src/guard.rs | 19 +++
src/matcher/mod.rs | 146 +++++++++++++++++++++--
src/package/zip.rs | 19 +++
src/ui/modulo/mac.rs | 19 +++
src/ui/modulo/mod.rs | 19 +++
13 files changed, 546 insertions(+), 17 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index fbabf9d..fdca6f2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -380,10 +380,12 @@ dependencies = [
"dialoguer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html2text 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
+ "markdown 0.3.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)",
@@ -505,6 +507,15 @@ name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "futf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "new_debug_unreachable 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "futures"
version = "0.1.29"
@@ -546,6 +557,29 @@ dependencies = [
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "html2text"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "html5ever 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "markup5ever_rcdom 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "html5ever"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "http"
version = "0.1.18"
@@ -725,6 +759,48 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "markdown"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pipeline 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "markup5ever"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_codegen 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "string_cache_codegen 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "markup5ever_rcdom"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "html5ever 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xml5ever 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "matches"
version = "0.1.8"
@@ -839,6 +915,11 @@ dependencies = [
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "nodrop"
version = "0.1.13"
@@ -955,6 +1036,45 @@ name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "siphasher 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pipeline"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "pkg-config"
version = "0.3.16"
@@ -970,6 +1090,11 @@ name = "ppv-lite86"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "proc-macro2"
version = "0.4.30"
@@ -1042,6 +1167,7 @@ dependencies = [
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1139,6 +1265,14 @@ dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "rand_xorshift"
version = "0.1.1"
@@ -1393,6 +1527,11 @@ dependencies = [
"term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "siphasher"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "slab"
version = "0.4.2"
@@ -1416,6 +1555,29 @@ dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "string_cache"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "new_debug_unreachable 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "phf_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "strsim"
version = "0.8.0"
@@ -1465,6 +1627,16 @@ dependencies = [
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "tendril"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "term"
version = "0.6.1"
@@ -1703,6 +1875,11 @@ dependencies = [
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "utf-8"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "uuid"
version = "0.7.4"
@@ -1810,6 +1987,17 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "xml5ever"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "yaml-rust"
version = "0.4.3"
@@ -1888,10 +2076,13 @@ dependencies = [
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
+"checksum html2text 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a26379dcb715e237b96102a12b505c553e2bffa74bae2e54658748d298660ef1"
+"checksum html5ever 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4"
"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
@@ -1912,6 +2103,10 @@ dependencies = [
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae0136257df209261daa18d6c16394757c63e032e27aafd8b07788b051082bef"
+"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+"checksum markdown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef3aab6a1d529b112695f72beec5ee80e729cb45af58663ec902c8fac764ecdd"
+"checksum markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab"
+"checksum markup5ever_rcdom 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
@@ -1924,6 +2119,7 @@ dependencies = [
"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 new_debug_unreachable 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
@@ -1937,9 +2133,15 @@ dependencies = [
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+"checksum phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+"checksum phf_codegen 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+"checksum phf_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+"checksum phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+"checksum pipeline 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d15b6607fa632996eb8a17c9041cb6071cb75ac057abd45dece578723ea8c7c0"
"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
+"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95"
"checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510"
@@ -1958,6 +2160,7 @@ dependencies = [
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
+"checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
@@ -1986,15 +2189,19 @@ dependencies = [
"checksum signal-hook 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff2db2112d6c761e12522c65f7768548bd6e8cd23d2a9dae162520626629bd6"
"checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
"checksum simplelog 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe8c881061cce7ee205784634eda7a61922925e7cc2833188467d3a560e027"
+"checksum siphasher 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d"
+"checksum string_cache 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
+"checksum string_cache_codegen 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+"checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b"
"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
@@ -2020,6 +2227,7 @@ dependencies = [
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
+"checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
@@ -2036,5 +2244,6 @@ dependencies = [
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+"checksum xml5ever 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6"
diff --git a/Cargo.toml b/Cargo.toml
index b6baaf3..903dd97 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,6 +34,8 @@ dialoguer = "0.4.0"
rand = "0.7.2"
zip = "0.5.3"
notify = "4.0.13"
+markdown = "0.3.0"
+html2text = "0.2.1"
[target.'cfg(unix)'.dependencies]
libc = "0.2.62"
diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp
index 91ff5a4..11c9ab9 100644
--- a/native/libwinbridge/bridge.cpp
+++ b/native/libwinbridge/bridge.cpp
@@ -898,3 +898,37 @@ int32_t set_clipboard_image(wchar_t *path) {
return result;
}
+// Inspired by https://docs.microsoft.com/en-za/troubleshoot/cpp/add-html-code-clipboard
+int32_t set_clipboard_html(char * html, wchar_t * text_fallback) {
+ // Get clipboard id for HTML format
+ static int cfid = 0;
+ if(!cfid) {
+ cfid = RegisterClipboardFormat(L"HTML Format");
+ }
+
+ int32_t result = 0;
+ const size_t html_len = strlen(html) + 1;
+ HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, html_len * sizeof(char));
+ memcpy(GlobalLock(hMem), html, html_len * sizeof(char));
+ GlobalUnlock(hMem);
+
+ const size_t fallback_len = wcslen(text_fallback) + 1;
+ HGLOBAL hMemFallback = GlobalAlloc(GMEM_MOVEABLE, fallback_len * sizeof(wchar_t));
+ memcpy(GlobalLock(hMemFallback), text_fallback, fallback_len * sizeof(wchar_t));
+ GlobalUnlock(hMemFallback);
+
+ if (!OpenClipboard(NULL)) {
+ return -1;
+ }
+ EmptyClipboard();
+ if (!SetClipboardData(cfid, hMem)) {
+ result = -2;
+ }
+
+ if (!SetClipboardData(CF_UNICODETEXT, hMemFallback)) {
+ result = -3;
+ }
+ CloseClipboard();
+ GlobalFree(hMem);
+ return result;
+}
diff --git a/native/libwinbridge/bridge.h b/native/libwinbridge/bridge.h
index ae57213..fadcaeb 100644
--- a/native/libwinbridge/bridge.h
+++ b/native/libwinbridge/bridge.h
@@ -179,6 +179,13 @@ extern "C" int32_t set_clipboard(wchar_t * text);
*/
extern "C" int32_t set_clipboard_image(wchar_t * path);
+/*
+ * Set clipboard HTML. Notice how in this case, text is not a wide char but instead
+ * uses the UTF8 encoding.
+ * Also set the text fallback, in case some applications don't support HTML clipboard.
+ */
+extern "C" int32_t set_clipboard_html(char * html, wchar_t * text_fallback);
+
// PROCESSES
extern "C" int32_t start_process(wchar_t * cmd);
diff --git a/src/bridge/windows.rs b/src/bridge/windows.rs
index 554056f..470dbfd 100644
--- a/src/bridge/windows.rs
+++ b/src/bridge/windows.rs
@@ -17,7 +17,7 @@
* along with espanso. If not, see .
*/
-use std::os::raw::c_void;
+use std::os::raw::{c_char, c_void};
#[repr(C)]
pub struct WindowsMenuItem {
@@ -55,6 +55,7 @@ extern "C" {
pub fn get_clipboard(buffer: *mut u16, size: i32) -> i32;
pub fn set_clipboard(payload: *const u16) -> i32;
pub fn set_clipboard_image(path: *const u16) -> i32;
+ pub fn set_clipboard_html(html: *const c_char, text_fallback: *const u16) -> i32;
// KEYBOARD
pub fn register_keypress_callback(
diff --git a/src/clipboard/mod.rs b/src/clipboard/mod.rs
index 41e43de..a282c89 100644
--- a/src/clipboard/mod.rs
+++ b/src/clipboard/mod.rs
@@ -32,6 +32,7 @@ pub trait ClipboardManager {
fn get_clipboard(&self) -> Option;
fn set_clipboard(&self, payload: &str);
fn set_clipboard_image(&self, image_path: &Path);
+ fn set_clipboard_html(&self, html: &str);
}
// LINUX IMPLEMENTATION
diff --git a/src/clipboard/windows.rs b/src/clipboard/windows.rs
index b2e2e1d..efe69ee 100644
--- a/src/clipboard/windows.rs
+++ b/src/clipboard/windows.rs
@@ -17,8 +17,10 @@
* along with espanso. If not, see .
*/
-use crate::bridge::windows::{get_clipboard, set_clipboard, set_clipboard_image};
-use std::path::Path;
+use crate::bridge::windows::{
+ get_clipboard, set_clipboard, set_clipboard_html, set_clipboard_image,
+};
+use std::{ffi::CString, path::Path};
use widestring::U16CString;
pub struct WindowsClipboardManager {}
@@ -60,4 +62,58 @@ impl super::ClipboardManager for WindowsClipboardManager {
set_clipboard_image(payload_c.as_ptr());
}
}
+
+ fn set_clipboard_html(&self, html: &str) {
+ // In order to set the HTML clipboard, we have to create a prefix with a specific format
+ // For more information, look here:
+ // https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
+ // https://docs.microsoft.com/en-za/troubleshoot/cpp/add-html-code-clipboard
+ let mut tokens = Vec::new();
+ tokens.push("Version:0.9");
+ tokens.push("StartHTML:<");
+ tokens.push("EndHTML:<");
+ tokens.push("StartFragment:<");
+ tokens.push("EndFragment:<");
+ tokens.push("");
+ tokens.push("");
+ let content = format!("{}", html);
+ tokens.push(&content);
+ tokens.push("");
+ tokens.push("");
+
+ let mut render = tokens.join("\r\n");
+
+ // Now replace the placeholders with the actual positions
+ render = render.replace(
+ "<",
+ &format!("{:0>8}", render.find("").unwrap_or_default()),
+ );
+ render = render.replace("<", &format!("{:0>8}", render.len()));
+ render = render.replace(
+ "<",
+ &format!(
+ "{:0>8}",
+ render.find("").unwrap_or_default()
+ + "".len()
+ ),
+ );
+ render = render.replace(
+ "<",
+ &format!(
+ "{:0>8}",
+ render.find("").unwrap_or_default()
+ ),
+ );
+
+ // Render the text fallback for those applications that don't support HTML clipboard
+ let decorator = html2text::render::text_renderer::TrivialDecorator::new();
+ let text_fallback =
+ html2text::from_read_with_decorator(html.as_bytes(), 1000000, decorator);
+ unsafe {
+ let payload_c =
+ CString::new(render).expect("unable to create CString for html content");
+ let payload_fallback_c = U16CString::from_str(text_fallback).unwrap();
+ set_clipboard_html(payload_c.as_ptr(), payload_fallback_c.as_ptr());
+ }
+ }
}
diff --git a/src/engine.rs b/src/engine.rs
index 7bd7ce9..1dd2cd6 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -154,8 +154,14 @@ impl<
}
}
- fn inject_text(&self, config: &Configs, target_string: &str, force_clipboard: bool) {
- let backend = if force_clipboard {
+ fn inject_text(
+ &self,
+ config: &Configs,
+ target_string: &str,
+ force_clipboard: bool,
+ is_html: bool,
+ ) {
+ let backend = if force_clipboard || is_html {
&BackendType::Clipboard
} else if config.backend == BackendType::Auto {
if cfg!(target_os = "linux") {
@@ -188,7 +194,12 @@ impl<
}
}
BackendType::Clipboard => {
- self.clipboard_manager.set_clipboard(&target_string);
+ if !is_html {
+ self.clipboard_manager.set_clipboard(&target_string);
+ } else {
+ self.clipboard_manager.set_clipboard_html(&target_string);
+ }
+
self.keyboard_manager.trigger_paste(&config);
}
_ => {
@@ -270,10 +281,10 @@ impl<
// clipboard content to restore it later.
previous_clipboard_content = self.return_content_if_preserve_clipboard_is_enabled();
- self.inject_text(&config, &target_string, m.force_clipboard);
+ self.inject_text(&config, &target_string, m.force_clipboard, m.is_html);
- // Disallow undo backspace if cursor positioning is used
- if cursor_rewind.is_none() {
+ // Disallow undo backspace if cursor positioning is used or text is HTML
+ if cursor_rewind.is_none() && !m.is_html {
expansion_data = Some((
m.triggers[trigger_offset].clone(),
target_string.chars().count() as i32,
@@ -350,7 +361,7 @@ impl<
self.keyboard_manager
.delete_string(&config, *injected_text_len - 1);
// Restore previous text
- self.inject_text(&config, trigger_string, false);
+ self.inject_text(&config, trigger_string, false, false);
}
}
diff --git a/src/guard.rs b/src/guard.rs
index 5ada9ff..036b283 100644
--- a/src/guard.rs
+++ b/src/guard.rs
@@ -1,3 +1,22 @@
+/*
+ * 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 .
+ */
+
use crate::config::Configs;
use log::debug;
use std::sync::atomic::Ordering::Release;
diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs
index 4b64c4b..f6e09be 100644
--- a/src/matcher/mod.rs
+++ b/src/matcher/mod.rs
@@ -22,9 +22,9 @@ use crate::event::{KeyEvent, KeyModifier};
use regex::{Captures, Regex};
use serde::{Deserialize, Deserializer, Serialize};
use serde_yaml::{Mapping, Value};
-use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
+use std::{borrow::Cow, collections::HashMap};
pub(crate) mod scrolling;
@@ -36,6 +36,7 @@ pub struct Match {
pub passive_only: bool,
pub propagate_case: bool,
pub force_clipboard: bool,
+ pub is_html: bool,
// Automatically calculated from the triggers, used by the matcher to check for correspondences.
#[serde(skip_serializing)]
@@ -132,15 +133,36 @@ impl<'a> From<&'a AutoMatch> for Match {
})
.collect();
- let content = if let Some(replace) = &other.replace {
- // Text match
- let new_replace = replace.clone();
+ let (text_content, is_html) = if let Some(replace) = &other.replace {
+ (Some(Cow::from(replace)), false)
+ } else if let Some(markdown_str) = &other.markdown {
+ // Render the markdown into HTML
+ let mut html = markdown::to_html(markdown_str);
+ html = html.trim().to_owned();
+ if !other.paragraph {
+ // Remove the surrounding paragraph
+ if html.starts_with("") {
+ html = html.trim_start_matches("
").to_owned();
+ }
+ if html.ends_with("
") {
+ html = html.trim_end_matches("
").to_owned();
+ }
+ }
+
+ (Some(Cow::from(html)), true)
+ } else if let Some(html) = &other.html {
+ (Some(Cow::from(html)), true)
+ } else {
+ (None, false)
+ };
+
+ let content = if let Some(content) = text_content {
// Check if the match contains variables
- let has_vars = VAR_REGEX.is_match(replace);
+ let has_vars = VAR_REGEX.is_match(&content);
let content = TextContent {
- replace: new_replace,
+ replace: content.to_string(),
vars: other.vars.clone(),
_has_vars: has_vars,
};
@@ -208,7 +230,7 @@ impl<'a> From<&'a AutoMatch> for Match {
MatchContentType::Image(content)
} else {
- eprintln!("ERROR: no action specified for match {}, please specify either 'replace', 'image_path' or 'form'", other.trigger);
+ eprintln!("ERROR: no action specified for match {}, please specify either 'replace', 'markdown', 'html', image_path' or 'form'", other.trigger);
std::process::exit(2);
};
@@ -220,6 +242,7 @@ impl<'a> From<&'a AutoMatch> for Match {
_trigger_sequences: trigger_sequences,
propagate_case: other.propagate_case,
force_clipboard: other.force_clipboard,
+ is_html,
}
}
}
@@ -259,6 +282,15 @@ struct AutoMatch {
#[serde(default = "default_force_clipboard")]
pub force_clipboard: bool,
+
+ #[serde(default = "default_markdown")]
+ pub markdown: Option,
+
+ #[serde(default = "default_paragraph")]
+ pub paragraph: bool,
+
+ #[serde(default = "default_html")]
+ pub html: Option,
}
fn default_trigger() -> String {
@@ -294,6 +326,15 @@ fn default_propagate_case() -> bool {
fn default_force_clipboard() -> bool {
false
}
+fn default_markdown() -> Option {
+ None
+}
+fn default_paragraph() -> bool {
+ true
+}
+fn default_html() -> Option {
+ None
+}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct MatchVariable {
@@ -663,4 +704,95 @@ mod tests {
_ => panic!("wrong content"),
}
}
+
+ #[test]
+ fn test_match_markdown_loaded_correctly() {
+ let match_str = r###"
+ trigger: ":test"
+ markdown: "This *text* is **very bold**"
+ "###;
+
+ let _match: Match = serde_yaml::from_str(match_str).unwrap();
+
+ match _match.content {
+ MatchContentType::Text(content) => {
+ assert_eq!(
+ content.replace,
+ "This text is very bold"
+ );
+ assert_eq!(_match.is_html, true);
+ }
+ _ => {
+ assert!(false);
+ }
+ }
+ }
+
+ #[test]
+ fn test_match_markdown_keep_vars() {
+ let match_str = r###"
+ trigger: ":test"
+ markdown: "This *text* is {{variable}} **very bold**"
+ "###;
+
+ let _match: Match = serde_yaml::from_str(match_str).unwrap();
+
+ match _match.content {
+ MatchContentType::Text(content) => {
+ assert_eq!(
+ content.replace,
+ "This text is {{variable}} very bold"
+ );
+ assert_eq!(_match.is_html, true);
+ assert_eq!(content._has_vars, true);
+ }
+ _ => {
+ assert!(false);
+ }
+ }
+ }
+
+ #[test]
+ fn test_match_html_loaded_correctly() {
+ let match_str = r###"
+ trigger: ":test"
+ html: "This text is very bold"
+ "###;
+
+ let _match: Match = serde_yaml::from_str(match_str).unwrap();
+
+ match _match.content {
+ MatchContentType::Text(content) => {
+ assert_eq!(content.replace, "This text is very bold");
+ assert_eq!(_match.is_html, true);
+ }
+ _ => {
+ assert!(false);
+ }
+ }
+ }
+
+ #[test]
+ fn test_match_html_keep_vars() {
+ let match_str = r###"
+ trigger: ":test"
+ html: "This text is {{var}} very bold"
+ "###;
+
+ let _match: Match = serde_yaml::from_str(match_str).unwrap();
+
+ match _match.content {
+ MatchContentType::Text(content) => {
+ assert_eq!(
+ content.replace,
+ "This text is {{var}} very bold"
+ );
+ assert_eq!(_match.is_html, true);
+ assert_eq!(content._has_vars, true);
+ }
+ _ => {
+ assert!(false);
+ }
+ }
+ }
}
diff --git a/src/package/zip.rs b/src/package/zip.rs
index 4912e5f..b3a197a 100644
--- a/src/package/zip.rs
+++ b/src/package/zip.rs
@@ -1,3 +1,22 @@
+/*
+ * 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 .
+ */
+
use log::debug;
use std::error::Error;
use std::io::{copy, Cursor};
diff --git a/src/ui/modulo/mac.rs b/src/ui/modulo/mac.rs
index 1b55b2e..394ec88 100644
--- a/src/ui/modulo/mac.rs
+++ b/src/ui/modulo/mac.rs
@@ -1,3 +1,22 @@
+/*
+ * 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 .
+ */
+
use log::info;
use std::os::unix::fs::symlink;
use std::path::PathBuf;
diff --git a/src/ui/modulo/mod.rs b/src/ui/modulo/mod.rs
index 445d5a5..7b1544f 100644
--- a/src/ui/modulo/mod.rs
+++ b/src/ui/modulo/mod.rs
@@ -1,3 +1,22 @@
+/*
+ * 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 .
+ */
+
use crate::config::Configs;
use log::{error, info};
use std::io::{Error, Write};
From c43774a59f9c7c4cfa5d1f8370c5d78daa142b42 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Mon, 12 Oct 2020 20:49:07 +0200
Subject: [PATCH 05/19] Add linux implementation for Rich text
---
src/clipboard/linux.rs | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/src/clipboard/linux.rs b/src/clipboard/linux.rs
index 24dda8c..d051d02 100644
--- a/src/clipboard/linux.rs
+++ b/src/clipboard/linux.rs
@@ -89,6 +89,31 @@ impl super::ClipboardManager for LinuxClipboardManager {
error!("Could not set image clipboard: {}", e);
}
}
+
+ fn set_clipboard_html(&self, html: &str) {
+ let res = Command::new("xclip")
+ .args(&["-sel", "clip", "-t", "text/html"])
+ .stdin(Stdio::piped())
+ .spawn();
+
+ if let Ok(mut child) = res {
+ let stdin = child.stdin.as_mut();
+
+ if let Some(output) = stdin {
+ let res = output.write_all(html.as_bytes());
+
+ if let Err(e) = res {
+ error!("Could not set clipboard html: {}", e);
+ }
+
+ let res = child.wait();
+
+ if let Err(e) = res {
+ error!("Could not set clipboard html: {}", e);
+ }
+ }
+ }
+ }
}
impl LinuxClipboardManager {
From 37bd79039f2985a2d64d13017135b61c239a8929 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Fri, 23 Oct 2020 21:28:05 +0200
Subject: [PATCH 06/19] Add macOS support for rich text
---
native/libmacbridge/bridge.h | 5 +++++
native/libmacbridge/bridge.mm | 18 ++++++++++++++++++
src/bridge/macos.rs | 1 +
src/clipboard/macos.rs | 13 +++++++++++++
4 files changed, 37 insertions(+)
diff --git a/native/libmacbridge/bridge.h b/native/libmacbridge/bridge.h
index a50212c..a735cf8 100644
--- a/native/libmacbridge/bridge.h
+++ b/native/libmacbridge/bridge.h
@@ -170,6 +170,11 @@ int32_t set_clipboard(char * text);
*/
int32_t set_clipboard_image(char * path);
+/*
+ * Set the clipboard html
+ */
+int32_t set_clipboard_html(char * html, char * fallback);
+
/*
* If a process is currently holding SecureInput, then return 1 and set the pid pointer to the corresponding PID.
*/
diff --git a/native/libmacbridge/bridge.mm b/native/libmacbridge/bridge.mm
index 27cd5b1..72fa20b 100644
--- a/native/libmacbridge/bridge.mm
+++ b/native/libmacbridge/bridge.mm
@@ -324,6 +324,24 @@ int32_t set_clipboard_image(char *path) {
return result;
}
+int32_t set_clipboard_html(char * html, char * fallback_text) {
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSArray *array = @[NSRTFPboardType, NSPasteboardTypeString];
+ [pasteboard declareTypes:array owner:nil];
+
+ NSString *nsHtml = [NSString stringWithUTF8String:html];
+ NSDictionary *documentAttributes = [NSDictionary dictionaryWithObjectsAndKeys:NSHTMLTextDocumentType, NSDocumentTypeDocumentAttribute, NSCharacterEncodingDocumentAttribute,[NSNumber numberWithInt:NSUTF8StringEncoding], nil];
+ NSAttributedString* atr = [[NSAttributedString alloc] initWithData:[nsHtml dataUsingEncoding:NSUTF8StringEncoding] options:documentAttributes documentAttributes:nil error:nil];
+
+ NSData *rtf = [atr RTFFromRange:NSMakeRange(0, [atr length])
+ documentAttributes:nil];
+
+ [pasteboard setData:rtf forType:NSRTFPboardType];
+
+ NSString *nsText = [NSString stringWithUTF8String:fallback_text];
+ [pasteboard setString:nsText forType:NSPasteboardTypeString];
+}
+
// CONTEXT MENU
diff --git a/src/bridge/macos.rs b/src/bridge/macos.rs
index 01846d7..fd6dc44 100644
--- a/src/bridge/macos.rs
+++ b/src/bridge/macos.rs
@@ -51,6 +51,7 @@ extern "C" {
pub fn get_clipboard(buffer: *mut c_char, size: i32) -> i32;
pub fn set_clipboard(text: *const c_char) -> i32;
pub fn set_clipboard_image(path: *const c_char) -> i32;
+ pub fn set_clipboard_html(html: *const c_char, text_fallback: *const c_char) -> i32;
// UI
pub fn register_icon_click_callback(cb: extern "C" fn(_self: *mut c_void));
diff --git a/src/clipboard/macos.rs b/src/clipboard/macos.rs
index 8299179..307423b 100644
--- a/src/clipboard/macos.rs
+++ b/src/clipboard/macos.rs
@@ -65,6 +65,19 @@ impl super::ClipboardManager for MacClipboardManager {
}
}
}
+
+ fn set_clipboard_html(&self, html: &str) {
+ // Render the text fallback for those applications that don't support HTML clipboard
+ let decorator = html2text::render::text_renderer::TrivialDecorator::new();
+ let text_fallback =
+ html2text::from_read_with_decorator(html.as_bytes(), 1000000, decorator);
+ unsafe {
+ let payload_c =
+ CString::new(html).expect("unable to create CString for html content");
+ let payload_fallback_c = CString::new(text_fallback).unwrap();
+ set_clipboard_html(payload_c.as_ptr(), payload_fallback_c.as_ptr());
+ }
+ }
}
impl MacClipboardManager {
From dcdae3ad2d4f32335b23adb460be850d453345c3 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Fri, 23 Oct 2020 21:28:37 +0200
Subject: [PATCH 07/19] Format code
---
src/clipboard/macos.rs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/clipboard/macos.rs b/src/clipboard/macos.rs
index 307423b..44f7161 100644
--- a/src/clipboard/macos.rs
+++ b/src/clipboard/macos.rs
@@ -72,8 +72,7 @@ impl super::ClipboardManager for MacClipboardManager {
let text_fallback =
html2text::from_read_with_decorator(html.as_bytes(), 1000000, decorator);
unsafe {
- let payload_c =
- CString::new(html).expect("unable to create CString for html content");
+ let payload_c = CString::new(html).expect("unable to create CString for html content");
let payload_fallback_c = CString::new(text_fallback).unwrap();
set_clipboard_html(payload_c.as_ptr(), payload_fallback_c.as_ptr());
}
From 63c28081a61528421f7ea79f5efb64ed2642c586 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Fri, 23 Oct 2020 21:40:05 +0200
Subject: [PATCH 08/19] Fix bugs related to macOS status icon. #480 #474
---
native/libmacbridge/AppDelegate.mm | 5 -----
1 file changed, 5 deletions(-)
diff --git a/native/libmacbridge/AppDelegate.mm b/native/libmacbridge/AppDelegate.mm
index 5bfb256..340f04a 100644
--- a/native/libmacbridge/AppDelegate.mm
+++ b/native/libmacbridge/AppDelegate.mm
@@ -26,7 +26,6 @@
// Setup status icon
if (show_icon) {
myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
-
[self setIcon: icon_path];
}
@@ -61,16 +60,12 @@
- (void) updateIcon: (char *)iconPath {
if (show_icon) {
- [myStatusItem release];
-
[self setIcon: iconPath];
}
}
- (void) setIcon: (char *)iconPath {
if (show_icon) {
- myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
-
NSString *nsIconPath = [NSString stringWithUTF8String:iconPath];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:nsIconPath];
[statusImage setTemplate:YES];
From be0dd8a2cb8fc2b022133ce48e60b4228ce63d55 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Fri, 23 Oct 2020 21:54:27 +0200
Subject: [PATCH 09/19] Add option to wait for modifier release before
injection. Fix #470
---
src/config/mod.rs | 7 +++++++
src/engine.rs | 7 +++++++
src/keyboard/mod.rs | 17 +++++++++++++++++
3 files changed, 31 insertions(+)
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 58ab626..7e4d1c0 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -159,6 +159,10 @@ fn default_post_inject_delay() -> u64 {
100
}
+fn default_wait_for_modifiers_release() -> bool {
+ false
+}
+
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Configs {
#[serde(default = "default_name")]
@@ -283,6 +287,9 @@ pub struct Configs {
#[serde(default = "default_modulo_path")]
pub modulo_path: Option,
+
+ #[serde(default = "default_wait_for_modifiers_release")]
+ pub wait_for_modifiers_release: bool,
}
// Macro used to validate config fields
diff --git a/src/engine.rs b/src/engine.rs
index 1dd2cd6..81e4fa5 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -231,6 +231,13 @@ impl<
m.triggers[trigger_offset].chars().count() as i32 + 1 // Count also the separator
};
+ // If configured to do so, wait until the modifier keys are released (or timeout) so
+ // that we avoid unwanted interactions. As an example, see:
+ // https://github.com/federico-terzi/espanso/issues/470
+ if config.wait_for_modifiers_release {
+ crate::keyboard::wait_for_modifiers_release();
+ }
+
if !skip_delete {
self.keyboard_manager.delete_string(&config, char_count);
}
diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs
index 7a5c2ca..d9f11a4 100644
--- a/src/keyboard/mod.rs
+++ b/src/keyboard/mod.rs
@@ -18,6 +18,7 @@
*/
use crate::config::Configs;
+use log::warn;
use serde::{Deserialize, Serialize};
#[cfg(target_os = "windows")]
@@ -71,3 +72,19 @@ pub fn get_manager() -> impl KeyboardManager {
pub fn get_manager() -> impl KeyboardManager {
macos::MacKeyboardManager {}
}
+
+// These methods are used to wait until all modifiers are released (or timeout occurs)
+pub fn wait_for_modifiers_release() {
+ #[cfg(target_os = "windows")]
+ let released = crate::keyboard::windows::wait_for_modifiers_release();
+
+ #[cfg(target_os = "macos")]
+ let released = crate::keyboard::macos::wait_for_modifiers_release();
+
+ #[cfg(target_os = "linux")]
+ let released = true; // NOOP on linux (at least for now)
+
+ if !released {
+ warn!("Wait for modifiers release timed out! Please release your modifiers keys (CTRL, CMD, ALT, SHIFT) after typing the trigger");
+ }
+}
From 1431145afa6a6248a3b61b230c823519b8c64f71 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Sat, 14 Nov 2020 20:52:02 +0100
Subject: [PATCH 10/19] Add option to disable passive argument injection in
shell extension. Fix #513
---
src/extension/shell.rs | 71 +++++++++++++++++++++++++++++++-----------
src/matcher/mod.rs | 2 +-
2 files changed, 54 insertions(+), 19 deletions(-)
diff --git a/src/extension/shell.rs b/src/extension/shell.rs
index fd0f43a..4e323a8 100644
--- a/src/extension/shell.rs
+++ b/src/extension/shell.rs
@@ -25,11 +25,8 @@ use std::collections::HashMap;
use std::process::{Command, Output};
lazy_static! {
- static ref POS_ARG_REGEX: Regex = if cfg!(target_os = "windows") {
- Regex::new("%(?P\\d+)").unwrap()
- } else {
- Regex::new("\\$(?P\\d+)").unwrap()
- };
+ static ref UNIX_POS_ARG_REGEX: Regex = Regex::new("\\$(?P\\d+)").unwrap();
+ static ref WIN_POS_ARG_REGEX: Regex = Regex::new("%(?P\\d+)").unwrap();
}
pub enum Shell {
@@ -121,6 +118,18 @@ impl Shell {
_ => None,
}
}
+
+ fn get_arg_regex(&self) -> &Regex {
+ let regex = match self {
+ Shell::Cmd | Shell::Powershell => {
+ &*WIN_POS_ARG_REGEX
+ }
+ _ => {
+ &*UNIX_POS_ARG_REGEX
+ }
+ };
+ regex
+ }
}
impl Default for Shell {
@@ -162,20 +171,13 @@ impl super::Extension for ShellExtension {
return None;
}
- let original_cmd = cmd.unwrap().as_str().unwrap();
+ let inject_args = params
+ .get(&Value::from("inject_args"))
+ .unwrap_or(&Value::from(false))
+ .as_bool()
+ .unwrap_or(false);
- // Render positional parameters in args
- let cmd = POS_ARG_REGEX
- .replace_all(&original_cmd, |caps: &Captures| {
- let position_str = caps.name("pos").unwrap().as_str();
- let position = position_str.parse::().unwrap_or(-1);
- if position >= 0 && position < args.len() as i32 {
- args[position as usize].to_owned()
- } else {
- "".to_owned()
- }
- })
- .to_string();
+ let original_cmd = cmd.unwrap().as_str().unwrap();
let shell_param = params.get(&Value::from("shell"));
let shell = if let Some(shell_param) = shell_param {
@@ -192,6 +194,23 @@ impl super::Extension for ShellExtension {
Shell::default()
};
+ // Render positional parameters in args
+ let cmd = if inject_args {
+ shell.get_arg_regex()
+ .replace_all(&original_cmd, |caps: &Captures| {
+ let position_str = caps.name("pos").unwrap().as_str();
+ let position = position_str.parse::().unwrap_or(-1);
+ if position >= 0 && position < args.len() as i32 {
+ args[position as usize].to_owned()
+ } else {
+ "".to_owned()
+ }
+ })
+ .to_string()
+ } else {
+ original_cmd.to_owned()
+ };
+
let env_variables = super::utils::convert_to_env_variables(&vars);
let output = shell.execute_cmd(&cmd, &env_variables);
@@ -348,6 +367,7 @@ mod tests {
fn test_shell_args_unix() {
let mut params = Mapping::new();
params.insert(Value::from("cmd"), Value::from("echo $0"));
+ params.insert(Value::from("inject_args"), Value::from(true));
let extension = ShellExtension::new();
let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new());
@@ -357,11 +377,26 @@ mod tests {
assert_eq!(output.unwrap(), ExtensionResult::Single("hello".to_owned()));
}
+ #[test]
+ #[cfg(not(target_os = "windows"))]
+ fn test_shell_no_default_inject_args_unix() {
+ let mut params = Mapping::new();
+ params.insert(Value::from("cmd"), Value::from("echo 'hey friend' | awk '{ print $2 }'"));
+
+ let extension = ShellExtension::new();
+ let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new());
+
+ assert!(output.is_some());
+
+ assert_eq!(output.unwrap(), ExtensionResult::Single("friend".to_owned()));
+ }
+
#[test]
#[cfg(target_os = "windows")]
fn test_shell_args_windows() {
let mut params = Mapping::new();
params.insert(Value::from("cmd"), Value::from("echo %0"));
+ params.insert(Value::from("inject_args"), Value::from(true));
let extension = ShellExtension::new();
let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new());
diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs
index f6e09be..955e43c 100644
--- a/src/matcher/mod.rs
+++ b/src/matcher/mod.rs
@@ -330,7 +330,7 @@ fn default_markdown() -> Option {
None
}
fn default_paragraph() -> bool {
- true
+ false
}
fn default_html() -> Option {
None
From 4c799f736f25c2c049c16448074de266c2eba4b6 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Sat, 14 Nov 2020 21:11:46 +0100
Subject: [PATCH 11/19] Add support for escaped brakets in form fields. Fix
#503
---
src/matcher/mod.rs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/matcher/mod.rs b/src/matcher/mod.rs
index 955e43c..c8f8286 100644
--- a/src/matcher/mod.rs
+++ b/src/matcher/mod.rs
@@ -177,6 +177,9 @@ impl<'a> From<&'a AutoMatch> for Match {
});
let new_replace = new_replace.to_string();
+ // Convert escaped brakets in forms
+ let form = form.replace("\\{", "{ ").replace("\\}", " }");
+
// Convert the form data to valid variables
let mut params = Mapping::new();
if let Some(fields) = &other.form_fields {
@@ -186,7 +189,7 @@ impl<'a> From<&'a AutoMatch> for Match {
});
params.insert(Value::from("fields"), Value::from(mapping_fields));
}
- params.insert(Value::from("layout"), Value::from(form.to_owned()));
+ params.insert(Value::from("layout"), Value::from(form));
let vars = vec![MatchVariable {
name: "form1".to_owned(),
From c4409241b68f5a1780daa78ae912389f63064f91 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Sat, 14 Nov 2020 21:19:59 +0100
Subject: [PATCH 12/19] Disable argument rendering for matches unless args are
present. Fix #465
---
src/render/default.rs | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/src/render/default.rs b/src/render/default.rs
index 40d1857..3415f0d 100644
--- a/src/render/default.rs
+++ b/src/render/default.rs
@@ -238,13 +238,15 @@ impl super::Renderer for DefaultRenderer {
// Unescape any brackets (needed to be able to insert double brackets in replacement
// text, without triggering the variable system). See issue #187
- let target_string = target_string.replace("\\{", "{").replace("\\}", "}");
+ let mut target_string = target_string.replace("\\{", "{").replace("\\}", "}");
// Render any argument that may be present
- let target_string = utils::render_args(&target_string, &args);
+ if !args.is_empty() {
+ target_string = utils::render_args(&target_string, &args);
+ }
// Handle case propagation
- let target_string = if m.propagate_case {
+ target_string = if m.propagate_case {
let trigger = &m.triggers[trigger_offset];
// The check should be carried out from the position of the first
@@ -518,6 +520,25 @@ mod tests {
verify_render(rendered, "Hi Jon");
}
+ #[test]
+ fn test_render_passive_simple_match_no_args_should_not_replace_args_syntax() {
+ let text = ":greet";
+
+ let config = get_config_for(
+ r###"
+ matches:
+ - trigger: ':greet'
+ replace: "Hi $0$"
+ "###,
+ );
+
+ let renderer = get_renderer(config.clone());
+
+ let rendered = renderer.render_passive(text, &config);
+
+ verify_render(rendered, "Hi $0$");
+ }
+
#[test]
fn test_render_passive_simple_match_with_multiple_args() {
let text = ":greet/Jon/Snow/";
From c5c2a4ab90e949a8bd4bf400b87a43989b70c1ad Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Sat, 14 Nov 2020 21:27:47 +0100
Subject: [PATCH 13/19] Improve buffer handling when interfacing with native
layer. Fix #431
---
src/clipboard/macos.rs | 2 +-
src/clipboard/windows.rs | 2 +-
src/system/linux.rs | 12 ++++++------
src/system/macos.rs | 8 ++++----
src/system/windows.rs | 8 ++++----
5 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/clipboard/macos.rs b/src/clipboard/macos.rs
index 44f7161..a6ef7e5 100644
--- a/src/clipboard/macos.rs
+++ b/src/clipboard/macos.rs
@@ -29,7 +29,7 @@ impl super::ClipboardManager for MacClipboardManager {
fn get_clipboard(&self) -> Option {
unsafe {
let mut buffer: [c_char; 2000] = [0; 2000];
- let res = get_clipboard(buffer.as_mut_ptr(), buffer.len() as i32);
+ let res = get_clipboard(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
diff --git a/src/clipboard/windows.rs b/src/clipboard/windows.rs
index efe69ee..2341bcf 100644
--- a/src/clipboard/windows.rs
+++ b/src/clipboard/windows.rs
@@ -35,7 +35,7 @@ impl super::ClipboardManager for WindowsClipboardManager {
fn get_clipboard(&self) -> Option {
unsafe {
let mut buffer: [u16; 2000] = [0; 2000];
- let res = get_clipboard(buffer.as_mut_ptr(), buffer.len() as i32);
+ let res = get_clipboard(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = U16CString::from_ptr_str(buffer.as_ptr());
diff --git a/src/system/linux.rs b/src/system/linux.rs
index 5f2c8dc..0f8b144 100644
--- a/src/system/linux.rs
+++ b/src/system/linux.rs
@@ -29,8 +29,8 @@ pub struct LinuxSystemManager {}
impl super::SystemManager for LinuxSystemManager {
fn get_current_window_title(&self) -> Option {
unsafe {
- let mut buffer: [c_char; 100] = [0; 100];
- let res = get_active_window_name(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [c_char; 256] = [0; 256];
+ let res = get_active_window_name(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
@@ -47,8 +47,8 @@ impl super::SystemManager for LinuxSystemManager {
fn get_current_window_class(&self) -> Option {
unsafe {
- let mut buffer: [c_char; 100] = [0; 100];
- let res = get_active_window_class(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [c_char; 256] = [0; 256];
+ let res = get_active_window_class(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
@@ -65,8 +65,8 @@ impl super::SystemManager for LinuxSystemManager {
fn get_current_window_executable(&self) -> Option {
unsafe {
- let mut buffer: [c_char; 100] = [0; 100];
- let res = get_active_window_executable(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [c_char; 256] = [0; 256];
+ let res = get_active_window_executable(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
diff --git a/src/system/macos.rs b/src/system/macos.rs
index f6f6ec0..4480c3d 100644
--- a/src/system/macos.rs
+++ b/src/system/macos.rs
@@ -33,8 +33,8 @@ impl super::SystemManager for MacSystemManager {
fn get_current_window_class(&self) -> Option {
unsafe {
- let mut buffer: [c_char; 250] = [0; 250];
- let res = get_active_app_identifier(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [c_char; 256] = [0; 256];
+ let res = get_active_app_identifier(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
@@ -51,8 +51,8 @@ impl super::SystemManager for MacSystemManager {
fn get_current_window_executable(&self) -> Option {
unsafe {
- let mut buffer: [c_char; 250] = [0; 250];
- let res = get_active_app_bundle(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [c_char; 256] = [0; 256];
+ let res = get_active_app_bundle(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = CStr::from_ptr(buffer.as_ptr());
diff --git a/src/system/windows.rs b/src/system/windows.rs
index eb0d030..4d7eb27 100644
--- a/src/system/windows.rs
+++ b/src/system/windows.rs
@@ -31,8 +31,8 @@ impl WindowsSystemManager {
impl super::SystemManager for WindowsSystemManager {
fn get_current_window_title(&self) -> Option {
unsafe {
- let mut buffer: [u16; 100] = [0; 100];
- let res = get_active_window_name(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [u16; 256] = [0; 256];
+ let res = get_active_window_name(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = U16CString::from_ptr_str(buffer.as_ptr());
@@ -51,8 +51,8 @@ impl super::SystemManager for WindowsSystemManager {
fn get_current_window_executable(&self) -> Option {
unsafe {
- let mut buffer: [u16; 250] = [0; 250];
- let res = get_active_window_executable(buffer.as_mut_ptr(), buffer.len() as i32);
+ let mut buffer: [u16; 256] = [0; 256];
+ let res = get_active_window_executable(buffer.as_mut_ptr(), (buffer.len() - 1) as i32);
if res > 0 {
let c_string = U16CString::from_ptr_str(buffer.as_ptr());
From cc72f10398060e87d4a72c781db92ed82e0b63b1 Mon Sep 17 00:00:00 2001
From: Federico Terzi
Date: Sat, 14 Nov 2020 21:58:54 +0100
Subject: [PATCH 14/19] Refactor extensions to allow them to stop the expansion
process. Fix #475
---
src/extension/clipboard.rs | 8 +++++---
src/extension/date.rs | 6 ++++--
src/extension/dummy.rs | 8 ++++----
src/extension/form.rs | 16 +++++++++++-----
src/extension/mod.rs | 13 ++++++++++++-
src/extension/multiecho.rs | 4 ++--
src/extension/random.rs | 14 +++++++-------
src/extension/script.rs | 10 +++++-----
src/extension/shell.rs | 30 +++++++++++++++---------------
src/extension/vardummy.rs | 6 +++---
src/render/default.rs | 32 ++++++++++++++++++++------------
11 files changed, 88 insertions(+), 59 deletions(-)
diff --git a/src/extension/clipboard.rs b/src/extension/clipboard.rs
index 9b5477b..d82112d 100644
--- a/src/extension/clipboard.rs
+++ b/src/extension/clipboard.rs
@@ -22,6 +22,8 @@ use crate::extension::ExtensionResult;
use serde_yaml::Mapping;
use std::collections::HashMap;
+use super::ExtensionOut;
+
pub struct ClipboardExtension {
clipboard_manager: Box,
}
@@ -42,11 +44,11 @@ impl super::Extension for ClipboardExtension {
_: &Mapping,
_: &Vec,
_: &HashMap,
- ) -> Option {
+ ) -> ExtensionOut {
if let Some(clipboard) = self.clipboard_manager.get_clipboard() {
- Some(ExtensionResult::Single(clipboard))
+ Ok(Some(ExtensionResult::Single(clipboard)))
} else {
- None
+ Ok(None)
}
}
}
diff --git a/src/extension/date.rs b/src/extension/date.rs
index 2e4324b..90e1ecc 100644
--- a/src/extension/date.rs
+++ b/src/extension/date.rs
@@ -22,6 +22,8 @@ use chrono::{DateTime, Duration, Local};
use serde_yaml::{Mapping, Value};
use std::collections::HashMap;
+use super::ExtensionOut;
+
pub struct DateExtension {}
impl DateExtension {
@@ -40,7 +42,7 @@ impl super::Extension for DateExtension {
params: &Mapping,
_: &Vec,
_: &HashMap,
- ) -> Option {
+ ) -> ExtensionOut {
let mut now: DateTime = Local::now();
// Compute the given offset
@@ -59,6 +61,6 @@ impl super::Extension for DateExtension {
now.to_rfc2822()
};
- Some(ExtensionResult::Single(date))
+ Ok(Some(ExtensionResult::Single(date)))
}
}
diff --git a/src/extension/dummy.rs b/src/extension/dummy.rs
index e810823..030b71d 100644
--- a/src/extension/dummy.rs
+++ b/src/extension/dummy.rs
@@ -43,15 +43,15 @@ impl super::Extension for DummyExtension {
params: &Mapping,
_: &Vec,
_: &HashMap,
- ) -> Option {
+ ) -> super::ExtensionOut {
let echo = params.get(&Value::from("echo"));
if let Some(echo) = echo {
- Some(ExtensionResult::Single(
+ Ok(Some(ExtensionResult::Single(
echo.as_str().unwrap_or_default().to_owned(),
- ))
+ )))
} else {
- None
+ Ok(None)
}
}
}
diff --git a/src/extension/form.rs b/src/extension/form.rs
index 00ac091..888130f 100644
--- a/src/extension/form.rs
+++ b/src/extension/form.rs
@@ -43,13 +43,13 @@ impl super::Extension for FormExtension {
params: &Mapping,
_: &Vec,
_: &HashMap,
- ) -> Option {
+ ) -> super::ExtensionOut {
let layout = params.get(&Value::from("layout"));
let layout = if let Some(value) = layout {
value.as_str().unwrap_or_default().to_string()
} else {
error!("invoking form extension without specifying a layout");
- return None;
+ return Err(super::ExtensionError::Internal);
};
let mut form_config = Mapping::new();
@@ -81,16 +81,22 @@ impl super::Extension for FormExtension {
let json: Result, _> = serde_json::from_str(&output);
match json {
Ok(json) => {
- return Some(ExtensionResult::Multiple(json));
+ // Check if the JSON is empty. In those cases, it means the user exited
+ // the form before submitting it, therefore the expansion should stop
+ if json.is_empty() {
+ return Err(super::ExtensionError::Aborted);
+ }
+
+ return Ok(Some(ExtensionResult::Multiple(json)));
}
Err(error) => {
error!("modulo json parsing error: {}", error);
- return None;
+ return Err(super::ExtensionError::Internal);
}
}
} else {
error!("modulo form didn't return any output");
- return None;
+ return Err(super::ExtensionError::Internal);
}
}
}
diff --git a/src/extension/mod.rs b/src/extension/mod.rs
index 7ea9546..532c902 100644
--- a/src/extension/mod.rs
+++ b/src/extension/mod.rs
@@ -38,6 +38,17 @@ pub enum ExtensionResult {
Multiple(HashMap),
}
+#[derive(Clone, Debug, PartialEq)]
+pub enum ExtensionError {
+ // Returned by an extension if an internal process occurred
+ Internal,
+ // Returned by an extension if the user aborted the expansion
+ // for example when pressing ESC inside a FormExtension.
+ Aborted,
+}
+
+pub type ExtensionOut = Result