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 iterm2 UI instructions ## 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")) } } +