feat(core): implement rich text matches
This commit is contained in:
		
							parent
							
								
									038798a2d6
								
							
						
					
					
						commit
						2f53752e97
					
				
							
								
								
									
										251
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										251
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -299,9 +299,11 @@ dependencies = [
 | 
			
		|||
 "espanso-path",
 | 
			
		||||
 "espanso-render",
 | 
			
		||||
 "espanso-ui",
 | 
			
		||||
 "html2text",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "log",
 | 
			
		||||
 "maplit",
 | 
			
		||||
 "markdown",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "simplelog",
 | 
			
		||||
| 
						 | 
				
			
			@ -459,6 +461,16 @@ version = "0.1.1"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "futf"
 | 
			
		||||
version = "0.1.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "mac",
 | 
			
		||||
 "new_debug_unreachable",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "getrandom"
 | 
			
		||||
version = "0.1.16"
 | 
			
		||||
| 
						 | 
				
			
			@ -505,6 +517,31 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "html2text"
 | 
			
		||||
version = "0.2.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a26379dcb715e237b96102a12b505c553e2bffa74bae2e54658748d298660ef1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "html5ever",
 | 
			
		||||
 "markup5ever_rcdom",
 | 
			
		||||
 "unicode-width",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "html5ever"
 | 
			
		||||
version = "0.25.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "log",
 | 
			
		||||
 "mac",
 | 
			
		||||
 "markup5ever",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote 1.0.9",
 | 
			
		||||
 "syn 1.0.60",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "itertools"
 | 
			
		||||
version = "0.10.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -562,6 +599,12 @@ dependencies = [
 | 
			
		|||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "mac"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "mac-notification-sys"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -589,6 +632,43 @@ version = "1.0.2"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "markdown"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ef3aab6a1d529b112695f72beec5ee80e729cb45af58663ec902c8fac764ecdd"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "pipeline",
 | 
			
		||||
 "regex",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "markup5ever"
 | 
			
		||||
version = "0.10.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "log",
 | 
			
		||||
 "phf",
 | 
			
		||||
 "phf_codegen",
 | 
			
		||||
 "string_cache",
 | 
			
		||||
 "string_cache_codegen",
 | 
			
		||||
 "tendril",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "markup5ever_rcdom"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "html5ever",
 | 
			
		||||
 "markup5ever",
 | 
			
		||||
 "tendril",
 | 
			
		||||
 "xml5ever",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "memchr"
 | 
			
		||||
version = "2.3.4"
 | 
			
		||||
| 
						 | 
				
			
			@ -613,6 +693,12 @@ dependencies = [
 | 
			
		|||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "new_debug_unreachable"
 | 
			
		||||
version = "1.0.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "notify-rust"
 | 
			
		||||
version = "4.2.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -681,6 +767,50 @@ dependencies = [
 | 
			
		|||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf"
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf_codegen"
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "phf_generator",
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf_generator"
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
 "rand 0.7.3",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "phf_shared"
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "siphasher",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "pipeline"
 | 
			
		||||
version = "0.5.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d15b6607fa632996eb8a17c9041cb6071cb75ac057abd45dece578723ea8c7c0"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "pkg-config"
 | 
			
		||||
version = "0.3.19"
 | 
			
		||||
| 
						 | 
				
			
			@ -693,6 +823,12 @@ version = "0.2.10"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "precomputed-hash"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "proc-macro2"
 | 
			
		||||
version = "1.0.24"
 | 
			
		||||
| 
						 | 
				
			
			@ -730,6 +866,20 @@ dependencies = [
 | 
			
		|||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand"
 | 
			
		||||
version = "0.7.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "getrandom 0.1.16",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "rand_chacha 0.2.2",
 | 
			
		||||
 "rand_core 0.5.1",
 | 
			
		||||
 "rand_hc 0.2.0",
 | 
			
		||||
 "rand_pcg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand"
 | 
			
		||||
version = "0.8.3"
 | 
			
		||||
| 
						 | 
				
			
			@ -737,9 +887,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		|||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
 "rand_chacha",
 | 
			
		||||
 "rand_chacha 0.3.0",
 | 
			
		||||
 "rand_core 0.6.2",
 | 
			
		||||
 "rand_hc",
 | 
			
		||||
 "rand_hc 0.3.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_chacha"
 | 
			
		||||
version = "0.2.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "ppv-lite86",
 | 
			
		||||
 "rand_core 0.5.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
| 
						 | 
				
			
			@ -767,6 +927,15 @@ version = "0.4.2"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_core"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "getrandom 0.1.16",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_core"
 | 
			
		||||
version = "0.6.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -776,6 +945,15 @@ dependencies = [
 | 
			
		|||
 "getrandom 0.2.2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_hc"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "rand_core 0.5.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_hc"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -785,6 +963,15 @@ dependencies = [
 | 
			
		|||
 "rand_core 0.6.2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rand_pcg"
 | 
			
		||||
version = "0.2.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "rand_core 0.5.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rdrand"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -933,6 +1120,37 @@ dependencies = [
 | 
			
		|||
 "termcolor",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "siphasher"
 | 
			
		||||
version = "0.3.5"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "string_cache"
 | 
			
		||||
version = "0.8.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "new_debug_unreachable",
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
 "precomputed-hash",
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "string_cache_codegen"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "phf_generator",
 | 
			
		||||
 "phf_shared",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote 1.0.9",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strsim"
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
| 
						 | 
				
			
			@ -1010,6 +1228,17 @@ dependencies = [
 | 
			
		|||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tendril"
 | 
			
		||||
version = "0.4.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a9ef557cb397a4f0a5a3a628f06515f78563f2209e64d47055d9dc6052bf5e33"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "futf",
 | 
			
		||||
 "mac",
 | 
			
		||||
 "utf-8",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "termcolor"
 | 
			
		||||
version = "1.1.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -1092,6 +1321,12 @@ version = "0.2.1"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "utf-8"
 | 
			
		||||
version = "0.7.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "vec_map"
 | 
			
		||||
version = "0.8.2"
 | 
			
		||||
| 
						 | 
				
			
			@ -1204,6 +1439,18 @@ dependencies = [
 | 
			
		|||
 "bitflags 0.9.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "xml5ever"
 | 
			
		||||
version = "0.16.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "log",
 | 
			
		||||
 "mac",
 | 
			
		||||
 "markup5ever",
 | 
			
		||||
 "time",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "yaml-rust"
 | 
			
		||||
version = "0.4.5"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,4 +34,6 @@ crossbeam = "0.8.0"
 | 
			
		|||
enum-as-inner = "0.3.3"
 | 
			
		||||
dirs = "3.0.1"
 | 
			
		||||
serde = { version = "1.0.123", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.62"
 | 
			
		||||
serde_json = "1.0.62"
 | 
			
		||||
markdown = "0.3.0"
 | 
			
		||||
html2text = "0.2.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,7 @@
 | 
			
		|||
use espanso_inject::{Injector, keys::Key};
 | 
			
		||||
use espanso_clipboard::Clipboard;
 | 
			
		||||
 | 
			
		||||
use crate::engine::{dispatch::TextInjector};
 | 
			
		||||
use crate::engine::{dispatch::TextInjector, dispatch::HtmlInjector};
 | 
			
		||||
 | 
			
		||||
pub struct ClipboardInjectorAdapter<'a> {
 | 
			
		||||
  injector: &'a dyn Injector,
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +34,16 @@ impl <'a> ClipboardInjectorAdapter<'a> {
 | 
			
		|||
      clipboard,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn send_paste_combination(&self) -> anyhow::Result<()> {
 | 
			
		||||
    // TODO: handle delay duration
 | 
			
		||||
    std::thread::sleep(std::time::Duration::from_millis(100));
 | 
			
		||||
 | 
			
		||||
    // TODO: handle options
 | 
			
		||||
    self.injector.send_key_combination(&[Key::Control, Key::V], Default::default())?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl <'a> TextInjector for ClipboardInjectorAdapter<'a> {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,11 +55,18 @@ impl <'a> TextInjector for ClipboardInjectorAdapter<'a> {
 | 
			
		|||
    // TODO: handle clipboard restoration
 | 
			
		||||
    self.clipboard.set_text(text)?;
 | 
			
		||||
 | 
			
		||||
    // TODO: handle delay duration
 | 
			
		||||
    std::thread::sleep(std::time::Duration::from_millis(100));
 | 
			
		||||
 | 
			
		||||
    // TODO: handle options
 | 
			
		||||
    self.injector.send_key_combination(&[Key::Control, Key::V], Default::default())?;
 | 
			
		||||
    self.send_paste_combination()?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl <'a> HtmlInjector for ClipboardInjectorAdapter<'a> {
 | 
			
		||||
  fn inject_html(&self, html: &str, fallback_text: &str) -> anyhow::Result<()> {
 | 
			
		||||
    // TODO: handle clipboard restoration
 | 
			
		||||
    self.clipboard.set_html(html, Some(fallback_text))?;
 | 
			
		||||
 | 
			
		||||
    self.send_paste_combination()?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,6 +124,7 @@ pub fn initialize_and_spawn(
 | 
			
		|||
        &clipboard_injector,
 | 
			
		||||
        &config_manager,
 | 
			
		||||
        &key_injector,
 | 
			
		||||
        &clipboard_injector,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      let mut engine = crate::engine::Engine::new(&funnel, &mut processor, &dispatcher);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,14 @@
 | 
			
		|||
 | 
			
		||||
use espanso_config::matches::{Match, MatchEffect};
 | 
			
		||||
 | 
			
		||||
use crate::engine::{event::{EventType, internal::DetectedMatch, internal::RenderingRequestedEvent}, process::Multiplexer};
 | 
			
		||||
use crate::engine::{
 | 
			
		||||
  event::{
 | 
			
		||||
    internal::DetectedMatch,
 | 
			
		||||
    internal::{RenderingRequestedEvent, TextFormat},
 | 
			
		||||
    EventType,
 | 
			
		||||
  },
 | 
			
		||||
  process::Multiplexer,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub trait MatchProvider<'a> {
 | 
			
		||||
  fn get(&self, match_id: i32) -> Option<&'a Match>;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,15 +47,24 @@ impl<'a> Multiplexer for MultiplexAdapter<'a> {
 | 
			
		|||
    let m = self.provider.get(detected_match.id)?;
 | 
			
		||||
 | 
			
		||||
    match &m.effect {
 | 
			
		||||
      MatchEffect::Text(_) => Some(EventType::RenderingRequested(RenderingRequestedEvent {
 | 
			
		||||
      MatchEffect::Text(effect) => Some(EventType::RenderingRequested(RenderingRequestedEvent {
 | 
			
		||||
        match_id: detected_match.id,
 | 
			
		||||
        trigger: detected_match.trigger,
 | 
			
		||||
        left_separator: detected_match.left_separator,
 | 
			
		||||
        right_separator: detected_match.right_separator,
 | 
			
		||||
        trigger_args: detected_match.args,
 | 
			
		||||
        format: convert_format(&effect.format),
 | 
			
		||||
      })),
 | 
			
		||||
      // TODO: think about rich text and image
 | 
			
		||||
      // TODO: think about image
 | 
			
		||||
      MatchEffect::None => None,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_format(format: &espanso_config::matches::TextFormat) -> TextFormat {
 | 
			
		||||
  match format {
 | 
			
		||||
    espanso_config::matches::TextFormat::Plain => TextFormat::Plain,
 | 
			
		||||
    espanso_config::matches::TextFormat::Markdown => TextFormat::Markdown, 
 | 
			
		||||
    espanso_config::matches::TextFormat::Html => TextFormat::Html, 
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
use super::Event;
 | 
			
		||||
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector};
 | 
			
		||||
use super::{ModeProvider, Dispatcher, Executor, KeyInjector, TextInjector, HtmlInjector};
 | 
			
		||||
 | 
			
		||||
pub struct DefaultDispatcher<'a> {
 | 
			
		||||
  executors: Vec<Box<dyn Executor + 'a>>,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ impl<'a> DefaultDispatcher<'a> {
 | 
			
		|||
    clipboard_injector: &'a dyn TextInjector,
 | 
			
		||||
    mode_provider: &'a dyn ModeProvider,
 | 
			
		||||
    key_injector: &'a dyn KeyInjector,
 | 
			
		||||
    html_injector: &'a dyn HtmlInjector,
 | 
			
		||||
  ) -> Self {
 | 
			
		||||
    Self {
 | 
			
		||||
      executors: vec![
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +42,9 @@ impl<'a> DefaultDispatcher<'a> {
 | 
			
		|||
        Box::new(super::executor::key_inject::KeyInjectExecutor::new(
 | 
			
		||||
          key_injector,
 | 
			
		||||
        )),
 | 
			
		||||
        Box::new(super::executor::html_inject::HtmlInjectExecutor::new(
 | 
			
		||||
          html_injector,
 | 
			
		||||
        ))
 | 
			
		||||
      ],
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										61
									
								
								espanso/src/engine/dispatch/executor/html_inject.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								espanso/src/engine/dispatch/executor/html_inject.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use super::super::{Event, Executor};
 | 
			
		||||
use crate::engine::event::EventType;
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use log::error;
 | 
			
		||||
 | 
			
		||||
pub trait HtmlInjector {
 | 
			
		||||
  fn inject_html(&self, html: &str, fallback: &str) -> Result<()>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct HtmlInjectExecutor<'a> {
 | 
			
		||||
  injector: &'a dyn HtmlInjector,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> HtmlInjectExecutor<'a> {
 | 
			
		||||
  pub fn new(injector: &'a dyn HtmlInjector) -> Self {
 | 
			
		||||
    Self { injector }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Executor for HtmlInjectExecutor<'a> {
 | 
			
		||||
  fn execute(&self, event: &Event) -> bool {
 | 
			
		||||
    if let EventType::HtmlInject(inject_event) = &event.etype {
 | 
			
		||||
      // Render the text fallback for those applications that don't support HTML clipboard
 | 
			
		||||
      let decorator = html2text::render::text_renderer::TrivialDecorator::new();
 | 
			
		||||
      let fallback_text =
 | 
			
		||||
        html2text::from_read_with_decorator(inject_event.html.as_bytes(), 1000000, decorator);
 | 
			
		||||
 | 
			
		||||
      if let Err(error) = self
 | 
			
		||||
        .injector
 | 
			
		||||
        .inject_html(&inject_event.html, &fallback_text)
 | 
			
		||||
      {
 | 
			
		||||
        error!("html injector reported an error: {:?}", error);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: test
 | 
			
		||||
| 
						 | 
				
			
			@ -18,4 +18,5 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
pub mod text_inject;
 | 
			
		||||
pub mod key_inject;
 | 
			
		||||
pub mod key_inject;
 | 
			
		||||
pub mod html_inject;
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,8 @@ pub trait Dispatcher {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Re-export dependency injection entities
 | 
			
		||||
pub use executor::text_inject::{ModeProvider, Mode, TextInjector};
 | 
			
		||||
pub use executor::html_inject::HtmlInjector;
 | 
			
		||||
pub use executor::text_inject::{Mode, ModeProvider, TextInjector};
 | 
			
		||||
 | 
			
		||||
// TODO: move into module
 | 
			
		||||
pub trait KeyInjector {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +46,13 @@ pub fn default<'a>(
 | 
			
		|||
  clipboard_injector: &'a dyn TextInjector,
 | 
			
		||||
  mode_provider: &'a dyn ModeProvider,
 | 
			
		||||
  key_injector: &'a dyn KeyInjector,
 | 
			
		||||
  html_injector: &'a dyn HtmlInjector,
 | 
			
		||||
) -> impl Dispatcher + 'a {
 | 
			
		||||
  default::DefaultDispatcher::new(event_injector, clipboard_injector, mode_provider, key_injector)
 | 
			
		||||
  default::DefaultDispatcher::new(
 | 
			
		||||
    event_injector,
 | 
			
		||||
    clipboard_injector,
 | 
			
		||||
    mode_provider,
 | 
			
		||||
    key_injector,
 | 
			
		||||
    html_injector,
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,16 @@ pub struct TextInjectRequest {
 | 
			
		|||
  pub force_mode: Option<TextInjectMode>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct MarkdownInjectRequest {
 | 
			
		||||
  pub markdown: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct HtmlInjectRequest {
 | 
			
		||||
  pub html: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Clone)]
 | 
			
		||||
pub enum TextInjectMode {
 | 
			
		||||
  Keys,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,12 +50,21 @@ pub struct RenderingRequestedEvent {
 | 
			
		|||
  pub left_separator: Option<String>,
 | 
			
		||||
  pub right_separator: Option<String>,
 | 
			
		||||
  pub trigger_args: HashMap<String, String>,
 | 
			
		||||
  pub format: TextFormat,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum TextFormat {
 | 
			
		||||
  Plain,
 | 
			
		||||
  Markdown,
 | 
			
		||||
  Html, 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub struct RenderedEvent {
 | 
			
		||||
  pub match_id: i32,
 | 
			
		||||
  pub body: String,
 | 
			
		||||
  pub format: TextFormat,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,4 +69,6 @@ pub enum EventType {
 | 
			
		|||
 | 
			
		||||
  KeySequenceInject(effect::KeySequenceInjectRequest),
 | 
			
		||||
  TextInject(effect::TextInjectRequest),
 | 
			
		||||
  MarkdownInject(effect::MarkdownInjectRequest),
 | 
			
		||||
  HtmlInject(effect::HtmlInjectRequest),
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ use log::trace;
 | 
			
		|||
use super::{MatchFilter, MatchInfoProvider, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer, middleware::{
 | 
			
		||||
    match_select::MatchSelectMiddleware, matcher::MatcherMiddleware, multiplex::MultiplexMiddleware,
 | 
			
		||||
    render::RenderMiddleware, action::{ActionMiddleware, EventSequenceProvider}, cursor_hint::CursorHintMiddleware, cause::CauseCompensateMiddleware,
 | 
			
		||||
    delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider},
 | 
			
		||||
    delay_modifiers::{DelayForModifierReleaseMiddleware, ModifierStatusProvider}, markdown::MarkdownMiddleware,
 | 
			
		||||
    past_discard::PastEventsDiscardMiddleware,
 | 
			
		||||
  }};
 | 
			
		||||
use crate::engine::event::{Event, EventType};
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +55,7 @@ impl<'a> DefaultProcessor<'a> {
 | 
			
		|||
        Box::new(RenderMiddleware::new(renderer)),
 | 
			
		||||
        Box::new(CursorHintMiddleware::new()),
 | 
			
		||||
        Box::new(ActionMiddleware::new(match_info_provider, event_sequence_provider)),
 | 
			
		||||
        Box::new(MarkdownMiddleware::new()),
 | 
			
		||||
        Box::new(DelayForModifierReleaseMiddleware::new(modifier_status_provider)),
 | 
			
		||||
      ],
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,12 +18,7 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
use super::super::Middleware;
 | 
			
		||||
use crate::engine::event::{
 | 
			
		||||
  effect::{KeySequenceInjectRequest, TextInjectMode, TextInjectRequest},
 | 
			
		||||
  input::Key,
 | 
			
		||||
  internal::DiscardPreviousEvent,
 | 
			
		||||
  Event, EventType,
 | 
			
		||||
};
 | 
			
		||||
use crate::engine::event::{Event, EventType, effect::{HtmlInjectRequest, KeySequenceInjectRequest, MarkdownInjectRequest, TextInjectMode, TextInjectRequest}, input::Key, internal::{DiscardPreviousEvent, TextFormat}};
 | 
			
		||||
 | 
			
		||||
pub trait MatchInfoProvider {
 | 
			
		||||
  fn get_force_mode(&self, match_id: i32) -> Option<TextInjectMode>;
 | 
			
		||||
| 
						 | 
				
			
			@ -68,10 +63,18 @@ impl<'a> Middleware for ActionMiddleware<'a> {
 | 
			
		|||
 | 
			
		||||
        Event::caused_by(
 | 
			
		||||
          event.source_id,
 | 
			
		||||
          EventType::TextInject(TextInjectRequest {
 | 
			
		||||
            text: m_event.body.clone(),
 | 
			
		||||
            force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
 | 
			
		||||
          }),
 | 
			
		||||
          match m_event.format {
 | 
			
		||||
            TextFormat::Plain => EventType::TextInject(TextInjectRequest {
 | 
			
		||||
              text: m_event.body.clone(),
 | 
			
		||||
              force_mode: self.match_info_provider.get_force_mode(m_event.match_id),
 | 
			
		||||
            }),
 | 
			
		||||
            TextFormat::Html => EventType::HtmlInject(HtmlInjectRequest {
 | 
			
		||||
              html: m_event.body.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
            TextFormat::Markdown => EventType::MarkdownInject(MarkdownInjectRequest {
 | 
			
		||||
              markdown: m_event.body.clone(),
 | 
			
		||||
            }),
 | 
			
		||||
          },
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      EventType::CursorHintCompensation(m_event) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								espanso/src/engine/process/middleware/markdown.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								espanso/src/engine/process/middleware/markdown.rs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
/*
 | 
			
		||||
 * This file is part of espanso.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019-2021 Federico Terzi
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * espanso is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with espanso.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use super::super::Middleware;
 | 
			
		||||
use crate::engine::event::{Event, EventType, effect::{HtmlInjectRequest}};
 | 
			
		||||
 | 
			
		||||
// Convert markdown injection requests to HTML on the fly
 | 
			
		||||
pub struct MarkdownMiddleware {}
 | 
			
		||||
 | 
			
		||||
impl MarkdownMiddleware {
 | 
			
		||||
  pub fn new() -> Self {
 | 
			
		||||
    Self {}
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Middleware for MarkdownMiddleware {
 | 
			
		||||
  fn name(&self) -> &'static str {
 | 
			
		||||
    "markdown"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
 | 
			
		||||
    if let EventType::MarkdownInject(m_event) = &event.etype {
 | 
			
		||||
      // Render the markdown into HTML
 | 
			
		||||
      let html = markdown::to_html(&m_event.markdown);
 | 
			
		||||
      let mut html = html.trim();
 | 
			
		||||
 | 
			
		||||
      // Remove the surrounding paragraph
 | 
			
		||||
      if html.starts_with("<p>") {
 | 
			
		||||
        html = html.trim_start_matches("<p>");
 | 
			
		||||
      }
 | 
			
		||||
      if html.ends_with("</p>") {
 | 
			
		||||
        html = html.trim_end_matches("</p>");
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return Event::caused_by(event.source_id, EventType::HtmlInject(HtmlInjectRequest {
 | 
			
		||||
        html: html.to_owned(),
 | 
			
		||||
      }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: test
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ pub mod cursor_hint;
 | 
			
		|||
pub mod delay_modifiers;
 | 
			
		||||
pub mod match_select;
 | 
			
		||||
pub mod matcher;
 | 
			
		||||
pub mod markdown;
 | 
			
		||||
pub mod multiplex;
 | 
			
		||||
pub mod past_discard;
 | 
			
		||||
pub mod render;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,6 +62,7 @@ impl<'a> Middleware for RenderMiddleware<'a> {
 | 
			
		|||
            EventType::Rendered(RenderedEvent {
 | 
			
		||||
              match_id: m_event.match_id,
 | 
			
		||||
              body,
 | 
			
		||||
              format: m_event.format,
 | 
			
		||||
            }),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -226,6 +226,7 @@ fn main() {
 | 
			
		|||
      let config = ConfigBuilder::new()
 | 
			
		||||
        .set_time_to_local(true)
 | 
			
		||||
        .set_location_level(LevelFilter::Off)
 | 
			
		||||
        .add_filter_ignore_str("html5ever")
 | 
			
		||||
        .build();
 | 
			
		||||
 | 
			
		||||
      CombinedLogger::init(vec![
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user