add emoji browser and support variables ($) for files

This commit is contained in:
Alexander Mohr 2025-05-03 22:48:57 +02:00
parent 7fd4df26aa
commit e8f90a3ba3
9 changed files with 654 additions and 70 deletions

439
Cargo.lock generated
View file

@ -119,6 +119,15 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "cc"
version = "1.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cfg-expr" name = "cfg-expr"
version = "0.17.2" version = "0.17.2"
@ -188,7 +197,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -289,12 +298,30 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "emoji"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e9309870371f7fa7767752e5048fc0c0675b017a27d9c601dffe9a1efe0301"
dependencies = [
"fuzzy-matcher",
"itertools",
"lazy_static",
"phf",
]
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.3" version = "0.1.3"
@ -340,6 +367,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]] [[package]]
name = "field-offset" name = "field-offset"
version = "0.3.6" version = "0.3.6"
@ -412,7 +445,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -435,6 +468,15 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]] [[package]]
name = "gdk-pixbuf" name = "gdk-pixbuf"
version = "0.20.9" version = "0.20.9"
@ -493,6 +535,17 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -501,7 +554,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
] ]
[[package]] [[package]]
@ -585,7 +650,7 @@ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -721,7 +786,7 @@ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -796,6 +861,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.15"
@ -823,7 +897,7 @@ checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -832,6 +906,12 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.171" version = "0.2.171"
@ -854,6 +934,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.9.4" version = "0.9.4"
@ -937,6 +1023,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_pipe"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
dependencies = [
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.6.1" version = "6.6.1"
@ -977,6 +1073,50 @@ dependencies = [
"indexmap 2.9.0", "indexmap 2.9.0",
] ]
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_shared",
"proc-macro-hack",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
@ -1010,6 +1150,15 @@ dependencies = [
"portable-atomic", "portable-atomic",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.3.0" version = "3.3.0"
@ -1019,6 +1168,12 @@ dependencies = [
"toml_edit", "toml_edit",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.94" version = "1.0.94"
@ -1038,6 +1193,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "quick-xml"
version = "0.37.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.40" version = "1.0.40"
@ -1047,6 +1211,63 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[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",
"rand_core",
"rand_hc",
"rand_pcg",
]
[[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",
]
[[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_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.10.0" version = "1.10.0"
@ -1073,7 +1294,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.2.15",
"libredox", "libredox",
"thiserror", "thiserror",
] ]
@ -1116,6 +1337,19 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.5" version = "1.0.5"
@ -1125,7 +1359,7 @@ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys 0.9.4",
"windows-sys", "windows-sys",
] ]
@ -1158,7 +1392,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
] ]
[[package]] [[package]]
@ -1182,6 +1416,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -1209,6 +1455,17 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.100" version = "2.0.100"
@ -1239,6 +1496,19 @@ version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tempfile"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"fastrand",
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.5",
"windows-sys",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@ -1271,7 +1541,17 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.100",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
] ]
[[package]] [[package]]
@ -1345,12 +1625,97 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wayland-backend"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
dependencies = [
"cc",
"downcast-rs",
"rustix 0.38.44",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
dependencies = [
"bitflags 2.9.0",
"rustix 0.38.44",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols"
version = "0.32.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
dependencies = [
"bitflags 2.9.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
dependencies = [
"bitflags 2.9.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
dependencies = [
"proc-macro2",
"quick-xml 0.37.5",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
dependencies = [
"pkg-config",
]
[[package]] [[package]]
name = "which" name = "which"
version = "7.0.3" version = "7.0.3"
@ -1359,7 +1724,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
dependencies = [ dependencies = [
"either", "either",
"env_home", "env_home",
"rustix", "rustix 1.0.5",
"winsafe", "winsafe",
] ]
@ -1482,6 +1847,34 @@ version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.0",
]
[[package]]
name = "wl-clipboard-rs"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb"
dependencies = [
"libc",
"log",
"os_pipe",
"rustix 0.38.44",
"tempfile",
"thiserror",
"tree_magic_mini",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-protocols-wlr",
]
[[package]] [[package]]
name = "worf" name = "worf"
version = "0.1.0" version = "0.1.0"
@ -1490,6 +1883,7 @@ dependencies = [
"clap 4.5.35", "clap 4.5.35",
"crossbeam", "crossbeam",
"dirs", "dirs",
"emoji",
"env_logger", "env_logger",
"freedesktop-file-parser", "freedesktop-file-parser",
"gdk4", "gdk4",
@ -1508,6 +1902,7 @@ dependencies = [
"toml", "toml",
"tree_magic_mini", "tree_magic_mini",
"which", "which",
"wl-clipboard-rs",
] ]
[[package]] [[package]]
@ -1517,7 +1912,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeac9c0125f3c131c6a2898d2a9f25c11b7954c3ff644a018cb9e06fa92919b" checksum = "5aeac9c0125f3c131c6a2898d2a9f25c11b7954c3ff644a018cb9e06fa92919b"
dependencies = [ dependencies = [
"clap 3.2.25", "clap 3.2.25",
"quick-xml", "quick-xml 0.21.0",
"serde", "serde",
"tini", "tini",
] ]
@ -1536,3 +1931,23 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "zerocopy"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]

View file

@ -47,3 +47,5 @@ meval = "0.2.0"
tree_magic_mini = "3.1.6" tree_magic_mini = "3.1.6"
rayon = "1.10.0" rayon = "1.10.0"
nix = { version = "0.30.0", features = ["process"] } nix = { version = "0.30.0", features = ["process"] }
emoji = "0.2.1"
wl-clipboard-rs = "0.9.2"

View file

@ -13,19 +13,24 @@ It supports various modes:
* File * File
* Ssh * Ssh
* Run * Run
* Emoji
* // WebSearch * // WebSearch
* // Emoji
* Auto * Auto
Auto mode tries to detect the desired mode automatically, i.e. `ssh`, `?` (for web search), `emoji`, `/` or `~` (for file). Auto mode tries to detect the desired mode automatically, to achieve this some modes require a prefix in the search.
The standard view will show `ssh` and `drun`, for other modes the following prefixes are available:
* `ssh` (optional)
* `?` web search
* `/`, `$` or `~` for files
* `emoji` for emojis
<img src="images/demo.gif" style="width:600px;"> <img src="images/demo.gif">
## Not finished ## Not finished
* [ ] key support * [ ] key support
* [ ] full config support * [ ] full config support
* [ ] web search mode * [ ] web search mode
* [ ] emoji finder
* [ ] publish library * [ ] publish library

View file

@ -65,6 +65,9 @@ pub enum Mode {
/// Connect via ssh to a given host /// Connect via ssh to a given host
Ssh, Ssh,
/// Emoji browser
Emoji,
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -98,6 +101,7 @@ impl FromStr for Mode {
"file" => Ok(Mode::File), "file" => Ok(Mode::File),
"math" => Ok(Mode::Math), "math" => Ok(Mode::Math),
"ssh" => Ok(Mode::Ssh), "ssh" => Ok(Mode::Ssh),
"emoji" => Ok(Mode::Emoji),
"auto" => Ok(Mode::Auto), "auto" => Ok(Mode::Auto),
_ => Err(ArgsError::InvalidParameter( _ => Err(ArgsError::InvalidParameter(
format!("{s} is not a valid argument, see help for details").to_owned(), format!("{s} is not a valid argument, see help for details").to_owned(),
@ -210,11 +214,10 @@ pub struct Config {
#[clap(short = 'M', long = "matching")] #[clap(short = 'M', long = "matching")]
matching: Option<MatchMethod>, matching: Option<MatchMethod>,
/// Control if search is case insenstive or not. /// Control if search is case-insensitive or not.
/// Defaults to false /// Defaults to true
#[clap(short = 'i', long = "insensitive")] #[clap(short = 'i', long = "insensitive")]
#[serde(default = "default_true")] insensitive: Option<bool>,
insensitive: bool,
#[clap(short = 'q', long = "parse-search")] #[clap(short = 'q', long = "parse-search")]
parse_search: Option<bool>, // todo support this parse_search: Option<bool>, // todo support this
@ -395,6 +398,7 @@ impl Config {
Mode::File => "file".to_owned(), Mode::File => "file".to_owned(),
Mode::Auto => "auto".to_owned(), Mode::Auto => "auto".to_owned(),
Mode::Ssh => "ssh".to_owned(), Mode::Ssh => "ssh".to_owned(),
Mode::Emoji => "emoji".to_owned(),
}, },
}, },
@ -467,7 +471,7 @@ impl Config {
#[must_use] #[must_use]
pub fn insensitive(&self) -> bool { pub fn insensitive(&self) -> bool {
self.insensitive self.insensitive.unwrap_or(true)
} }
#[must_use] #[must_use]
@ -490,9 +494,9 @@ fn default_false() -> bool {
false false
} }
fn default_true() -> bool { // fn default_true() -> bool {
true // true
} // }
// //
// // TODO // // TODO
@ -644,7 +648,7 @@ pub fn load_config(args_opt: Option<&Config>) -> Result<Config, Error> {
toml::from_str(&toml_content).map_err(|e| Error::ParsingError(format!("{e}")))?; toml::from_str(&toml_content).map_err(|e| Error::ParsingError(format!("{e}")))?;
if let Some(args) = args_opt { if let Some(args) = args_opt {
let merge_result = merge_config_with_args(&mut config, &args) let merge_result = merge_config_with_args(&mut config, args)
.map_err(|e| Error::ParsingError(format!("{e}")))?; .map_err(|e| Error::ParsingError(format!("{e}")))?;
Ok(merge_result) Ok(merge_result)
} else { } else {

View file

@ -1,8 +1,3 @@
use crate::Error;
use crate::config::expand_path;
use freedesktop_file_parser::DesktopFile;
use rayon::prelude::*;
use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use std::os::unix::prelude::CommandExt; use std::os::unix::prelude::CommandExt;
@ -12,6 +7,14 @@ use std::process::{Command, Stdio};
use std::time::Instant; use std::time::Instant;
use std::{env, fs, io}; use std::{env, fs, io};
use freedesktop_file_parser::DesktopFile;
use rayon::prelude::*;
use regex::Regex;
use wl_clipboard_rs::copy::{ClipboardType, MimeType, ServeRequests, Source};
use crate::Error;
use crate::config::expand_path;
/// Returns a regex with supported image extensions /// Returns a regex with supported image extensions
/// # Panics /// # Panics
/// ///
@ -291,3 +294,18 @@ pub fn is_executable(entry: &Path) -> bool {
false false
} }
} }
/// Copy the given text into the clipboard.
/// # Errors
/// Will return an error if copying to the clipboard failed.
pub fn copy_to_clipboard(text: String) -> Result<(), Error> {
let mut opts = wl_clipboard_rs::copy::Options::new();
opts.clipboard(ClipboardType::Regular);
opts.serve_requests(ServeRequests::Only(1));
let result = opts.copy(Source::Bytes(text.into_bytes().into()), MimeType::Text);
match result {
Ok(()) => Ok(()),
Err(e) => Err(Error::Clipboard(e.to_string())),
}
}

View file

@ -4,7 +4,6 @@ use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use anyhow::anyhow;
use crossbeam::channel; use crossbeam::channel;
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use gdk4::gio::File; use gdk4::gio::File;
@ -28,11 +27,11 @@ use regex::Regex;
use crate::config::{Anchor, Config, MatchMethod, WrapMode}; use crate::config::{Anchor, Config, MatchMethod, WrapMode};
use crate::desktop::known_image_extension_regex_pattern; use crate::desktop::known_image_extension_regex_pattern;
use crate::{config, desktop}; use crate::{Error, config, desktop};
type ArcMenuMap<T> = Arc<Mutex<HashMap<FlowBoxChild, MenuItem<T>>>>; type ArcMenuMap<T> = Arc<Mutex<HashMap<FlowBoxChild, MenuItem<T>>>>;
type ArcProvider<T> = Arc<Mutex<dyn ItemProvider<T> + Send>>; type ArcProvider<T> = Arc<Mutex<dyn ItemProvider<T> + Send>>;
type MenuItemSender<T> = Sender<Result<MenuItem<T>, anyhow::Error>>; type MenuItemSender<T> = Sender<Result<MenuItem<T>, Error>>;
pub trait ItemProvider<T: Clone> { pub trait ItemProvider<T: Clone> {
fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>); fn get_elements(&mut self, search: Option<&str>) -> (bool, Vec<MenuItem<T>>);
@ -141,6 +140,7 @@ struct MetaData<T: Clone> {
selected_sender: MenuItemSender<T>, selected_sender: MenuItemSender<T>,
config: Rc<Config>, config: Rc<Config>,
new_on_empty: bool, new_on_empty: bool,
search_ignored_words: Option<Vec<Regex>>,
} }
struct UiElements<T: Clone> { struct UiElements<T: Clone> {
@ -159,12 +159,13 @@ pub fn show<T, P>(
config: Config, config: Config,
item_provider: P, item_provider: P,
new_on_empty: bool, new_on_empty: bool,
) -> Result<MenuItem<T>, anyhow::Error> search_ignored_words: Option<Vec<Regex>>,
) -> Result<MenuItem<T>, Error>
where where
T: Clone + 'static + Send, T: Clone + 'static + Send,
P: ItemProvider<T> + 'static + Clone + Send, P: ItemProvider<T> + 'static + Clone + Send,
{ {
gtk4::init()?; gtk4::init().map_err(|e| Error::Graphics(e.to_string()))?;
log::debug!("Starting GUI"); log::debug!("Starting GUI");
if let Some(ref css) = config.style() { if let Some(ref css) = config.style() {
let provider = CssProvider::new(); let provider = CssProvider::new();
@ -189,20 +190,22 @@ where
sender.clone(), sender.clone(),
app.clone(), app.clone(),
new_on_empty, new_on_empty,
search_ignored_words.clone(),
); );
}); });
let gtk_args: [&str; 0] = []; let gtk_args: [&str; 0] = [];
app.run_with_args(&gtk_args); app.run_with_args(&gtk_args);
receiver.recv()? receiver.recv().map_err(|e| Error::Io(e.to_string()))?
} }
fn build_ui<T, P>( fn build_ui<T, P>(
config: &Config, config: &Config,
item_provider: P, item_provider: P,
sender: Sender<Result<MenuItem<T>, anyhow::Error>>, sender: Sender<Result<MenuItem<T>, Error>>,
app: Application, app: Application,
new_on_empty: bool, new_on_empty: bool,
search_ignored_words: Option<Vec<Regex>>,
) where ) where
T: Clone + 'static + Send, T: Clone + 'static + Send,
P: ItemProvider<T> + 'static + Send, P: ItemProvider<T> + 'static + Send,
@ -214,6 +217,7 @@ fn build_ui<T, P>(
selected_sender: sender, selected_sender: sender,
config: Rc::new(config.clone()), config: Rc::new(config.clone()),
new_on_empty, new_on_empty,
search_ignored_words,
}); });
let provider_clone = Arc::clone(&meta.item_provider); let provider_clone = Arc::clone(&meta.item_provider);
@ -377,7 +381,12 @@ fn build_ui_from_menu_items<T: Clone + 'static>(
let query = ui_clone.search.text(); let query = ui_clone.search.text();
let menus = &mut *lock; let menus = &mut *lock;
set_menu_visibility_for_search(&query, menus, &meta_clone.config); set_menu_visibility_for_search(
&query,
menus,
&meta_clone.config,
meta_clone.search_ignored_words.as_ref(),
);
} }
let items_sort = ArcMenuMap::clone(&ui_clone.menu_rows); let items_sort = ArcMenuMap::clone(&ui_clone.menu_rows);
@ -390,7 +399,11 @@ fn build_ui_from_menu_items<T: Clone + 'static>(
let menus = &mut *lock; let menus = &mut *lock;
select_first_visible_child(menus, &ui_clone.main_box); select_first_visible_child(menus, &ui_clone.main_box);
log::debug!("Created menu items in {:?}", start.elapsed()); log::debug!(
"Created {} menu items in {:?}",
menus.len(),
start.elapsed()
);
ControlFlow::Break ControlFlow::Break
} else { } else {
ControlFlow::Continue ControlFlow::Continue
@ -421,7 +434,12 @@ fn handle_key_press<T: Clone + 'static>(
let update_view = |query: &String| { let update_view = |query: &String| {
let mut lock = ui.menu_rows.lock().unwrap(); let mut lock = ui.menu_rows.lock().unwrap();
let menus = &mut *lock; let menus = &mut *lock;
set_menu_visibility_for_search(query, menus, &meta.config); set_menu_visibility_for_search(
query,
menus,
&meta.config,
meta.search_ignored_words.as_ref(),
);
select_first_visible_child(&*lock, &ui.main_box); select_first_visible_child(&*lock, &ui.main_box);
}; };
@ -436,7 +454,7 @@ fn handle_key_press<T: Clone + 'static>(
match keyboard_key { match keyboard_key {
Key::Escape => { Key::Escape => {
if let Err(e) = meta.selected_sender.send(Err(anyhow!("No item selected"))) { if let Err(e) = meta.selected_sender.send(Err(Error::NoSelection)) {
log::error!("failed to send message {e}"); log::error!("failed to send message {e}");
} }
close_gui(ui.app.clone(), ui.window.clone(), &meta.config); close_gui(ui.app.clone(), ui.window.clone(), &meta.config);
@ -879,6 +897,8 @@ fn parse_label(label: &str) -> (Option<String>, Option<String>) {
// Treat as fallback text if no text tag is present // Treat as fallback text if no text tag is present
if text.is_none() { if text.is_none() {
text = Some((*other.unwrap_or(&"")).to_string()); text = Some((*other.unwrap_or(&"")).to_string());
} else {
text = Some(text.unwrap() + ":" + (*other.unwrap_or(&"")));
} }
i += 1; i += 1;
} }
@ -917,6 +937,7 @@ fn set_menu_visibility_for_search<T: Clone>(
query: &str, query: &str,
items: &mut HashMap<FlowBoxChild, MenuItem<T>>, items: &mut HashMap<FlowBoxChild, MenuItem<T>>,
config: &Config, config: &Config,
search_ignored_words: Option<&Vec<Regex>>,
) { ) {
{ {
if query.is_empty() { if query.is_empty() {
@ -928,20 +949,37 @@ fn set_menu_visibility_for_search<T: Clone>(
return; return;
} }
let query = if config.insensitive() { let mut query = if config.insensitive() {
query.to_owned().to_lowercase() query.to_owned().to_lowercase()
} else { } else {
query.to_owned() query.to_owned()
}; };
if let Some(s) = search_ignored_words.as_ref() {
s.iter().for_each(|rgx| {
query = rgx.replace_all(&query, "").to_string();
});
}
for (fb, menu_item) in items.iter_mut() { for (fb, menu_item) in items.iter_mut() {
let menu_item_search = format!( let menu_item_search = format!(
"{} {}", "{} {}",
menu_item menu_item
.action .action
.as_ref() .as_ref()
.map(|a| a.to_lowercase()) .map(|a| {
if config.insensitive() {
a.to_lowercase()
} else {
a.clone()
}
})
.unwrap_or_default(), .unwrap_or_default(),
&menu_item.label.to_lowercase() if config.insensitive() {
menu_item.label.to_lowercase()
} else {
menu_item.label.clone()
}
); );
let (search_sort_score, visible) = match config.match_method() { let (search_sort_score, visible) = match config.match_method() {

View file

@ -22,6 +22,12 @@ pub enum Error {
RunFailed(String), RunFailed(String),
/// An IO operation failed /// An IO operation failed
Io(String), Io(String),
/// An error occurred while accessing the clipboard
Clipboard(String),
/// Graphical subsystem related error
Graphics(String),
/// Nothing selected
NoSelection,
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -46,6 +52,15 @@ impl fmt::Display for Error {
Error::Io(s) => { Error::Io(s) => {
write!(f, "IO {s}") write!(f, "IO {s}")
} }
Error::Clipboard(s) => {
write!(f, "Clipboard {s}")
}
Error::Graphics(s) => {
write!(f, "graphics {s}")
}
Error::NoSelection => {
write!(f, "NoSelection")
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
use crate::config::{Config, expand_path}; use crate::config::{Config, expand_path};
use crate::desktop::{ use crate::desktop::{
create_file_if_not_exists, find_desktop_files, get_locale_variants, is_executable, copy_to_clipboard, create_file_if_not_exists, find_desktop_files, get_locale_variants,
load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork, is_executable, load_cache_file, lookup_name_with_locale, save_cache_file, spawn_fork,
}; };
use crate::gui::{ItemProvider, MenuItem}; use crate::gui::{ItemProvider, MenuItem};
use crate::{Error, gui}; use crate::{Error, gui};
@ -309,7 +309,10 @@ impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
}; };
let mut trimmed_search = search.unwrap_or(&default_path).to_owned(); let mut trimmed_search = search.unwrap_or(&default_path).to_owned();
if !trimmed_search.starts_with('/') && !trimmed_search.starts_with('~') { if !trimmed_search.starts_with('/')
&& !trimmed_search.starts_with('~')
&& !trimmed_search.starts_with('$')
{
trimmed_search = format!("{default_path}/{trimmed_search}"); trimmed_search = format!("{default_path}/{trimmed_search}");
} }
@ -503,6 +506,49 @@ impl<T: Clone> ItemProvider<T> for MathProvider<T> {
} }
} }
#[derive(Clone)]
struct EmojiProvider<T: Clone> {
elements: Vec<MenuItem<T>>,
#[allow(dead_code)] // needed for the detection of mode in 'auto'
menu_item_data: T,
}
impl<T: Clone> EmojiProvider<T> {
fn new(data: T) -> Self {
let emoji = emoji::search::search_annotation_all("");
let mut menus = emoji
.into_iter()
.map(|e| {
MenuItem::new(
format!("{} — Category: {} — Name: {}", e.glyph, e.group, e.name),
None,
Some(format!("emoji {}", e.glyph)),
vec![],
None,
0.0,
Some(data.clone()),
)
})
.collect::<Vec<_>>();
gui::sort_menu_items_alphabetically_honor_initial_score(&mut menus);
Self {
elements: menus,
menu_item_data: data.clone(),
}
}
}
impl<T: Clone> ItemProvider<T> for EmojiProvider<T> {
fn get_elements(&mut self, _: Option<&str>) -> (bool, Vec<MenuItem<T>>) {
(false, self.elements.clone())
}
fn get_sub_elements(&mut self, _: &MenuItem<T>) -> (bool, Option<Vec<MenuItem<T>>>) {
(false, None)
}
}
#[derive(Clone)] #[derive(Clone)]
struct DMenuProvider { struct DMenuProvider {
items: Vec<MenuItem<String>>, items: Vec<MenuItem<String>>,
@ -541,9 +587,8 @@ enum AutoRunType {
DRun, DRun,
File, File,
Ssh, Ssh,
Emoji,
// WebSearch, // WebSearch,
// Emoji,
// Run,
} }
#[derive(Clone)] #[derive(Clone)]
@ -552,6 +597,7 @@ struct AutoItemProvider {
file: FileItemProvider<AutoRunType>, file: FileItemProvider<AutoRunType>,
math: MathProvider<AutoRunType>, math: MathProvider<AutoRunType>,
ssh: SshProvider<AutoRunType>, ssh: SshProvider<AutoRunType>,
emoji: EmojiProvider<AutoRunType>,
last_mode: Option<AutoRunType>, last_mode: Option<AutoRunType>,
} }
@ -562,6 +608,7 @@ impl AutoItemProvider {
file: FileItemProvider::new(AutoRunType::File), file: FileItemProvider::new(AutoRunType::File),
math: MathProvider::new(AutoRunType::Math), math: MathProvider::new(AutoRunType::Math),
ssh: SshProvider::new(AutoRunType::Ssh), ssh: SshProvider::new(AutoRunType::Ssh),
emoji: EmojiProvider::new(AutoRunType::Emoji),
last_mode: None, last_mode: None,
} }
} }
@ -597,6 +644,8 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
(AutoRunType::File, self.file.get_elements(search_opt)) (AutoRunType::File, self.file.get_elements(search_opt))
} else if search.starts_with("ssh") { } else if search.starts_with("ssh") {
(AutoRunType::Ssh, self.ssh.get_elements(search_opt)) (AutoRunType::Ssh, self.ssh.get_elements(search_opt))
} else if search.starts_with("emoji") {
(AutoRunType::Emoji, self.emoji.get_elements(search_opt))
} else { } else {
return self.default_auto_elements(search_opt); return self.default_auto_elements(search_opt);
}; };
@ -623,12 +672,12 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
/// ///
/// Will return `Err` if it was not able to spawn the process /// Will return `Err` if it was not able to spawn the process
pub fn d_run(config: &Config) -> Result<(), Error> { pub fn d_run(config: &Config) -> Result<(), Error> {
let provider = DRunProvider::new(String::new()); let provider = DRunProvider::new(0);
let cache_path = provider.cache_path.clone(); let cache_path = provider.cache_path.clone();
let mut cache = provider.cache.clone(); let mut cache = provider.cache.clone();
// todo ues a arc instead of cloning the config // todo ues a arc instead of cloning the config
let selection_result = gui::show(config.clone(), provider, false); let selection_result = gui::show(config.clone(), provider, false, None);
match selection_result { match selection_result {
Ok(s) => update_drun_cache_and_run(cache_path, &mut cache, s)?, Ok(s) => update_drun_cache_and_run(cache_path, &mut cache, s)?,
Err(_) => { Err(_) => {
@ -648,7 +697,7 @@ pub fn run(config: &Config) -> Result<(), Error> {
let cache_path = provider.cache_path.clone(); let cache_path = provider.cache_path.clone();
let mut cache = provider.cache.clone(); let mut cache = provider.cache.clone();
let selection_result = gui::show(config.clone(), provider, false); let selection_result = gui::show(config.clone(), provider, false, None);
match selection_result { match selection_result {
Ok(s) => update_run_cache_and_run(cache_path, &mut cache, s)?, Ok(s) => update_run_cache_and_run(cache_path, &mut cache, s)?,
Err(_) => { Err(_) => {
@ -664,6 +713,9 @@ pub fn run(config: &Config) -> Result<(), Error> {
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
///
/// # Panics
/// Panics if an internal static regex cannot be passed anymore, should never happen
pub fn auto(config: &Config) -> Result<(), Error> { pub fn auto(config: &Config) -> Result<(), Error> {
let mut provider = AutoItemProvider::new(); let mut provider = AutoItemProvider::new();
let cache_path = provider.drun.cache_path.clone(); let cache_path = provider.drun.cache_path.clone();
@ -671,7 +723,17 @@ pub fn auto(config: &Config) -> Result<(), Error> {
loop { loop {
// todo ues a arc instead of cloning the config // todo ues a arc instead of cloning the config
let selection_result = gui::show(config.clone(), provider.clone(), true); let selection_result = gui::show(
config.clone(),
provider.clone(),
true,
Some(
vec!["ssh", "emoji", "^\\$\\w+"]
.into_iter()
.map(|s| Regex::new(s).unwrap())
.collect(),
),
);
if let Ok(mut selection_result) = selection_result { if let Ok(mut selection_result) = selection_result {
if let Some(data) = &selection_result.data { if let Some(data) = &selection_result.data {
@ -693,6 +755,14 @@ pub fn auto(config: &Config) -> Result<(), Error> {
ssh_launch(&selection_result, config)?; ssh_launch(&selection_result, config)?;
break; break;
} }
AutoRunType::Emoji => {
if let Some(action) = selection_result.action {
copy_to_clipboard(action)?;
} else {
return Err(Error::MissingAction);
}
break;
}
} }
} else if selection_result.label.starts_with("ssh") { } else if selection_result.label.starts_with("ssh") {
selection_result.label = selection_result.label.chars().skip(4).collect(); selection_result.label = selection_result.label.chars().skip(4).collect();
@ -712,23 +782,24 @@ pub fn auto(config: &Config) -> Result<(), Error> {
/// ///
/// Will return `Err` /// Will return `Err`
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
///
/// # Panics
/// In case an internal regex does not parse anymore, this should never happen
pub fn file(config: &Config) -> Result<(), Error> { pub fn file(config: &Config) -> Result<(), Error> {
let provider = FileItemProvider::new(String::new()); let provider = FileItemProvider::new(0);
// todo ues a arc instead of cloning the config // todo ues a arc instead of cloning the config
let selection_result = gui::show(config.clone(), provider, false); let selection_result = gui::show(
match selection_result { config.clone(),
Ok(s) => { provider,
if let Some(action) = s.action { false,
spawn_fork(&action, s.working_dir.as_ref())?; Some(vec![Regex::new("^\\$\\w+").unwrap()]),
} )?;
} if let Some(action) = selection_result.action {
Err(_) => { spawn_fork(&action, selection_result.working_dir.as_ref())
log::error!("No item selected"); } else {
} Err(Error::MissingAction)
} }
Ok(())
} }
fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(), Error> { fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(), Error> {
@ -759,8 +830,8 @@ fn ssh_launch<T: Clone>(menu_item: &MenuItem<T>, config: &Config) -> Result<(),
/// * if it was not able to spawn the process /// * if it was not able to spawn the process
/// * if it didn't find a terminal /// * if it didn't find a terminal
pub fn ssh(config: &Config) -> Result<(), Error> { pub fn ssh(config: &Config) -> Result<(), Error> {
let provider = SshProvider::new(String::new()); let provider = SshProvider::new(0);
let selection_result = gui::show(config.clone(), provider, true); let selection_result = gui::show(config.clone(), provider, true, None);
if let Ok(mi) = selection_result { if let Ok(mi) = selection_result {
ssh_launch(&mi, config)?; ssh_launch(&mi, config)?;
} else { } else {
@ -775,7 +846,7 @@ pub fn math(config: &Config) {
loop { loop {
let mut provider = MathProvider::new(String::new()); let mut provider = MathProvider::new(String::new());
provider.add_elements(&mut calc.clone()); provider.add_elements(&mut calc.clone());
let selection_result = gui::show(config.clone(), provider, true); let selection_result = gui::show(config.clone(), provider, true, None);
if let Ok(mi) = selection_result { if let Ok(mi) = selection_result {
calc.push(mi); calc.push(mi);
} else { } else {
@ -785,6 +856,19 @@ pub fn math(config: &Config) {
} }
} }
/// Shows the emoji mode
/// # Errors
///
/// Forwards errors from the gui. See `gui::show` for details.
pub fn emoji(config: &Config) -> Result<(), Error> {
let provider = EmojiProvider::new(0);
let selection_result = gui::show(config.clone(), provider, true, None)?;
match selection_result.action {
None => Err(Error::MissingAction),
Some(action) => copy_to_clipboard(action),
}
}
/// Shows the dmenu mode /// Shows the dmenu mode
/// # Errors /// # Errors
/// ///
@ -792,7 +876,7 @@ pub fn math(config: &Config) {
pub fn dmenu(config: &Config) -> Result<(), Error> { pub fn dmenu(config: &Config) -> Result<(), Error> {
let provider = DMenuProvider::new()?; let provider = DMenuProvider::new()?;
let selection_result = gui::show(config.clone(), provider, true); let selection_result = gui::show(config.clone(), provider, true, None);
match selection_result { match selection_result {
Ok(s) => { Ok(s) => {
println!("{}", s.label); println!("{}", s.label);

View file

@ -32,6 +32,9 @@ fn main() -> anyhow::Result<()> {
Mode::Ssh => { Mode::Ssh => {
mode::ssh(&config).map_err(|e| anyhow!(e))?; mode::ssh(&config).map_err(|e| anyhow!(e))?;
} }
Mode::Emoji => {
mode::emoji(&config).map_err(|e| anyhow!(e))?;
}
Mode::Auto => { Mode::Auto => {
mode::auto(&config).map_err(|e| anyhow!(e))?; mode::auto(&config).map_err(|e| anyhow!(e))?;
} }