Use fuzzy search instead of regex search

Ref: https://github.com/sayanarijit/xplr/issues/496
pull/514/head v0.19.4
Arijit Basu 2 years ago committed by Arijit Basu
parent 93bd53bbcb
commit 57483bef41

290
Cargo.lock generated

@ -20,11 +20,17 @@ dependencies = [
"libc",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "ansi-to-tui"
version = "1.0.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1213bdcb277c5489f80dca52a5342ca1bc5cbc8deba211714da49ac669336f22"
checksum = "3460d7beaf8b192c09a55933da038ccd514f00efdb37d7d87f3ce078336b47e9"
dependencies = [
"nom",
"thiserror",
@ -33,9 +39,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.64"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
[[package]]
name = "assert_cmd"
@ -83,7 +89,6 @@ dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
@ -132,15 +137,52 @@ dependencies = [
"winapi",
]
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "2.34.0"
version = "3.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
"unicode-width",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
@ -151,15 +193,16 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "criterion"
version = "0.3.6"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
@ -168,7 +211,6 @@ dependencies = [
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
@ -177,9 +219,9 @@ dependencies = [
[[package]]
name = "criterion-plot"
version = "0.4.5"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
@ -208,42 +250,24 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.10"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "crossterm"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
@ -271,28 +295,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa 0.4.8",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "difflib"
version = "0.4.0"
@ -340,6 +342,15 @@ dependencies = [
"serde",
]
[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]]
name = "gethostname"
version = "0.2.3"
@ -384,20 +395,22 @@ dependencies = [
[[package]]
name = "humansize"
version = "1.1.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
checksum = "a866837516f34ad34fb221f3ee01fd0db75f2c2f6abeda2047dc6963fb04ad9a"
dependencies = [
"libm",
]
[[package]]
name = "iana-time-zone"
version = "0.1.47"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7"
checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"once_cell",
"wasm-bindgen",
"winapi",
]
@ -415,19 +428,13 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.3"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.3"
@ -436,9 +443,9 @@ checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "js-sys"
version = "0.3.59"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
@ -451,9 +458,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.132"
version = "0.2.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
[[package]]
name = "libm"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
[[package]]
name = "linked-hash-map"
@ -463,9 +476,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
@ -491,9 +504,9 @@ dependencies = [
[[package]]
name = "luajit-src"
version = "210.4.1+restyaa7a722"
version = "210.4.3+resty8384278"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92879345f9a97ee140cfe2e08eff49b101533d784527d46ce6d2dc0096d27b3"
checksum = "19ee5d5afddf1ec76ffa55ca7c3001f2f8a703834beba53c56a38ea6641cef44"
dependencies = [
"cc",
]
@ -612,9 +625,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "oorandom"
@ -622,6 +635,12 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -708,9 +727,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.43"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
dependencies = [
"unicode-ident",
]
@ -820,28 +839,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.144"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
@ -854,7 +863,7 @@ version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa 1.0.3",
"itoa",
"ryu",
"serde",
]
@ -903,15 +912,15 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.9.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "syn"
version = "1.0.99"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
dependencies = [
"proc-macro2",
"quote",
@ -926,33 +935,39 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
[[package]]
name = "thiserror"
version = "1.0.34"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.34"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.1.44"
@ -976,13 +991,13 @@ dependencies = [
[[package]]
name = "tui"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fe69244ec2af261bced1d9046a6fee6c8c2a6b0228e59e5ba39bc8ba4ed729"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags",
"cassowary",
"crossterm 0.23.2",
"crossterm",
"serde",
"unicode-segmentation",
"unicode-width",
@ -990,11 +1005,11 @@ dependencies = [
[[package]]
name = "tui-input"
version = "0.5.1"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fd9573b9b1940b491977c1877284e36953a8cf4f8836cf5dc82996912485de"
checksum = "e4efaf26a91ee5307d54e94e95c6772c522a005feb5a959bf3b57e07f49e0f23"
dependencies = [
"crossterm 0.25.0",
"crossterm",
"serde",
]
@ -1009,21 +1024,21 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "version_check"
@ -1065,9 +1080,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -1075,9 +1090,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
@ -1090,9 +1105,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1100,9 +1115,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
@ -1113,15 +1128,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.59"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -1203,15 +1218,16 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "xplr"
version = "0.19.3"
version = "0.19.4"
dependencies = [
"ansi-to-tui",
"anyhow",
"assert_cmd",
"chrono",
"criterion",
"crossterm 0.25.0",
"crossterm",
"dirs",
"fuzzy-matcher",
"gethostname",
"humansize",
"indexmap",

@ -8,7 +8,7 @@ path = './benches/criterion.rs'
[package]
name = 'xplr'
version = '0.19.3'
version = '0.19.4'
authors = ['Arijit Basu <hi@arijitbasu.in>']
edition = '2021'
description = 'A hackable, minimal, fast TUI file explorer'
@ -22,19 +22,20 @@ categories = ['command-line-interface', 'command-line-utilities']
include = ['src/**/*', 'docs/en/src/**/*', 'LICENSE', 'README.md']
[dependencies]
libc = "0.2.132"
humansize = "1.1.1"
libc = "0.2.134"
humansize = "2.1.0"
natord = "1.0.9"
anyhow = "1.0.64"
anyhow = "1.0.65"
# Let's keep this locked. See https://docs.rs/serde_yaml/0.9.11/serde_yaml/
# Let's keep this locked to 0.8.26. See https://docs.rs/serde_yaml/0.9.11/serde_yaml/
serde_yaml = "0.8.26"
crossterm = "0.25.0"
dirs = "4.0.0"
ansi-to-tui = "1.0.1"
ansi-to-tui = "2.0.0"
regex = "1.6.0"
gethostname = "0.2.3"
fuzzy-matcher = "0.3.7"
[dependencies.lazy_static]
version = "1.4.0"
@ -45,12 +46,12 @@ version = "2.0.4"
features = ["rev-mappings"]
[dependencies.tui]
version = "0.18.0"
version = "0.19.0"
default-features = false
features = ['crossterm', 'serde']
[dependencies.serde]
version = "1.0.144"
version = "1.0.145"
features = ['derive']
[dependencies.chrono]
@ -66,11 +67,11 @@ version = "0.8.3"
features = ['luajit', 'vendored', 'serialize', 'send']
[dependencies.tui-input]
version = "0.5.1"
version = "0.6.0"
features = ['serde']
[dev-dependencies]
criterion = "0.3.6"
criterion = "0.4.0"
assert_cmd = "2.0.4"
[profile.release]

@ -11,6 +11,7 @@ of the following plugins work for you, it's very easy to
- [**sayanarijit/dual-pane.xplr**][43] Implements support for dual-pane navigation into xplr.
- [**sayanarijit/map.xplr**][38] Visually inspect and interactively execute batch commands using xplr.
- [**sayanarijit/offline-docs.xplr**][51] Fetch the appropriate version of xplr docs and browse offline.
- [**sayanarijit/regex-search.xplr**][55] Bring back the regex based seach in xplr.
- [**sayanarijit/registers.xplr**][49] Use multiple registers to store the selected paths.
- [**sayanarijit/type-to-nav.xplr**][28] Inspired by [nnn's type-to-nav mode][29] for xplr,
with some tweaks.
@ -109,3 +110,4 @@ of the following plugins work for you, it's very easy to
[52]: https://github.com/sayanarijit/wl-clipboard.xplr
[53]: https://github.com/Junker/nuke.xplr
[54]: https://github.com/sayanarijit/scp.xplr
[55]: https://github.com/sayanarijit/regex-search.xplr

@ -7,14 +7,10 @@ requirements.
When you press `?` in [default mode][3], you can see the complete list
of [modes][4] and the key mappings for each mode.
### create_directory
| key | remaps | action |
| ------ | ------ | ------------ |
| ctrl-c | | terminate |
| enter | | submit |
| esc | | cancel |
| tab | | try complete |
[1]: https://www.vim.org/
[2]: https://github.com/jarun/nnn/
[3]: #default
[4]: modes.md
### default
@ -26,7 +22,6 @@ of [modes][4] and the key mappings for each mode.
| ? | | global help menu |
| G | | go to bottom |
| V | ctrl-a | select/unselect all |
| ctrl-c | | terminate |
| ctrl-d | | duplicate as |
| ctrl-i | tab | next visited path |
| ctrl-o | | last visited path |
@ -48,86 +43,64 @@ of [modes][4] and the key mappings for each mode.
| ~ | | go home |
| [0-9] | | input |
### duplicate_as
### go_to_path
| key | remaps | action |
| ------ | ------ | --------- |
| ctrl-c | | terminate |
| enter | | duplicate |
| esc | | cancel |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### relative_path_does_not_match_regex
| key | remaps | action |
| ------ | ------ | --------- |
| ctrl-c | | terminate |
| enter | | submit |
| esc | | cancel |
| key | remaps | action |
| ----- | ------ | ------ |
| enter | | submit |
### number
| key | remaps | action |
| ------ | ------ | --------- |
| ctrl-c | | terminate |
| down | j | to down |
| enter | | to index |
| esc | | cancel |
| k | up | to up |
| [0-9] | | input |
| key | remaps | action |
| ----- | ------ | -------- |
| down | j | to down |
| enter | | to index |
| k | up | to up |
| [0-9] | | input |
### search
| key | remaps | action |
| ------ | ------ | ---------------- |
| ctrl-c | | terminate |
| ctrl-n | down | down |
| ctrl-p | up | up |
| enter | esc | focus |
| left | | back |
| right | | enter |
| tab | | toggle selection |
### quit
### selection_ops
| key | remaps | action |
| ------ | ------ | ----------------------- |
| ctrl-c | | terminate |
| enter | | just quit |
| esc | | cancel |
| f | | quit printing focus |
| p | | quit printing pwd |
| r | | quit printing result |
| s | | quit printing selection |
| key | remaps | action |
| --- | ------ | ----------- |
| c | | copy here |
| m | | move here |
| x | | open in gui |
### create
### duplicate_as
| key | remaps | action |
| ------ | ------ | ---------------- |
| ctrl-c | | terminate |
| d | | create directory |
| esc | | cancel |
| f | | create file |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### filter
### action
| key | remaps | action |
| --------- | ------ | ---------------------------------- |
| R | | relative path does not match regex |
| backspace | | remove last filter |
| ctrl-c | | terminate |
| ctrl-r | | reset filters |
| ctrl-u | | clear filters |
| enter | esc | submit |
| r | | relative path does match regex |
| key | remaps | action |
| ----- | ------ | -------------------- |
| ! | | shell |
| c | | create |
| e | | open in editor |
| l | | logs |
| m | | toggle mouse |
| q | | quit options |
| s | | selection operations |
| [0-9] | | go to index |
### delete
### go_to
| key | remaps | action |
| ------ | ------ | ------------ |
| D | | force delete |
| ctrl-c | | terminate |
| d | | delete |
| esc | | cancel |
| key | remaps | action |
| --- | ------ | -------------- |
| f | | follow symlink |
| g | | top |
| p | | path |
| x | | open in gui |
### sort
@ -143,116 +116,106 @@ of [modes][4] and the key mappings for each mode.
| S | | by size reverse |
| backspace | | remove last sorter |
| c | | by created |
| ctrl-c | | terminate |
| ctrl-r | | reset sorters |
| ctrl-u | | clear sorters |
| e | | by canonical extension |
| enter | esc | submit |
| enter | | submit |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |
### go_to
### rename
| key | remaps | action |
| ------ | ------ | -------------- |
| ctrl-c | | terminate |
| esc | | cancel |
| f | | follow symlink |
| g | | top |
| p | | path |
| x | | open in gui |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### create_file
### debug_error
| key | remaps | action |
| ------ | ------ | ------------ |
| ctrl-c | | terminate |
| enter | | submit |
| esc | | cancel |
| tab | | try complete |
| key | remaps | action |
| ----- | ------ | ------------------- |
| enter | | open logs in editor |
| q | | quit |
### recover
### switch_layout
| key | remaps | action |
| ------ | ------ | --------- |
| ctrl-c | | terminate |
| esc | | escape |
| key | remaps | action |
| --- | ------ | -------------------- |
| 1 | | default |
| 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
### action
### search
| key | remaps | action |
| ------ | ------ | -------------------- |
| ! | | shell |
| c | | create |
| ctrl-c | | terminate |
| e | | open in editor |
| esc | | cancel |
| l | | logs |
| m | | toggle mouse |
| q | | quit options |
| s | | selection operations |
| [0-9] | | go to index |
| key | remaps | action |
| ------ | ------ | ---------------- |
| ctrl-n | down | down |
| ctrl-p | up | up |
| enter | | submit |
| esc | | cancel |
| left | | back |
| right | | enter |
| tab | | toggle selection |
### switch_layout
### create_file
| key | remaps | action |
| ------ | ------ | -------------------- |
| 1 | | default |
| 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
| ctrl-c | | terminate |
| esc | | cancel |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### relative_path_does_match_regex
### recover
| key | remaps | action |
| ------ | ------ | --------- |
| ctrl-c | | terminate |
| enter | | submit |
| esc | | cancel |
| key | remaps | action |
| --- | ------ | ------ |
### debug_error
### create
| key | remaps | action |
| ------ | ------ | ------------------- |
| ctrl-c | | terminate |
| enter | | open logs in editor |
| esc | | escape |
| q | | quit |
| key | remaps | action |
| --- | ------ | ---------------- |
| d | | create directory |
| f | | create file |
### go_to_path
### filter
| key | remaps | action |
| ------ | ------ | ------------ |
| ctrl-c | | terminate |
| enter | | submit |
| esc | | cancel |
| tab | | try complete |
| key | remaps | action |
| --------- | ------ | ---------------------------------- |
| R | | relative path does not match regex |
| backspace | | remove last filter |
| ctrl-r | | reset filters |
| ctrl-u | | clear filters |
| r | | relative path does match regex |
### selection_ops
### delete
| key | remaps | action |
| ------ | ------ | ----------- |
| c | | copy here |
| ctrl-c | | terminate |
| esc | | cancel |
| m | | move here |
| x | | open in gui |
| key | remaps | action |
| --- | ------ | ------------ |
| D | | force delete |
| d | | delete |
### rename
### create_directory
| key | remaps | action |
| ------ | ------ | ------------ |
| ctrl-c | | terminate |
| enter | | rename |
| esc | | cancel |
| tab | | try complete |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
[1]: https://www.vim.org/
[2]: https://github.com/jarun/nnn/
[3]: #default
[4]: modes.md
### quit
| key | remaps | action |
| ----- | ------ | ----------------------- |
| enter | | just quit |
| f | | quit printing focus |
| p | | quit printing pwd |
| r | | quit printing result |
| s | | quit printing selection |
### relative_path_does_match_regex
| key | remaps | action |
| ----- | ------ | ------ |
| enter | | submit |

@ -322,6 +322,12 @@ Type: nullable mapping of the following key-value pairs:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.search_identifier
The identifiers used to denote applied search input.
Type: { format = nullable string, style = [Style](https://xplr.dev/en/style) }
#### xplr.config.general.panel_ui.default.title.format
The content for panel title by default.

@ -15,6 +15,7 @@ xplr messages categorized based on their purpose.
- [Select Operations](#select-operations)
- [Filter Operations](#filter-operations)
- [Sort Operations](#sort-operations)
- [Search Operations](#search-operations)
- [Mouse Operations](#mouse-operations)
- [Fifo Operations](#fifo-operations)
- [Logging](#logging)
@ -757,6 +758,8 @@ Example:
Add a [filter](https://xplr.dev/en/filtering#filter) to exclude nodes
while exploring directories.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Filters get automatically cleared when changing directories.
Type: { AddNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering#filter), input = "string" }
@ -768,6 +771,7 @@ Example:
#### RemoveNodeFilter
Remove an existing [filter](https://xplr.dev/en/filtering#filter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { RemoveNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering), input = "string" }
@ -780,6 +784,7 @@ Example:
Remove a [filter](https://xplr.dev/en/filtering#filter) if it exists,
else, add a it.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { ToggleNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering), input = "string" }
@ -792,6 +797,7 @@ Example:
Add a node [filter](https://xplr.dev/en/filtering#filter) reading the
input from the buffer.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { AddNodeFilterFromInput = [Filter](https://xplr.dev/en/filtering) }
@ -804,6 +810,7 @@ Example:
Remove a node [filter](https://xplr.dev/en/filtering#filter) reading
the input from the buffer.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { RemoveNodeFilterFromInput = [Filter](https://xplr.dev/en/filtering) }
@ -815,6 +822,7 @@ Example:
#### RemoveLastNodeFilter
Remove the last node [filter](https://xplr.dev/en/filtering).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -825,6 +833,7 @@ Example:
Reset the node [filters](https://xplr.dev/en/filtering) back to the
default configuration.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -834,6 +843,7 @@ Example:
#### ClearNodeFilters
Clear all the node [filters](https://xplr.dev/en/filtering).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -846,6 +856,7 @@ Example:
Add a [sorter](https://xplr.dev/en/sorting#sorter) to sort nodes while
exploring directories.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { AddNodeSorter = { sorter = [Sorter](https://xplr.dev/en/sorting#sorter), reverse = bool } }
@ -857,6 +868,7 @@ Example:
#### RemoveNodeSorter
Remove an existing [sorter](https://xplr.dev/en/sorting#sorter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { RemoveNodeSorter = [Sorter](https://xplr.dev/en/sorting#sorter) }
@ -868,6 +880,7 @@ Example:
#### ReverseNodeSorter
Reverse a node [sorter](https://xplr.dev/en/sorting#sorter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { ReverseNodeSorter = [Sorter](https://xplr.dev/en/sorting#sorter) }
@ -880,6 +893,7 @@ Example:
Remove a [sorter](https://xplr.dev/en/sorting#sorter) if it exists,
else, add a it.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Type: { ToggleNodeSorter = { sorter = [Sorter](https://xplr.dev/en/sorting#sorter), reverse = bool } }
@ -891,6 +905,7 @@ Example:
#### ReverseNodeSorters
Reverse the node [sorters](https://xplr.dev/en/sorting#sorter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -900,6 +915,7 @@ Example:
#### RemoveLastNodeSorter
Remove the last node [sorter](https://xplr.dev/en/sorting#sorter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -910,6 +926,7 @@ Example:
Reset the node [sorters](https://xplr.dev/en/sorting#sorter) back to
the default configuration.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
@ -919,12 +936,60 @@ Example:
#### ClearNodeSorters
Clear all the node [sorters](https://xplr.dev/en/sorting#sorter).
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
- Lua: `"ClearNodeSorters"`
- YAML: `ClearNodeSorters`
### Search Operations
#### SearchFuzzy
Search files using fuzzy match algorithm.
It keeps the filters, but overrides the sorters.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
It gets reset automatically when changing directory.
Type: { SearchFuzzy = "string" }
Example:
- Lua: `{ SearchFuzzy = "pattern" }`
- YAML: `SearchFuzzy: pattern`
#### SearchFuzzyFromInput
Calls `SearchFuzzy` with the input taken from the input buffer.
You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
Example:
- Lua: `"SearchFuzzyFromInput"`
- YAML: `SearchFuzzyFromInput`
#### AcceptSearch
Accepts the search by keeping the latest focus while in search mode.
Automatically calls `ExplorePwd`.
Example:
- Lua: `"AcceptSearch"`
- YAML: `AcceptSearch`
#### CancelSearch
Cancels the search by discarding the latest focus and recovering
the focus before search.
Automatically calls `ExplorePwd`.
Example:
- Lua: `"CancelSearch"`
- YAML: `CancelSearch`
### Mouse Operations
#### EnableMouse

@ -45,7 +45,7 @@ compatibility.
### Instructions
#### [v0.18.0][46] -> [v0.19.3][47]
#### [v0.18.0][46] -> [v0.19.4][47]
- BREAKING: The builtin modes cannot be accessed using space separated names
anymore. Use underscore separated mode names. For e.g.
@ -77,6 +77,20 @@ compatibility.
- Fixed applying regex based filters via the CLI and `$XPLR_PIPE_MSG_IN` pipe.
- You can use the `prompt` field to define input prompt for each mode, instead
of using the `SetInputPrompt` message.
- Since version v0.19.4, the native search will default to skim-v2 based fuzzy
matching. `esc` while in search mode will recover the initial focus. People
who prefer the regex based search, can use the `regex-search.xplr` plugin.
The following messages will be available for search based operations:
- SearchFuzzy
- SearchFuzzyFromInput
- AcceptSearch
- CancelSearch
- Since version v0.19.4, quick scrolling operations are supported using the
following messages and keys:
- ScrollUp -------- page-up
- ScrollDown ------ page-down
- ScrollUpHalf ---- {
- ScrollDownHalf -- }
<sub>Like this project so far? **[Please consider contributing][5]**.</sub>
@ -387,4 +401,4 @@ Else do the following:
[44]: https://github.com/sayanarijit/xplr/releases/tag/v0.16.4
[45]: https://github.com/sayanarijit/xplr/releases/tag/v0.17.6
[46]: https://github.com/sayanarijit/xplr/releases/tag/v0.18.0
[47]: https://github.com/sayanarijit/xplr/releases/tag/v0.19.3
[47]: https://github.com/sayanarijit/xplr/releases/tag/v0.19.4

@ -8,6 +8,7 @@ pub use crate::msg::in_::external::Command;
pub use crate::msg::in_::external::ExplorerConfig;
pub use crate::msg::in_::external::NodeFilter;
pub use crate::msg::in_::external::NodeFilterApplicable;
use crate::msg::in_::external::NodeSearcher;
pub use crate::msg::in_::external::NodeSorter;
pub use crate::msg::in_::external::NodeSorterApplicable;
pub use crate::msg::in_::ExternalMsg;
@ -478,6 +479,10 @@ impl App {
ReverseNodeSorters => self.reverse_node_sorters(),
ResetNodeSorters => self.reset_node_sorters(),
ClearNodeSorters => self.clear_node_sorters(),
SearchFuzzy(p) => self.search_fuzzy(p),
SearchFuzzyFromInput => self.search_fuzzy_from_input(),
AcceptSearch => self.accept_search(),
CancelSearch => self.cancel_search(),
EnableMouse => self.enable_mouse(),
DisableMouse => self.disable_mouse(),
ToggleMouse => self.toggle_mouse(),
@ -735,6 +740,7 @@ impl App {
let focus = self.focused_node().map(|n| n.relative_path.clone());
self = self.add_last_focus(pwd, focus)?;
self.pwd = dir.to_string_lossy().to_string();
self.explorer_config.searcher = None;
if save_history {
self.history = self.history.push(format!("{}/", self.pwd));
}
@ -1146,9 +1152,11 @@ impl App {
dir.parent.clone(),
dir.focused_node().map(|n| n.relative_path.clone()),
)?;
if dir.parent == self.pwd {
self.directory_buffer = Some(dir);
}
};
Ok(self)
}
@ -1374,6 +1382,58 @@ impl App {
Ok(self)
}
pub fn search_fuzzy(mut self, pattern: String) -> Result<Self> {
let rf = self
.explorer_config
.searcher
.as_ref()
.map(|s| s.recoverable_focus.clone())
.unwrap_or_else(|| self.focused_node().map(|n| n.absolute_path.clone()));
self.explorer_config.searcher = Some(NodeSearcher::new(pattern, rf));
Ok(self)
}
fn search_fuzzy_from_input(self) -> Result<Self> {
if let Some(pattern) = self.input.buffer.as_ref().map(Input::to_string) {
self.search_fuzzy(pattern)
} else {
Ok(self)
}
}
fn accept_search(mut self) -> Result<Self> {
let focus = self
.directory_buffer
.as_ref()
.and_then(|dir| dir.focused_node())
.map(|n| n.relative_path.clone());
self.explorer_config.searcher = None;
self = self.explore_pwd()?;
if let Some(f) = focus {
self = self.focus_path(&f, true)?;
}
Ok(self)
}
fn cancel_search(mut self) -> Result<Self> {
let focus = self
.explorer_config
.searcher
.as_ref()
.and_then(|s| s.recoverable_focus.clone());
self.explorer_config.searcher = None;
self = self.explore_pwd()?;
if let Some(f) = focus {
self = self.focus_path(&f, true)?;
}
Ok(self)
}
fn enable_mouse(mut self) -> Result<Self> {
self.msg_out.push_back(MsgOut::EnableMouse);
Ok(self)

@ -80,7 +80,7 @@ pub struct NodeTypesConfig {
pub special: HashMap<String, NodeTypeConfig>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UiConfig {
#[serde(default)]
@ -93,7 +93,7 @@ pub struct UiConfig {
pub style: Style,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct UiElement {
#[serde(default)]
@ -189,6 +189,9 @@ pub struct SortAndFilterUi {
#[serde(default)]
pub filter_identifiers: HashMap<NodeFilter, UiElement>,
#[serde(default)]
pub search_identifier: Option<UiElement>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
@ -356,7 +359,7 @@ impl KeyBindings {
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Mode {
#[serde(default)]
@ -540,7 +543,7 @@ impl ModesConfig {
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct PanelUiConfig {
#[serde(default)]

@ -2,11 +2,18 @@ use crate::app::{
DirectoryBuffer, ExplorerConfig, ExternalMsg, InternalMsg, MsgIn, Node, Task,
};
use anyhow::{Error, Result};
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use lazy_static::lazy_static;
use std::fs;
use std::path::PathBuf;
use std::sync::mpsc::Sender;
use std::thread;
lazy_static! {
static ref FUZZY_MATCHER: SkimMatcherV2 = SkimMatcherV2::default();
}
pub(crate) fn explore_sync(
config: ExplorerConfig,
parent: PathBuf,
@ -27,9 +34,26 @@ pub(crate) fn explore_sync(
.filter(|n| config.filter(n))
.collect::<Vec<Node>>();
nodes.sort_by(|a, b| config.sort(a, b));
nodes = if let Some(pattern) = config.searcher.as_ref().map(|s| &s.pattern) {
let mut nodes = nodes
.into_iter()
.filter_map(|n| {
FUZZY_MATCHER
.fuzzy_match(&n.relative_path, pattern)
.map(|score| (n, score))
})
.collect::<Vec<(_, _)>>();
nodes.sort_by(|(_, s1), (_, s2)| s2.cmp(s1));
nodes.into_iter().map(|(n, _)| n).collect::<Vec<_>>()
} else {
nodes.sort_by(|a, b| config.sort(a, b));
nodes
};
let focus_index = if let Some(focus) = focused_path {
let focus_index = if config.searcher.is_some() {
0
} else if let Some(focus) = focused_path {
let focus_str = focus.to_string_lossy().to_string();
nodes
.iter()

@ -450,6 +450,14 @@ xplr.config.general.sort_and_filter_ui.filter_identifiers = {
IAbsolutePathDoesNotMatchRegex = { format = "[i]abs!/", style = {} },
}
-- The identifiers used to denote applied search input.
--
-- Type: { format = nullable string, style = [Style](https://xplr.dev/en/style) }
xplr.config.general.sort_and_filter_ui.search_identifier = {
format = "search:",
style = {},
}
-- The content for panel title by default.
--
-- Type: nullable string
@ -666,7 +674,7 @@ xplr.config.general.start_fifo = nil
-- Type: [Key Bindings](https://xplr.dev/en/configure-key-bindings#key-bindings)
xplr.config.general.global_key_bindings = {
on_key = {
esc = {
["esc"] = {
messages = {
"PopMode",
},
@ -1065,8 +1073,7 @@ xplr.config.modes.builtin.default = {
messages = {
"PopMode",
{ SwitchModeBuiltin = "search" },
{ SetInputBuffer = "(?i)" },
"ExplorePwdAsync",
{ SetInputBuffer = "" },
},
},
["ctrl-i"] = {
@ -1106,13 +1113,13 @@ xplr.config.modes.builtin.default = {
{ SwitchModeBuiltin = "delete" },
},
},
down = {
["down"] = {
help = "down",
messages = {
"FocusNext",
},
},
enter = {
["enter"] = {
help = "quit with result",
messages = {
"PrintResultAndQuit",
@ -1132,7 +1139,7 @@ xplr.config.modes.builtin.default = {
{ SwitchModeBuiltin = "go_to" },
},
},
left = {
["left"] = {
help = "back",
messages = {
"Back",
@ -1170,7 +1177,7 @@ xplr.config.modes.builtin.default = {
},
},
},
right = {
["right"] = {
help = "enter",
messages = {
"Enter",
@ -1183,14 +1190,14 @@ xplr.config.modes.builtin.default = {
{ SwitchModeBuiltin = "sort" },
},
},
space = {
["space"] = {
help = "toggle selection",
messages = {
"ToggleSelection",
"FocusNext",
},
},
up = {
["up"] = {
help = "up",
messages = {
"FocusPrevious",
@ -1308,7 +1315,7 @@ xplr.config.modes.builtin.debug_error = {
},
key_bindings = {
on_key = {
enter = {
["enter"] = {
help = "open logs in editor",
messages = {
{
@ -1318,7 +1325,7 @@ xplr.config.modes.builtin.debug_error = {
},
},
},
q = {
["q"] = {
help = "quit",
messages = {
"Quit",
@ -1369,7 +1376,7 @@ xplr.config.modes.builtin.go_to_path = {
name = "go to path",
key_bindings = {
on_key = {
enter = {
["enter"] = {
help = "submit",
messages = {
{
@ -1384,7 +1391,7 @@ xplr.config.modes.builtin.go_to_path = {
"PopMode",
},
},
tab = {
["tab"] = {
help = "try complete",
messages = {
{ CallLuaSilently = "builtin.try_complete_path" },
@ -1445,6 +1452,13 @@ xplr.config.modes.builtin.selection_ops = {
"PopMode",
},
},
["u"] = {
help = "clear selection",
messages = {
"ClearSelection",
"PopMode",
},
},
["x"] = {
help = "open in gui",
messages = {
@ -1480,7 +1494,7 @@ xplr.config.modes.builtin.create = {
name = "create",
key_bindings = {
on_key = {
d = {
["d"] = {
help = "create directory",
messages = {
"PopMode",
@ -1488,7 +1502,7 @@ xplr.config.modes.builtin.create = {
{ SetInputBuffer = "" },
},
},
f = {
["f"] = {
help = "create file",
messages = {
"PopMode",
@ -1508,13 +1522,13 @@ xplr.config.modes.builtin.create_directory = {
prompt = "ð ",
key_bindings = {
on_key = {
tab = {
["tab"] = {
help = "try complete",
messages = {
{ CallLuaSilently = "builtin.try_complete_path" },
},
},
enter = {
["enter"] = {
help = "submit",
messages = {
{
@ -1550,13 +1564,13 @@ xplr.config.modes.builtin.create_file = {
prompt = "ƒ ",
key_bindings = {
on_key = {
tab = {
["tab"] = {
help = "try complete",
messages = {
{ CallLuaSilently = "builtin.try_complete_path" },
},
},
enter = {
["enter"] = {
help = "submit",
messages = {
{
@ -1593,21 +1607,21 @@ xplr.config.modes.builtin.number = {
prompt = ":",
key_bindings = {
on_key = {
down = {
["down"] = {
help = "to down",
messages = {
"FocusNextByRelativeIndexFromInput",
"PopMode",
},
},
enter = {
["enter"] = {
help = "to index",
messages = {
"FocusByIndexFromInput",
"PopMode",
},
},
up = {
["up"] = {
help = "to up",
messages = {
"FocusPreviousByRelativeIndexFromInput",
@ -1641,21 +1655,21 @@ xplr.config.modes.builtin.go_to = {
name = "go to",
key_bindings = {
on_key = {
f = {
["f"] = {
help = "follow symlink",
messages = {
"FollowSymlink",
"PopMode",
},
},
g = {
["g"] = {
help = "top",
messages = {
"FocusFirst",
"PopMode",
},
},
p = {
["p"] = {
help = "path",
messages = {
"PopMode",
@ -1663,7 +1677,7 @@ xplr.config.modes.builtin.go_to = {
{ SetInputBuffer = "" },
},
},
x = {
["x"] = {
help = "open in gui",
messages = {
{
@ -1696,13 +1710,13 @@ xplr.config.modes.builtin.rename = {
name = "rename",
key_bindings = {
on_key = {
tab = {
["tab"] = {
help = "try complete",
messages = {
{ CallLuaSilently = "builtin.try_complete_path" },
},
},
enter = {
["enter"] = {
help = "submit",
messages = {
{
@ -1738,13 +1752,13 @@ xplr.config.modes.builtin.duplicate_as = {
name = "duplicate as",
key_bindings = {
on_key = {
tab = {
["tab"] = {
help = "try complete",
messages = {
{ CallLuaSilently = "builtin.try_complete_path" },
},
},
enter = {
["enter"] = {
help = "submit",
messages = {
{
@ -1915,31 +1929,31 @@ xplr.config.modes.builtin.quit = {
name = "quit",
key_bindings = {
on_key = {
enter = {
["enter"] = {
help = "just quit",
messages = {
"Quit",
},
},
p = {
["p"] = {
help = "quit printing pwd",
messages = {
"PrintPwdAndQuit",
},
},
f = {
["f"] = {
help = "quit printing focus",
messages = {
"PrintFocusPathAndQuit",
},
},
s = {
["s"] = {
help = "quit printing selection",
messages = {
"PrintSelectionAndQuit",
},
},
r = {
["r"] = {
help = "quit printing result",
messages = {
"PrintResultAndQuit",
@ -1957,65 +1971,64 @@ xplr.config.modes.builtin.search = {
prompt = "/",
key_bindings = {
on_key = {
down = {
help = "down",
["up"] = {
help = "up",
messages = {
"FocusNext",
"FocusPrevious",
},
},
enter = {
help = "submit",
["down"] = {
help = "down",
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"PopMode",
"ExplorePwdAsync",
"FocusNext",
},
},
right = {
["right"] = {
help = "enter",
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"Enter",
{ SetInputBuffer = "(?i)" },
"ExplorePwdAsync",
{ SetInputBuffer = "" },
},
},
left = {
["left"] = {
help = "back",
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"Back",
{ SetInputBuffer = "(?i)" },
"ExplorePwdAsync",
{ SetInputBuffer = "" },
},
},
tab = {
["tab"] = {
help = "toggle selection",
messages = {
"ToggleSelection",
"FocusNext",
},
},
up = {
help = "up",
["enter"] = {
help = "submit",
messages = {
"FocusPrevious",
"AcceptSearch",
"PopMode",
},
},
["esc"] = {
help = "cancel",
messages = {
"CancelSearch",
"PopMode",
},
},
},
default = {
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"UpdateInputBufferFromKey",
{ AddNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"SearchFuzzyFromInput",
"ExplorePwdAsync",
},
},
},
}
xplr.config.modes.builtin.search.key_bindings.on_key["esc"] =
xplr.config.modes.builtin.search.key_bindings.on_key.enter
xplr.config.modes.builtin.search.key_bindings.on_key["ctrl-n"] =
xplr.config.modes.builtin.search.key_bindings.on_key.down
xplr.config.modes.builtin.search.key_bindings.on_key["ctrl-p"] =
@ -2046,7 +2059,7 @@ xplr.config.modes.builtin.filter = {
"ExplorePwdAsync",
},
},
backspace = {
["backspace"] = {
help = "remove last filter",
messages = {
"RemoveLastNodeFilter",
@ -2079,13 +2092,13 @@ xplr.config.modes.builtin.relative_path_does_match_regex = {
prompt = xplr.config.general.sort_and_filter_ui.filter_identifiers.RelativePathDoesMatchRegex.format,
key_bindings = {
on_key = {
enter = {
["enter"] = {
help = "submit",
messages = {
"PopMode",
},
},
esc = {
["esc"] = {
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesMatchRegex" },
"PopMode",
@ -2112,13 +2125,13 @@ xplr.config.modes.builtin.relative_path_does_not_match_regex = {
prompt = xplr.config.general.sort_and_filter_ui.filter_identifiers.RelativePathDoesNotMatchRegex.format,
key_bindings = {
on_key = {
enter = {
["enter"] = {
help = "submit",
messages = {
"PopMode",
},
},
esc = {
["esc"] = {
messages = {
{ RemoveNodeFilterFromInput = "RelativePathDoesNotMatchRegex" },
"PopMode",
@ -2223,7 +2236,7 @@ xplr.config.modes.builtin.sort = {
"ExplorePwdAsync",
},
},
backspace = {
["backspace"] = {
help = "remove last sorter",
messages = {
"RemoveLastNodeSorter",
@ -2256,7 +2269,7 @@ xplr.config.modes.builtin.sort = {
"ExplorePwdAsync",
},
},
enter = {
["enter"] = {
help = "submit",
messages = {
"PopMode",

@ -146,24 +146,24 @@ mod tests {
assert!(check_version(VERSION, "foo path").is_ok());
// Current release if OK
assert!(check_version("0.19.3", "foo path").is_ok());
assert!(check_version("0.19.4", "foo path").is_ok());
// Prev major release is ERR
// - Not yet
// Prev minor release is ERR (Change when we get to v1)
assert!(check_version("0.18.3", "foo path").is_err());
assert!(check_version("0.18.4", "foo path").is_err());
// Prev bugfix release is OK
assert!(check_version("0.19.2", "foo path").is_ok());
assert!(check_version("0.19.3", "foo path").is_ok());
// Next major release is ERR
assert!(check_version("1.19.3", "foo path").is_err());
assert!(check_version("1.19.4", "foo path").is_err());
// Next minor release is ERR
assert!(check_version("0.20.3", "foo path").is_err());
assert!(check_version("0.20.4", "foo path").is_err());
// Next bugfix release is ERR (Change when we get to v1)
assert!(check_version("0.19.4", "foo path").is_err());
assert!(check_version("0.19.5", "foo path").is_err());
}
}

@ -675,6 +675,8 @@ pub enum ExternalMsg {
/// Add a [filter](https://xplr.dev/en/filtering#filter) to exclude nodes
/// while exploring directories.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
/// Filters get automatically cleared when changing directories.
///
/// Type: { AddNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering#filter), input = "string" }
///
@ -685,6 +687,7 @@ pub enum ExternalMsg {
AddNodeFilter(NodeFilterApplicable),
/// Remove an existing [filter](https://xplr.dev/en/filtering#filter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { RemoveNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering), input = "string" }
///
@ -696,6 +699,7 @@ pub enum ExternalMsg {
/// Remove a [filter](https://xplr.dev/en/filtering#filter) if it exists,
/// else, add a it.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { ToggleNodeFilter = { filter = [Filter](https://xplr.dev/en/filtering), input = "string" }
///
@ -707,6 +711,7 @@ pub enum ExternalMsg {
/// Add a node [filter](https://xplr.dev/en/filtering#filter) reading the
/// input from the buffer.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { AddNodeFilterFromInput = [Filter](https://xplr.dev/en/filtering) }
///
@ -718,6 +723,7 @@ pub enum ExternalMsg {
/// Remove a node [filter](https://xplr.dev/en/filtering#filter) reading
/// the input from the buffer.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { RemoveNodeFilterFromInput = [Filter](https://xplr.dev/en/filtering) }
///
@ -728,6 +734,7 @@ pub enum ExternalMsg {
RemoveNodeFilterFromInput(NodeFilter),
/// Remove the last node [filter](https://xplr.dev/en/filtering).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -737,6 +744,7 @@ pub enum ExternalMsg {
/// Reset the node [filters](https://xplr.dev/en/filtering) back to the
/// default configuration.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -745,6 +753,7 @@ pub enum ExternalMsg {
ResetNodeFilters,
/// Clear all the node [filters](https://xplr.dev/en/filtering).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -756,6 +765,7 @@ pub enum ExternalMsg {
/// Add a [sorter](https://xplr.dev/en/sorting#sorter) to sort nodes while
/// exploring directories.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { AddNodeSorter = { sorter = [Sorter](https://xplr.dev/en/sorting#sorter), reverse = bool } }
///
@ -766,6 +776,7 @@ pub enum ExternalMsg {
AddNodeSorter(NodeSorterApplicable),
/// Remove an existing [sorter](https://xplr.dev/en/sorting#sorter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { RemoveNodeSorter = [Sorter](https://xplr.dev/en/sorting#sorter) }
///
@ -776,6 +787,7 @@ pub enum ExternalMsg {
RemoveNodeSorter(NodeSorter),
/// Reverse a node [sorter](https://xplr.dev/en/sorting#sorter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { ReverseNodeSorter = [Sorter](https://xplr.dev/en/sorting#sorter) }
///
@ -787,6 +799,7 @@ pub enum ExternalMsg {
/// Remove a [sorter](https://xplr.dev/en/sorting#sorter) if it exists,
/// else, add a it.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Type: { ToggleNodeSorter = { sorter = [Sorter](https://xplr.dev/en/sorting#sorter), reverse = bool } }
///
@ -797,6 +810,7 @@ pub enum ExternalMsg {
ToggleNodeSorter(NodeSorterApplicable),
/// Reverse the node [sorters](https://xplr.dev/en/sorting#sorter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -805,6 +819,7 @@ pub enum ExternalMsg {
ReverseNodeSorters,
/// Remove the last node [sorter](https://xplr.dev/en/sorting#sorter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -814,6 +829,7 @@ pub enum ExternalMsg {
/// Reset the node [sorters](https://xplr.dev/en/sorting#sorter) back to
/// the default configuration.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -822,6 +838,7 @@ pub enum ExternalMsg {
ResetNodeSorters,
/// Clear all the node [sorters](https://xplr.dev/en/sorting#sorter).
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
@ -829,6 +846,49 @@ pub enum ExternalMsg {
/// - YAML: `ClearNodeSorters`
ClearNodeSorters,
/// ### Search Operations --------------------------------------------------
/// Search files using fuzzy match algorithm.
/// It keeps the filters, but overrides the sorters.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
/// It gets reset automatically when changing directory.
///
/// Type: { SearchFuzzy = "string" }
///
/// Example:
///
/// - Lua: `{ SearchFuzzy = "pattern" }`
/// - YAML: `SearchFuzzy: pattern`
SearchFuzzy(String),
/// Calls `SearchFuzzy` with the input taken from the input buffer.
/// You need to call `ExplorePwd` or `ExplorePwdAsync` explicitely.
///
/// Example:
///
/// - Lua: `"SearchFuzzyFromInput"`
/// - YAML: `SearchFuzzyFromInput`
SearchFuzzyFromInput,
/// Accepts the search by keeping the latest focus while in search mode.
/// Automatically calls `ExplorePwd`.
///
/// Example:
///
/// - Lua: `"AcceptSearch"`
/// - YAML: `AcceptSearch`
AcceptSearch,
/// Cancels the search by discarding the latest focus and recovering
/// the focus before search.
/// Automatically calls `ExplorePwd`.
///
/// Example:
///
/// - Lua: `"CancelSearch"`
/// - YAML: `CancelSearch`
CancelSearch,
/// ### Mouse Operations ---------------------------------------------------
/// Enable mouse
@ -1513,10 +1573,26 @@ impl NodeFilterApplicable {
}
}
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct NodeSearcher {
pub pattern: String,
pub recoverable_focus: Option<String>,
}
impl NodeSearcher {
pub fn new(pattern: String, recoverable_focus: Option<String>) -> Self {
Self {
pattern,
recoverable_focus,
}
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ExplorerConfig {
pub filters: IndexSet<NodeFilterApplicable>,
pub sorters: IndexSet<NodeSorterApplicable>,
pub searcher: Option<NodeSearcher>,
}
impl ExplorerConfig {

@ -1,5 +1,5 @@
use crate::permissions::Permissions;
use humansize::{file_size_opts as options, FileSize};
use humansize::{format_size, DECIMAL};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::os::unix::prelude::MetadataExt;
@ -7,8 +7,7 @@ use std::path::{Path, PathBuf};
use std::time::UNIX_EPOCH;
fn to_human_size(size: u64) -> String {
size.file_size(options::CONVENTIONAL)
.unwrap_or_else(|_| format!("{} B", size))
format_size(size, DECIMAL)
}
fn mime_essence(path: &Path, is_dir: bool) -> String {

@ -104,7 +104,7 @@ pub enum ContentBody {
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub enum Layout {
Nothing,
@ -251,7 +251,7 @@ impl Modifier {
}
}
#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Style {
pub fg: Option<Color>,
@ -861,6 +861,12 @@ fn draw_sort_n_filter<B: Backend>(
let ui = app.config.general.sort_and_filter_ui.to_owned();
let filter_by: &IndexSet<NodeFilterApplicable> = &app.explorer_config.filters;
let sort_by: &IndexSet<NodeSorterApplicable> = &app.explorer_config.sorters;
let search = app
.explorer_config
.searcher
.as_ref()
.map(|s| s.pattern.clone());
let defaultui = &ui.default_identifier;
let forwardui = defaultui
.to_owned()
@ -886,25 +892,45 @@ fn draw_sort_n_filter<B: Backend>(
})
.unwrap_or((Span::raw("f"), Span::raw("")))
})
.chain(sort_by.iter().map(|s| {
let direction = if s.reverse { &reverseui } else { &forwardui };
ui.sorter_identifiers
.get(&s.sorter)
.chain(
sort_by
.iter()
.map(|s| {
let direction = if s.reverse { &reverseui } else { &forwardui };
ui.sorter_identifiers
.get(&s.sorter)
.map(|u| {
let ui = defaultui.to_owned().extend(u);
(
Span::styled(
ui.format.to_owned().unwrap_or_default(),
ui.style.into(),
),
Span::styled(
direction.format.to_owned().unwrap_or_default(),
direction.style.to_owned().into(),
),
)
})
.unwrap_or((Span::raw("s"), Span::raw("")))
})
.take(if search.is_some() { 0 } else { sort_by.len() }),
)
.chain(search.iter().map(|s| {
ui.search_identifier
.as_ref()
.map(|u| {
let ui = defaultui.to_owned().extend(u);
(
Span::styled(
ui.format.to_owned().unwrap_or_default(),
ui.style.into(),
),
Span::styled(
direction.format.to_owned().unwrap_or_default(),
direction.style.to_owned().into(),
ui.style.to_owned().into(),
),
Span::styled(s, ui.style.into()),
)
})
.unwrap_or((Span::raw("s"), Span::raw("")))
.unwrap_or((Span::raw("/"), Span::raw(s)))
}))
.zip(std::iter::repeat(Span::styled(
ui.separator.format.to_owned().unwrap_or_default(),
@ -912,6 +938,7 @@ fn draw_sort_n_filter<B: Backend>(
)))
.flat_map(|((a, b), c)| vec![a, b, c])
.collect::<Vec<Span>>();
spans.pop();
let p = Paragraph::new(Spans::from(spans)).block(block(

Loading…
Cancel
Save