diff --git a/Cargo.lock b/Cargo.lock
index 46b316b4..a3f5f0e8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aho-corasick"
-version = "0.7.10"
+version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
@@ -21,16 +21,13 @@ dependencies = [
]
[[package]]
-name = "arrayref"
-version = "0.3.6"
+name = "atomic"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
-
-[[package]]
-name = "arrayvec"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281"
+dependencies = [
+ "autocfg",
+]
[[package]]
name = "atty"
@@ -45,15 +42,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
-
-[[package]]
-name = "base64"
-version = "0.11.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
@@ -61,22 +52,11 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
-[[package]]
-name = "blake2b_simd"
-version = "0.5.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
-dependencies = [
- "arrayref",
- "arrayvec",
- "constant_time_eq",
-]
-
[[package]]
name = "bstr"
-version = "0.2.12"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41"
+checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
dependencies = [
"lazy_static",
"memchr",
@@ -84,17 +64,11 @@ dependencies = [
"serde",
]
-[[package]]
-name = "byteorder"
-version = "1.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
-
[[package]]
name = "cc"
-version = "1.0.52"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
+checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
[[package]]
name = "cfg-if"
@@ -102,6 +76,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
[[package]]
name = "chrono"
version = "0.4.19"
@@ -117,9 +97,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "2.33.0"
+version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
@@ -131,27 +111,35 @@ dependencies = [
]
[[package]]
-name = "constant_time_eq"
-version = "0.1.5"
+name = "crossterm"
+version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "lazy_static",
+ "libc",
+ "mio",
+ "parking_lot",
+ "signal-hook",
+ "winapi",
+]
[[package]]
-name = "crossbeam-utils"
-version = "0.7.2"
+name = "crossterm_winapi"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db"
dependencies = [
- "autocfg",
- "cfg-if",
- "lazy_static",
+ "winapi",
]
[[package]]
name = "csv"
-version = "1.1.3"
+version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
+checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
@@ -175,17 +163,37 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
"dirs-sys",
]
+[[package]]
+name = "dirs-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+dependencies = [
+ "cfg-if 1.0.0",
+ "dirs-sys-next",
+]
+
[[package]]
name = "dirs-sys"
-version = "0.3.4"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
- "cfg-if",
"libc",
"redox_users",
"winapi",
@@ -197,22 +205,46 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+[[package]]
+name = "figment"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca029e813a72b7526d28273d25f3e4a2f365d1b7a1018a6f93ec9053a119763"
+dependencies = [
+ "atomic",
+ "serde",
+ "toml",
+ "uncased",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
[[package]]
name = "getrandom"
-version = "0.1.14"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
"libc",
- "wasi",
+ "wasi 0.10.0+wasi-snapshot-preview1",
]
[[package]]
name = "hermit-abi"
-version = "0.1.12"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
@@ -223,6 +255,15 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+[[package]]
+name = "instant"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
[[package]]
name = "itertools"
version = "0.9.0"
@@ -234,9 +275,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "0.4.5"
+version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "lazy_static"
@@ -246,9 +287,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.69"
+version = "0.2.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
+checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
[[package]]
name = "libsqlite3-sys"
@@ -263,9 +304,27 @@ dependencies = [
[[package]]
name = "linked-hash-map"
-version = "0.5.3"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
+name = "lock_api"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if 1.0.0",
+]
[[package]]
name = "lru-cache"
@@ -282,8 +341,10 @@ version = "0.5.9"
dependencies = [
"chrono",
"clap",
+ "crossterm",
"csv",
"dirs",
+ "figment",
"humantime",
"itertools",
"libc",
@@ -291,16 +352,47 @@ dependencies = [
"regex",
"relative-path",
"rusqlite",
+ "serde",
"shellexpand",
- "termion",
"unicode-segmentation",
]
[[package]]
name = "memchr"
-version = "2.3.3"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
+
+[[package]]
+name = "mio"
+version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
+dependencies = [
+ "libc",
+ "log",
+ "miow",
+ "ntapi",
+ "winapi",
+]
+
+[[package]]
+name = "miow"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
+dependencies = [
+ "winapi",
+]
[[package]]
name = "num-integer"
@@ -322,22 +414,59 @@ dependencies = [
]
[[package]]
-name = "numtoa"
-version = "0.1.0"
+name = "parking_lot"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
+checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+dependencies = [
+ "cfg-if 1.0.0",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
[[package]]
name = "pkg-config"
-version = "0.3.17"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "ppv-lite86"
-version = "0.2.6"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
+checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+dependencies = [
+ "proc-macro2",
+]
[[package]]
name = "rand"
@@ -345,7 +474,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
- "getrandom",
+ "getrandom 0.1.16",
"libc",
"rand_chacha",
"rand_core",
@@ -368,7 +497,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
- "getrandom",
+ "getrandom 0.1.16",
]
[[package]]
@@ -382,62 +511,51 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.1.56"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
-
-[[package]]
-name = "redox_termios"
-version = "0.1.1"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
+checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
dependencies = [
- "redox_syscall",
+ "bitflags",
]
[[package]]
name = "redox_users"
-version = "0.3.4"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
- "getrandom",
+ "getrandom 0.2.3",
"redox_syscall",
- "rust-argon2",
]
[[package]]
name = "regex"
-version = "1.3.7"
+version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
- "thread_local",
]
[[package]]
name = "regex-automata"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
-dependencies = [
- "byteorder",
-]
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
-version = "0.6.17"
+version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "relative-path"
-version = "1.0.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bedde000f40f2921ce439ea165c9c53fd629bfa115140c72e22aceacb4a21954"
+checksum = "a479d53d7eed831f3c92ca79c61002d5987e21417d528296832f802bca532380"
[[package]]
name = "rusqlite"
@@ -452,38 +570,72 @@ dependencies = [
]
[[package]]
-name = "rust-argon2"
-version = "0.7.0"
+name = "ryu"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
-dependencies = [
- "base64",
- "blake2b_simd",
- "constant_time_eq",
- "crossbeam-utils",
-]
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
-name = "ryu"
-version = "1.0.4"
+name = "scopeguard"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
-version = "1.0.106"
+version = "1.0.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
+checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "shellexpand"
-version = "2.0.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2b22262a9aaf9464d356f656fea420634f78c881c5eebd5ef5e66d8b9bc603"
+checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829"
dependencies = [
- "dirs",
+ "dirs-next",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
]
+[[package]]
+name = "smallvec"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+
[[package]]
name = "strsim"
version = "0.8.0"
@@ -491,15 +643,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
-name = "termion"
-version = "1.5.5"
+name = "syn"
+version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
+checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
dependencies = [
- "libc",
- "numtoa",
- "redox_syscall",
- "redox_termios",
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
]
[[package]]
@@ -512,41 +663,57 @@ dependencies = [
]
[[package]]
-name = "thread_local"
-version = "1.0.1"
+name = "time"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
- "lazy_static",
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
]
[[package]]
-name = "time"
-version = "0.1.43"
+name = "toml"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
- "libc",
- "winapi",
+ "serde",
+]
+
+[[package]]
+name = "uncased"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
+dependencies = [
+ "version_check",
]
[[package]]
name = "unicode-segmentation"
-version = "1.6.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "unicode-width"
-version = "0.1.7"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vcpkg"
-version = "0.2.8"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
@@ -554,17 +721,29 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+[[package]]
+name = "version_check"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
[[package]]
name = "winapi"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
diff --git a/Cargo.toml b/Cargo.toml
index eb9252e4..2479e024 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,15 +23,17 @@ debug = true
chrono = "0.4.19"
clap = "2.33"
csv = "1"
+crossterm = "0.18"
dirs = "2.0"
+figment = { version = "0.10", features = ["toml"] }
humantime = "2.1.0"
itertools = "0.9.0"
libc = "0.2"
rand = "0.7"
regex = "1"
relative-path = "1.0"
+serde = { version = "1.0", features = ["derive"] }
shellexpand = "2.0"
-termion = "1.5.5"
unicode-segmentation = "1.6"
[dependencies.rusqlite]
diff --git a/README.md b/README.md
index dc8b0189..41d8905b 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
McFly replaces your default `ctrl-r` shell history search with an intelligent search engine that takes into account
your working directory and the context of recently executed commands. McFly's suggestions are prioritized
in real time with a small neural network.
-
+
TL;DR: an upgraded `ctrl-r` where history results make sense for what you're working on right now.
## Features
@@ -193,10 +193,13 @@ To avoid McFly's UI messing up your scrollback history in iTerm2, make sure this
## Settings
-A number of settings can be set via environment variables. To set a setting you should add the following snippets to your `~/.bashrc` / `~/.zshrc` / `~/.config/fish/config.fish`.
+
+Settings, in particular the interface colors, may be configured in [`~/.mcfly/mcfly.toml`](https://github.com/cantino/mcfly/blob/crossterm-and-colors/mcfly.example.toml).
+
+A number of settings can also be set via environment variables. To set a setting you should add the following snippets to your `~/.bashrc` / `~/.zshrc` / `~/.config/fish/config.fish`.
### Light Mode
-To swap the color scheme for use in a light terminal, set the environment variable `MCFLY_LIGHT`.
+To swap the default color scheme for use in a light terminal, set the environment variable `MCFLY_LIGHT`.
bash / zsh:
```bash
diff --git a/mcfly.example.toml b/mcfly.example.toml
new file mode 100644
index 00000000..083bc54f
--- /dev/null
+++ b/mcfly.example.toml
@@ -0,0 +1,31 @@
+# Mcfly sample configuration, to be placed in ~/.mcfly/mcfly.toml
+
+fuzzy = true
+results = 25
+key_scheme = "Vim" # Vim or Emacs (case-sensitive)
+
+[colors]
+# Colors correspond to the ANSI colors defined for your terminal, and are
+# named in crossterm style (case-insensitive):
+# black dark_grey
+# dark_red red
+# dark_green green
+# dark_yellow yellow
+# dark_blue blue
+# dark_magenta magenta
+# dark_cyan cyan
+# grey white
+
+menu_bg = "dark_red"
+menu_fg = "white"
+menu_deleting_bg = "cyan"
+menu_deleting_fg = "dark_red"
+
+bg = "black"
+fg = "white"
+prompt_fg = "white"
+highlight = "red"
+timing = "magenta"
+cursor_bg = "dark_grey"
+cursor_fg = "white"
+cursor_highlight = "red"
diff --git a/src/interface.rs b/src/interface.rs
index d11a3c3c..788ec4e0 100644
--- a/src/interface.rs
+++ b/src/interface.rs
@@ -1,3 +1,4 @@
+
use crate::command_input::{CommandInput, Move};
use crate::history::History;
@@ -6,15 +7,16 @@ use crate::history::Command;
use crate::history_cleaner;
use crate::settings::Settings;
use crate::settings::{InterfaceView, KeyScheme};
+use crossterm::cursor;
+use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
+use crossterm::queue;
+use crossterm::style::{Color, Print, ResetColor, SetBackgroundColor, SetForegroundColor};
+use crossterm::terminal;
+use crossterm::terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen};
+use std::io::{stdout, Write};
+use std::str::FromStr;
use chrono::{Duration, TimeZone, Utc};
use humantime::format_duration;
-use std::io::{stdin, stdout, Write};
-use termion::color;
-use termion::event::Key;
-use termion::input::TermRead;
-use termion::raw::IntoRawMode;
-use termion::screen::AlternateScreen;
-use termion::{clear, cursor, terminal_size};
pub struct Interface<'a> {
history: &'a History,
@@ -66,17 +68,28 @@ impl MenuMode {
}
}
- fn bg(&self) -> String {
+ fn bg(&self, interface: &Interface) -> Color {
+ match *self {
+ MenuMode::Normal => Color::from_str(&interface.settings.colors.menu_bg).unwrap(),
+ MenuMode::ConfirmDelete => {
+ Color::from_str(&interface.settings.colors.menu_deleting_bg).unwrap()
+ }
+ }
+ }
+
+ fn fg(&self, interface: &Interface) -> Color {
match *self {
- MenuMode::Normal => color::Bg(color::LightBlue).to_string(),
- MenuMode::ConfirmDelete => color::Bg(color::Red).to_string(),
+ MenuMode::Normal => Color::from_str(&interface.settings.colors.menu_fg).unwrap(),
+ MenuMode::ConfirmDelete => {
+ Color::from_str(&interface.settings.colors.menu_deleting_fg).unwrap()
+ }
}
}
}
-const PROMPT_LINE_INDEX: u16 = 3;
-const INFO_LINE_INDEX: u16 = 1;
-const RESULTS_TOP_INDEX: u16 = 5;
+const PROMPT_LINE_INDEX: u16 = 2;
+const INFO_LINE_INDEX: u16 = 0;
+const RESULTS_TOP_INDEX: u16 = 4;
impl<'a> Interface<'a> {
pub fn new(settings: &'a Settings, history: &'a History) -> Interface<'a> {
@@ -133,136 +146,89 @@ impl<'a> Interface<'a> {
}
fn menubar(&self, screen: &mut W) {
- let (width, _height): (u16, u16) = terminal_size().unwrap();
- write!(
+ let (width, _height): (u16, u16) = terminal::size().unwrap();
+
+ let _ = queue!(screen, cursor::MoveTo(0, self.info_line_index()));
+ let _ = queue!(screen, SetBackgroundColor(self.menu_mode.bg(self)));
+ let _ = queue!(screen, SetForegroundColor(self.menu_mode.fg(self)));
+ let _ = queue!(
screen,
- "{hide}{cursor}{clear}{fg}{bg}{text:width$}{reset_bg}",
- hide = cursor::Hide,
- fg = color::Fg(color::LightWhite).to_string(),
- bg = self.menu_mode.bg(),
- cursor = cursor::Goto(1, self.info_line_index()),
- clear = clear::CurrentLine,
- text = self.menu_mode.text(self),
- reset_bg = color::Bg(color::Reset).to_string(),
- width = width as usize
- )
- .unwrap();
+ Print(format!(
+ "{text:width$}",
+ text = self.menu_mode.text(self),
+ width = width as usize
+ ))
+ );
+ let _ = queue!(screen, ResetColor);
+
screen.flush().unwrap();
}
fn prompt(&self, screen: &mut W) {
- let prompt_line_index = self.prompt_line_index();
- write!(
+ let _ = queue!(
screen,
- "{}{}{}$ {}",
- if self.settings.lightmode {
- color::Fg(color::Black).to_string()
- } else {
- color::Fg(color::LightWhite).to_string()
- },
- cursor::Goto(1, self.prompt_line_index()),
- clear::CurrentLine,
- self.input
- )
- .unwrap();
- write!(
+ SetForegroundColor(Color::from_str(&self.settings.colors.prompt_fg).unwrap())
+ );
+ let _ = queue!(screen, cursor::MoveTo(0, self.prompt_line_index()));
+ let _ = queue!(screen, Clear(ClearType::CurrentLine));
+ let _ = queue!(screen, Print(format!("$ {}", self.input)));
+ let _ = queue!(
screen,
- "{}{}",
- cursor::Goto(self.input.cursor as u16 + 3, prompt_line_index),
- cursor::Show
- )
- .unwrap();
- screen.flush().unwrap();
- }
+ cursor::MoveTo(self.input.cursor as u16 + 2, self.prompt_line_index())
+ );
+
+ if self.in_vim_insert_mode {
+ let _ = queue!(screen, cursor::EnableBlinking);
+ } else {
+ let _ = queue!(screen, cursor::DisableBlinking);
+ }
+
+ let _ = queue!(screen, cursor::Show);
- fn debug_cursor(&self, screen: &mut W) {
- let result_top_index = self.result_top_index();
- write!(
- screen,
- "{}{}",
- cursor::Hide,
- cursor::Goto(0, result_top_index + self.settings.results + 1)
- )
- .unwrap();
screen.flush().unwrap();
}
fn results(&mut self, screen: &mut W) {
- let result_top_index = self.result_top_index();
- write!(
- screen,
- "{}{}{}",
- cursor::Hide,
- cursor::Goto(1, result_top_index),
- clear::All
- )
- .unwrap();
- let (width, _height): (u16, u16) = terminal_size().unwrap();
+ let _ = queue!(screen, cursor::Hide);
+ let _ = queue!(screen, cursor::MoveTo(0, self.result_top_index()));
+ let _ = queue!(screen, Clear(ClearType::All));
+
+ let (width, _height): (u16, u16) = terminal::size().unwrap();
if !self.matches.is_empty() && self.selection > self.matches.len() - 1 {
self.selection = self.matches.len() - 1;
}
for (index, command) in self.matches.iter().enumerate() {
- let mut fg = if self.settings.lightmode {
- color::Fg(color::Black).to_string()
- } else {
- color::Fg(color::LightWhite).to_string()
- };
-
- let mut highlight = if self.settings.lightmode {
- color::Fg(color::Blue).to_string()
- } else {
- color::Fg(color::Green).to_string()
- };
-
- let mut bg = color::Bg(color::Reset).to_string();
+ let mut fg = Color::from_str(&self.settings.colors.fg).unwrap();
+ let mut bg = Color::Reset;
+ let mut highlight = Color::from_str(&self.settings.colors.highlight).unwrap();
+ let mut timing_color = Color::from_str(&self.settings.colors.timing).unwrap();
if index == self.selection {
- if self.settings.lightmode {
- fg = color::Fg(color::LightWhite).to_string();
- bg = color::Bg(color::LightBlack).to_string();
- highlight = color::Fg(color::White).to_string();
- } else {
- fg = color::Fg(color::Black).to_string();
- bg = color::Bg(color::LightWhite).to_string();
- highlight = color::Fg(color::Green).to_string();
- }
+ fg = Color::from_str(&self.settings.colors.cursor_fg).unwrap();
+ bg = Color::from_str(&self.settings.colors.cursor_bg).unwrap();
+ highlight = Color::from_str(&self.settings.colors.cursor_highlight).unwrap();
+ timing_color = Color::from_str(&self.settings.colors.timing).unwrap();
}
- write!(screen, "{}{}", fg, bg).unwrap();
-
let command_line_index = self.command_line_index(index as i16);
-
- write!(
+ let _ = queue!(screen, cursor::MoveTo(0, (command_line_index as i16 + self.result_top_index() as i16) as u16));
+ let _ = queue!(screen, SetBackgroundColor(bg));
+ let _ = queue!(screen, SetForegroundColor(fg));
+ let _ = queue!(
screen,
- "{}{}",
- cursor::Goto(
- 1,
- (command_line_index as i16 + result_top_index as i16) as u16
- ),
- Interface::truncate_for_display(
+ Print(&format!("{: Interface<'a> {
.collect::>()
.join(" ");
- let highlight = if self.settings.lightmode {
- color::Fg(color::Blue).to_string()
- } else {
- color::Fg(color::LightBlue).to_string()
- };
-
- write!(screen, "{}", highlight).unwrap();
- write!(screen, "{:>9}", duration).unwrap();
+ let _ = queue!(screen, cursor::MoveTo(width - 9, (command_line_index as i16 + self.result_top_index() as i16) as u16));
+ let _ = queue!(screen, SetForegroundColor(timing_color));
+ let _ = queue!(screen, Print(&format!("{:>9}", duration)));
+ let _ = queue!(screen, SetForegroundColor(fg));
}
-
- write!(screen, "{}", color::Bg(color::Reset)).unwrap();
- write!(screen, "{}", color::Fg(color::Reset)).unwrap();
}
screen.flush().unwrap();
}
#[allow(unused)]
fn debug>(&self, screen: &mut W, s: S) {
- write!(
- screen,
- "{}{}{}",
- cursor::Goto(1, 2),
- clear::CurrentLine,
- s.into()
- )
- .unwrap();
+ let _ = queue!(screen, cursor::MoveTo(0, 0));
+ let _ = queue!(screen, Clear(ClearType::CurrentLine));
+ let _ = queue!(screen, Print(s.into()));
+
screen.flush().unwrap();
}
@@ -384,42 +339,60 @@ impl<'a> Interface<'a> {
}
fn select(&mut self) {
- let stdin = stdin();
- let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap());
- // let mut screen = stdout().into_raw_mode().unwrap();
- write!(screen, "{}", clear::All).unwrap();
+ let _ = terminal::enable_raw_mode();
+
+ let mut screen = stdout();
+
+ let _ = queue!(screen, EnterAlternateScreen);
+ let _ = queue!(screen, Clear(ClearType::All));
self.refresh_matches();
self.results(&mut screen);
self.menubar(&mut screen);
self.prompt(&mut screen);
- for c in stdin.keys() {
- self.debug_cursor(&mut screen);
+ loop {
+ let event =
+ read().unwrap_or_else(|e| panic!("McFly error: failed to read input {:?}", &e));
if self.menu_mode != MenuMode::Normal {
- match c.unwrap() {
- Key::Ctrl('c')
- | Key::Ctrl('d')
- | Key::Ctrl('g')
- | Key::Ctrl('z')
- | Key::Ctrl('r') => {
+ match event {
+ Event::Key(KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code:
+ KeyCode::Char('c')
+ | KeyCode::Char('d')
+ | KeyCode::Char('g')
+ | KeyCode::Char('z')
+ | KeyCode::Char('r'),
+ }) => {
self.run = false;
self.input.clear();
break;
}
- Key::Char('y') | Key::Char('Y') => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('y'),
+ ..
+ }) => {
self.confirm(true);
}
- Key::Char('n') | Key::Char('N') | Key::Esc => {
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Char('n'),
+ ..
+ }
+ | KeyEvent {
+ code: KeyCode::Esc, ..
+ },
+ ) => {
self.confirm(false);
}
_ => {}
- }
+ };
} else {
let early_out = match self.settings.key_scheme {
- KeyScheme::Emacs => self.select_with_emacs_key_scheme(c.unwrap()),
- KeyScheme::Vim => self.select_with_vim_key_scheme(c.unwrap()),
+ KeyScheme::Emacs => self.select_with_emacs_key_scheme(event),
+ KeyScheme::Vim => self.select_with_vim_key_scheme(event),
};
if early_out {
@@ -432,70 +405,144 @@ impl<'a> Interface<'a> {
self.prompt(&mut screen);
}
- write!(screen, "{}{}", clear::All, cursor::Show).unwrap();
+ let _ = queue!(screen, Clear(ClearType::All));
+ let _ = queue!(screen, cursor::Show);
+ let _ = queue!(screen, LeaveAlternateScreen);
+
+ let _ = terminal::disable_raw_mode();
}
- fn select_with_emacs_key_scheme(&mut self, k: Key) -> bool {
- match k {
- Key::Char('\n') | Key::Char('\r') | Key::Ctrl('j') => {
- self.run = true;
- self.accept_selection();
+ fn select_with_emacs_key_scheme(&mut self, event: Event) -> bool {
+ match event {
+ Event::Key(
+ KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code:
+ KeyCode::Char('c')
+ | KeyCode::Char('g')
+ | KeyCode::Char('z')
+ | KeyCode::Char('r'),
+ }
+ | KeyEvent {
+ code: KeyCode::Esc, ..
+ },
+ ) => {
+ self.run = false;
+ self.input.clear();
return true;
}
- Key::Char('\t') => {
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Tab, ..
+ }) => {
self.run = false;
self.accept_selection();
return true;
}
- Key::Ctrl('c') | Key::Ctrl('g') | Key::Ctrl('z') | Key::Esc | Key::Ctrl('r') => {
- self.run = false;
- self.input.clear();
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Enter,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('j'),
+ },
+ ) => {
+ self.run = true;
+ self.accept_selection();
return true;
}
- Key::Ctrl('b') => self.input.move_cursor(Move::Backward),
- Key::Ctrl('f') => self.input.move_cursor(Move::Forward),
- Key::Ctrl('a') => self.input.move_cursor(Move::BOL),
- Key::Ctrl('e') => self.input.move_cursor(Move::EOL),
- Key::Ctrl('w') | Key::Alt('\x08') | Key::Alt('\x7f') => {
+
+ Event::Key(
+ KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('w'),
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::ALT,
+ code: KeyCode::Char('\x08') | KeyCode::Char('\x7f'),
+ },
+ ) => {
self.input.delete(Move::BackwardWord);
self.refresh_matches();
}
- Key::Alt('d') => {
- self.input.delete(Move::ForwardWord);
- self.refresh_matches();
- }
- Key::Ctrl('v') => {
- self.debug = !self.debug;
- }
- Key::Alt('b') => self.input.move_cursor(Move::BackwardWord),
- Key::Alt('f') => self.input.move_cursor(Move::ForwardWord),
- Key::Left => self.input.move_cursor(Move::Backward),
- Key::Right => self.input.move_cursor(Move::Forward),
- Key::Up | Key::PageUp | Key::Ctrl('p') => self.move_selection(MoveSelection::Up),
- Key::Down | Key::PageDown | Key::Ctrl('n') => self.move_selection(MoveSelection::Down),
- Key::Ctrl('k') => {
- self.input.delete(Move::EOL);
- self.refresh_matches();
- }
- Key::Ctrl('u') => {
- self.input.delete(Move::BOL);
- self.refresh_matches();
- }
- Key::Backspace | Key::Ctrl('h') => {
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Left,
+ ..
+ }) => self.input.move_cursor(Move::Backward),
+ Event::Key(KeyEvent {
+ code: KeyCode::Right,
+ ..
+ }) => self.input.move_cursor(Move::Forward),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Up | KeyCode::PageUp,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('p'),
+ },
+ ) => self.move_selection(MoveSelection::Up),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Down | KeyCode::PageDown,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('n'),
+ },
+ ) => self.move_selection(MoveSelection::Down),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Backspace,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('h'),
+ },
+ ) => {
self.input.delete(Move::Backward);
self.refresh_matches();
}
- Key::Delete | Key::Ctrl('d') => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Delete,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('d')
+ }) => {
self.input.delete(Move::Forward);
self.refresh_matches();
}
- Key::Home => self.input.move_cursor(Move::BOL),
- Key::End => self.input.move_cursor(Move::EOL),
- Key::Char(c) => {
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Home,
+ ..
+ }) => self.input.move_cursor(Move::BOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::End, ..
+ }) => self.input.move_cursor(Move::EOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::Char(c),
+ ..
+ }) => {
self.input.insert(c);
self.refresh_matches();
}
- Key::F(2) => {
+ Event::Key(KeyEvent {
+ code: KeyCode::F(2),
+ ..
+ }) => {
if !self.matches.is_empty() {
if self.settings.delete_without_confirm {
self.delete_selection();
@@ -504,54 +551,151 @@ impl<'a> Interface<'a> {
}
}
}
+
+ Event::Key(KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code,
+ }) => match code {
+ KeyCode::Char('v') => self.debug = !self.debug,
+ KeyCode::Char('b') => self.input.move_cursor(Move::Backward),
+ KeyCode::Char('f') => self.input.move_cursor(Move::Forward),
+ KeyCode::Char('a') => self.input.move_cursor(Move::BOL),
+ KeyCode::Char('e') => self.input.move_cursor(Move::EOL),
+ KeyCode::Char('k') => {
+ self.input.delete(Move::EOL);
+ self.refresh_matches();
+ }
+ KeyCode::Char('u') => {
+ self.input.delete(Move::BOL);
+ self.refresh_matches();
+ }
+ _ => {}
+ },
+
+ Event::Key(KeyEvent {
+ modifiers: KeyModifiers::ALT,
+ code,
+ }) => match code {
+ KeyCode::Char('b') => self.input.move_cursor(Move::BackwardWord),
+ KeyCode::Char('f') => self.input.move_cursor(Move::ForwardWord),
+ KeyCode::Char('d') => {
+ self.input.delete(Move::ForwardWord);
+ self.refresh_matches();
+ }
+ _ => {}
+ },
+
_ => {}
}
false
}
- fn select_with_vim_key_scheme(&mut self, k: Key) -> bool {
+ fn select_with_vim_key_scheme(&mut self, event: Event) -> bool {
if self.in_vim_insert_mode {
- match k {
- Key::Char('\n') | Key::Char('\r') | Key::Ctrl('j') => {
- self.run = true;
+ match event {
+ Event::Key(KeyEvent {
+ code: KeyCode::Tab, ..
+ }) => {
+ self.run = false;
self.accept_selection();
return true;
}
- Key::Char('\t') => {
- self.run = false;
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Enter,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('j'),
+ },
+ ) => {
+ self.run = true;
self.accept_selection();
return true;
}
- Key::Ctrl('c') | Key::Ctrl('g') | Key::Ctrl('z') | Key::Ctrl('r') => {
+
+ Event::Key(KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code:
+ KeyCode::Char('c')
+ | KeyCode::Char('g')
+ | KeyCode::Char('z')
+ | KeyCode::Char('r'),
+ }) => {
self.run = false;
self.input.clear();
return true;
}
- Key::Left => self.input.move_cursor(Move::Backward),
- Key::Right => self.input.move_cursor(Move::Forward),
- Key::Up | Key::PageUp | Key::Ctrl('u') | Key::Ctrl('p') => {
- self.move_selection(MoveSelection::Up)
- }
- Key::Down | Key::PageDown | Key::Ctrl('d') | Key::Ctrl('n') => {
- self.move_selection(MoveSelection::Down)
- }
- Key::Esc => self.in_vim_insert_mode = false,
- Key::Backspace => {
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Left,
+ ..
+ }) => self.input.move_cursor(Move::Backward),
+ Event::Key(KeyEvent {
+ code: KeyCode::Right,
+ ..
+ }) => self.input.move_cursor(Move::Forward),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Up | KeyCode::PageUp,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('u') | KeyCode::Char('p'),
+ },
+ ) => self.move_selection(MoveSelection::Up),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Down | KeyCode::PageDown,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('d') | KeyCode::Char('n'),
+ },
+ ) => self.move_selection(MoveSelection::Down),
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Esc, ..
+ }) => self.in_vim_insert_mode = false,
+ Event::Key(KeyEvent {
+ code: KeyCode::Backspace,
+ ..
+ }) => {
self.input.delete(Move::Backward);
self.refresh_matches();
}
- Key::Delete => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Delete,
+ ..
+ }) => {
self.input.delete(Move::Forward);
self.refresh_matches();
}
- Key::Home => self.input.move_cursor(Move::BOL),
- Key::End => self.input.move_cursor(Move::EOL),
- Key::Char(c) => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Home,
+ ..
+ }) => self.input.move_cursor(Move::BOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::End, ..
+ }) => self.input.move_cursor(Move::EOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::Char(c),
+ ..
+ }) => {
self.input.insert(c);
self.refresh_matches();
}
- Key::F(2) => {
+ Event::Key(KeyEvent {
+ code: KeyCode::F(2),
+ ..
+ }) => {
if !self.matches.is_empty() {
if self.settings.delete_without_confirm {
self.delete_selection();
@@ -563,51 +707,127 @@ impl<'a> Interface<'a> {
_ => {}
}
} else {
- match k {
- Key::Char('\n') | Key::Char('\r') | Key::Ctrl('j') => {
- self.run = true;
+ match event {
+ Event::Key(KeyEvent {
+ code: KeyCode::Tab, ..
+ }) => {
+ self.run = false;
self.accept_selection();
return true;
}
- Key::Char('\t') => {
- self.run = false;
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Enter,
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('j'),
+ },
+ ) => {
+ self.run = true;
self.accept_selection();
return true;
}
- Key::Ctrl('c')
- | Key::Ctrl('g')
- | Key::Ctrl('z')
- | Key::Esc
- | Key::Char('q')
- // TODO add ZZ as shortcut
- | Key::Ctrl('r') => {
+
+ Event::Key(
+ KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code:
+ KeyCode::Char('c')
+ | KeyCode::Char('g')
+ | KeyCode::Char('z')
+ | KeyCode::Char('r'), // TODO add ZZ as shortcut
+ }
+ | KeyEvent {
+ code: KeyCode::Esc, ..
+ },
+ ) => {
self.run = false;
self.input.clear();
return true;
}
- Key::Left | Key::Char('h') => self.input.move_cursor(Move::Backward),
- Key::Right | Key::Char('l') => self.input.move_cursor(Move::Forward),
- Key::Up | Key::PageUp | Key::Char('k') | Key::Ctrl('u') => self.move_selection(MoveSelection::Up),
- Key::Down | Key::PageDown | Key::Char('j') | Key::Ctrl('d') => self.move_selection(MoveSelection::Down),
- Key::Char('b') | Key::Char('e') => self.input.move_cursor(Move::BackwardWord),
- Key::Char('w') => self.input.move_cursor(Move::ForwardWord),
- Key::Char('0') | Key::Char('^') => self.input.move_cursor(Move::BOL),
- Key::Char('$') => self.input.move_cursor(Move::EOL),
- Key::Char('i') | Key::Char('a') => self.in_vim_insert_mode = true,
- Key::Backspace => {
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Left | KeyCode::Char('h'),
+ ..
+ }) => self.input.move_cursor(Move::Backward),
+ Event::Key(KeyEvent {
+ code: KeyCode::Right | KeyCode::Char('l'),
+ ..
+ }) => self.input.move_cursor(Move::Forward),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Up | KeyCode::PageUp | KeyCode::Char('k'),
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('u'),
+ },
+ ) => self.move_selection(MoveSelection::Up),
+
+ Event::Key(
+ KeyEvent {
+ code: KeyCode::Down | KeyCode::PageDown | KeyCode::Char('j'),
+ ..
+ }
+ | KeyEvent {
+ modifiers: KeyModifiers::CONTROL,
+ code: KeyCode::Char('d'),
+ },
+ ) => self.move_selection(MoveSelection::Down),
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('b') | KeyCode::Char('e'),
+ ..
+ }) => self.input.move_cursor(Move::BackwardWord),
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('w'),
+ ..
+ }) => self.input.move_cursor(Move::ForwardWord),
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('0') | KeyCode::Char('^'),
+ ..
+ }) => self.input.move_cursor(Move::BOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('$'),
+ ..
+ }) => self.input.move_cursor(Move::EOL),
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('i') | KeyCode::Char('a'),
+ ..
+ }) => self.in_vim_insert_mode = true,
+
+ Event::Key(KeyEvent {
+ code: KeyCode::Backspace,
+ ..
+ }) => {
self.input.delete(Move::Backward);
self.refresh_matches();
}
- Key::Delete | Key::Char('x') => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Delete | KeyCode::Char('x'),
+ ..
+ }) => {
self.input.delete(Move::Forward);
self.refresh_matches();
}
- Key::Home => self.input.move_cursor(Move::BOL),
- Key::End => self.input.move_cursor(Move::EOL),
- Key::Char(_c) => {
-
- }
- Key::F(2) => {
+ Event::Key(KeyEvent {
+ code: KeyCode::Home,
+ ..
+ }) => self.input.move_cursor(Move::BOL),
+ Event::Key(KeyEvent {
+ code: KeyCode::End, ..
+ }) => self.input.move_cursor(Move::EOL),
+
+ Event::Key(KeyEvent {
+ code: KeyCode::F(2),
+ ..
+ }) => {
if !self.matches.is_empty() {
if self.settings.delete_without_confirm {
self.delete_selection();
@@ -627,8 +847,8 @@ impl<'a> Interface<'a> {
command: &Command,
search: &str,
width: u16,
- highlight_color: String,
- base_color: String,
+ highlight_color: Color,
+ base_color: Color,
debug: bool,
) -> String {
let mut prev: usize = 0;
@@ -646,9 +866,9 @@ impl<'a> Interface<'a> {
out.push_grapheme_str(&command.cmd[prev..*start]);
}
- out.push_str(&highlight_color);
+ out.push_str(&format!("{}", SetForegroundColor(highlight_color)));
out.push_grapheme_str(&command.cmd[*start..*end]);
- out.push_str(&base_color);
+ out.push_str(&format!("{}", SetForegroundColor(base_color)));
prev = *end;
}
}
@@ -660,7 +880,7 @@ impl<'a> Interface<'a> {
if debug {
out.max_grapheme_length += debug_space;
out.push_grapheme_str(" ");
- out.push_str(&format!("{}", color::Fg(color::LightBlue)));
+ out.push_str(&format!("{}", SetForegroundColor(Color::Blue)));
out.push_grapheme_str(format!("rnk: {:.*} ", 2, command.rank));
out.push_grapheme_str(format!("age: {:.*} ", 2, command.features.age_factor));
out.push_grapheme_str(format!("lng: {:.*} ", 2, command.features.length_factor));
@@ -687,14 +907,15 @@ impl<'a> Interface<'a> {
"s_occ: {:.*} ",
2, command.features.selected_occurrences_factor
));
- out.push_str(&base_color);
+
+ out.push_str(&format!("{}", SetForegroundColor(base_color)));
}
out.string
}
fn result_top_index(&self) -> u16 {
- let (_width, height): (u16, u16) = terminal_size().unwrap();
+ let (_width, height): (u16, u16) = terminal::size().unwrap();
if self.is_screen_view_bottom() {
return height - RESULTS_TOP_INDEX;
@@ -703,7 +924,7 @@ impl<'a> Interface<'a> {
}
fn prompt_line_index(&self) -> u16 {
- let (_width, height): (u16, u16) = terminal_size().unwrap();
+ let (_width, height): (u16, u16) = terminal::size().unwrap();
if self.is_screen_view_bottom() {
return height - PROMPT_LINE_INDEX;
}
@@ -711,7 +932,7 @@ impl<'a> Interface<'a> {
}
fn info_line_index(&self) -> u16 {
- let (_width, height): (u16, u16) = terminal_size().unwrap();
+ let (_width, height): (u16, u16) = terminal::size().unwrap();
if self.is_screen_view_bottom() {
return height;
}
@@ -744,3 +965,5 @@ impl<'a> Interface<'a> {
// Ctrl('q') | Ctrl('v') => quoted insert
// Ctrl('y') => yank
// Ctrl('_') => undo
+
+
diff --git a/src/settings.rs b/src/settings.rs
index 51b38011..ca21bb24 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -1,15 +1,21 @@
+
use crate::shell_history;
use clap::AppSettings;
use clap::{crate_authors, crate_version, value_t};
use clap::{App, Arg, SubCommand};
use dirs::home_dir;
+use figment::{
+ providers::{Format, Serialized, Toml},
+ Figment,
+};
+use serde::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
-#[derive(Debug)]
+#[derive(Debug, Serialize, Deserialize)]
pub enum Mode {
Add,
Search,
@@ -18,32 +24,32 @@ pub enum Mode {
Init,
}
-#[derive(Debug)]
+#[derive(Debug, Serialize, Deserialize)]
pub enum KeyScheme {
Emacs,
Vim,
}
-#[derive(Debug)]
+#[derive(Debug, Serialize, Deserialize)]
pub enum InitMode {
Bash,
Zsh,
Fish,
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum InterfaceView {
Top,
Bottom,
}
-#[derive(Debug)]
+#[derive(Debug, Serialize, Deserialize)]
pub enum ResultSort {
Rank,
LastRun,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum HistoryFormat {
/// bash format - commands in plain text, one per line, with multi-line commands joined.
/// HISTTIMEFORMAT is assumed to be empty.
@@ -61,7 +67,23 @@ pub enum HistoryFormat {
Fish,
}
-#[derive(Debug)]
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Colors {
+ pub menu_bg: String,
+ pub menu_fg: String,
+ pub menu_deleting_bg: String,
+ pub menu_deleting_fg: String,
+ pub bg: String,
+ pub fg: String,
+ pub prompt_fg: String,
+ pub highlight: String,
+ pub timing: String,
+ pub cursor_bg: String,
+ pub cursor_fg: String,
+ pub cursor_highlight: String,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
pub struct Settings {
pub mode: Mode,
pub debug: bool,
@@ -77,7 +99,6 @@ pub struct Settings {
pub old_dir: Option,
pub append_to_histfile: bool,
pub refresh_training_cache: bool,
- pub lightmode: bool,
pub key_scheme: KeyScheme,
pub history_format: HistoryFormat,
pub limit: Option,
@@ -86,6 +107,7 @@ pub struct Settings {
pub delete_without_confirm: bool,
pub interface_view: InterfaceView,
pub result_sort: ResultSort,
+ pub colors: Colors,
}
impl Default for Settings {
@@ -105,7 +127,6 @@ impl Default for Settings {
append_to_histfile: false,
debug: false,
fuzzy: false,
- lightmode: false,
key_scheme: KeyScheme::Emacs,
history_format: HistoryFormat::Bash,
limit: None,
@@ -114,6 +135,20 @@ impl Default for Settings {
delete_without_confirm: false,
interface_view: InterfaceView::Top,
result_sort: ResultSort::Rank,
+ colors: Colors {
+ menu_bg: "Blue".to_string(),
+ menu_fg: "White".to_string(),
+ menu_deleting_bg: "Red".to_string(),
+ menu_deleting_fg: "Cyan".to_string(),
+ bg: "Black".to_string(),
+ fg: "White".to_string(),
+ prompt_fg: "White".to_string(),
+ highlight: "Green".to_string(),
+ timing: "Blue".to_string(),
+ cursor_bg: "Grey".to_string(),
+ cursor_fg: "Black".to_string(),
+ cursor_highlight: "Green".to_string(),
+ },
}
}
}
@@ -247,7 +282,30 @@ impl Settings {
)
.get_matches();
- let mut settings = Settings::default();
+ let mut default_settings = Settings::default();
+
+ if env::var("MCFLY_LIGHT").is_ok() {
+ default_settings.colors = Colors {
+ menu_bg: "Blue".to_string(),
+ menu_fg: "White".to_string(),
+ menu_deleting_bg: "Red".to_string(),
+ menu_deleting_fg: "Cyan".to_string(),
+ bg: "White".to_string(),
+ fg: "Black".to_string(),
+ prompt_fg: "Black".to_string(),
+ highlight: "Blue".to_string(),
+ timing: "Blue".to_string(),
+ cursor_bg: "Dark_Grey".to_string(),
+ cursor_fg: "White".to_string(),
+ cursor_highlight: "White".to_string(),
+ };
+ }
+
+ let mut settings: Settings = Figment::from(Serialized::defaults(default_settings))
+ .merge(Toml::file(Settings::mcfly_config_path()))
+ .extract()
+ .unwrap();
+
if matches.is_present("init") {
settings.skip_environment_check = true;
}
@@ -404,8 +462,9 @@ impl Settings {
settings.results = results;
}
- settings.fuzzy =
- search_matches.is_present("fuzzy") || env::var("MCFLY_FUZZY").is_ok();
+ settings.fuzzy = settings.fuzzy
+ || env::var("MCFLY_FUZZY").is_ok()
+ || search_matches.is_present("fuzzy");
settings.delete_without_confirm = search_matches
.is_present("delete_without_confirm")
@@ -472,14 +531,12 @@ impl Settings {
_ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!()
}
- settings.lightmode = match env::var_os("MCFLY_LIGHT") {
- Some(_val) => true,
- None => false,
- };
- settings.key_scheme = match env::var("MCFLY_KEY_SCHEME").as_ref().map(String::as_ref) {
- Ok("vim") => KeyScheme::Vim,
- _ => KeyScheme::Emacs,
- };
+ if env::var("MCFLY_KEY_SCHEME").is_ok() {
+ settings.key_scheme = match env::var("MCFLY_KEY_SCHEME").as_ref().map(String::as_ref) {
+ Ok("vim") => KeyScheme::Vim,
+ _ => KeyScheme::Emacs,
+ };
+ }
settings
}
@@ -494,7 +551,12 @@ impl Settings {
.join(PathBuf::from(".mcfly"))
}
+ pub fn mcfly_config_path() -> PathBuf {
+ Settings::storage_dir_path().join(PathBuf::from("mcfly.toml"))
+ }
+
pub fn mcfly_db_path() -> PathBuf {
Settings::storage_dir_path().join(PathBuf::from("history.db"))
}
}
+