mirror of
https://github.com/nushell/nushell
synced 2024-11-10 07:04:13 +00:00
Help menu (#4992)
* nu-completer with suggestions * help menu with scrolling * updates description rows based on space * configuration for help menu * update nu-ansi-term * corrected test for update cells * changed keybinding
This commit is contained in:
parent
0011f4df56
commit
a4410fef40
15 changed files with 1152 additions and 192 deletions
154
Cargo.lock
generated
154
Cargo.lock
generated
|
@ -182,9 +182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625"
|
||||
checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
|
@ -192,9 +192,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
|
||||
checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -203,9 +203,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.52"
|
||||
version = "0.1.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
|
||||
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -428,9 +428,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "capnp"
|
||||
version = "0.14.5"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16c262726f68118392269a3f7a5546baf51dcfe5cb3c3f0957b502106bf1a065"
|
||||
checksum = "21d5d7da973146f1720672faa44f1523cc8f923636190ca1a931c7bc8834de68"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
|
@ -568,9 +568,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -586,9 +586,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.2"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
|
@ -607,10 +607,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.7"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
|
||||
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
|
@ -620,9 +621,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
|
@ -630,9 +631,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77b75a27dc8d220f1f8521ea69cd55a34d720a200ebb3a624d9aa19193d3b432"
|
||||
checksum = "f1fd7173631a4e9e2ca8b32ae2fad58aab9843ea5aaf56642661937d87e28a3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
|
@ -731,9 +732,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
|
||||
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -832,9 +833,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
|
@ -914,14 +915,14 @@ dependencies = [
|
|||
"rustc_version",
|
||||
"toml",
|
||||
"vswhom",
|
||||
"winreg 0.10.1",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eml-parser"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "031fe36712cec8b81c5b76b555666ce855a4dfc2dcc35bb907046bf2ef545578"
|
||||
checksum = "43e6fc6e74658e477675b59e61e10e9722cb2b845b0e2834df60f979c865e821"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
@ -966,9 +967,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.18"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56047058e1ab118075ca22f9ecd737bcc961aa3566a3019cb71388afa280bd8a"
|
||||
checksum = "ad132dd8d0d0b546348d7d86cb3191aad14b34e5f979781fc005c80d4ac67ffd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -1011,9 +1012,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "3.0.4"
|
||||
version = "3.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02ecad9808e0596f8956d14f7fa868f996290bd01c8d7329d6e5bc2bb76adf8f"
|
||||
checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
|
@ -1431,9 +1432,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.25.1"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
|
||||
checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
|
@ -1494,9 +1495,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.17"
|
||||
version = "0.14.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd"
|
||||
checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -1605,9 +1606,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "0.5.3"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6"
|
||||
checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504"
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
|
@ -1735,9 +1736,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lexical-write-float"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f6202bff3d35ede41a6200227837468bb92e4ecdd437328b1055ed218fb855"
|
||||
checksum = "8a89ec1d062e481210c309b672f73a0567b7855f21e7d2fae636df44d12e97f9"
|
||||
dependencies = [
|
||||
"lexical-util",
|
||||
"lexical-write-integer",
|
||||
|
@ -1756,9 +1757,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.119"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
|
@ -1843,9 +1844,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
@ -2020,14 +2021,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
|
||||
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -2717,9 +2719,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.2.0"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20448fd678ec04e6ea15bbe0476874af65e98a01515d667aa49f1434dc44ebf4"
|
||||
checksum = "5e72e30578e0d0993c8ae20823dd9cff2bc5517d2f586a8aef462a581e8a03eb"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
|
@ -3111,9 +3113,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50"
|
||||
checksum = "57c038cb5319b9c704bf9c227c261d275bfec0ad438118a2787ce47944fb228b"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
|
@ -3234,9 +3236,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -3405,27 +3407,28 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
|
||||
checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
|
||||
dependencies = [
|
||||
"getrandom 0.2.5",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/nushell/reedline#bc528de132e74594fdd5a9202cf32aee51e921e8"
|
||||
source = "git+https://github.com/nushell/reedline?branch=main#e982abf7e21dcb41e9e1d715f64259a4817bb1bc"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm",
|
||||
|
@ -3473,9 +3476,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.9"
|
||||
version = "0.11.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525"
|
||||
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
|
@ -3504,7 +3507,7 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg 0.7.0",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3598,9 +3601,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.33.4"
|
||||
version = "0.34.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef7ec6a44fba95d21fa522760c03c16ca5ee95cebb6e4ef579cab3e6d7ba6c06"
|
||||
checksum = "cd3cc851a13d30a34cb747ba2a0c5101a4b2e8b1677a29b213ee465365ea495e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
|
@ -4125,9 +4128,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4304,7 +4307,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio 0.8.0",
|
||||
"mio 0.8.2",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
|
@ -4521,9 +4524,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
|||
|
||||
[[package]]
|
||||
name = "utf8-width"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
|
||||
checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
|
@ -4647,6 +4650,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
|
@ -4725,9 +4734,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.4"
|
||||
version = "4.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2"
|
||||
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
|
@ -4881,15 +4890,6 @@ version = "0.32.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
@ -4916,9 +4916,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.3"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608"
|
||||
checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317"
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
|
|
|
@ -38,7 +38,7 @@ crossterm_winapi = "0.9.0"
|
|||
ctrlc = "3.2.1"
|
||||
log = "0.4"
|
||||
miette = "4.1.0"
|
||||
nu-ansi-term = "0.45.0"
|
||||
nu-ansi-term = "0.45.1"
|
||||
nu-cli = { path="./crates/nu-cli", version = "0.60.1" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.60.1" }
|
||||
nu-command = { path="./crates/nu-command", version = "0.60.1" }
|
||||
|
@ -55,7 +55,8 @@ nu-term-grid = { path = "./crates/nu-term-grid", version = "0.60.1" }
|
|||
nu-utils = { path = "./crates/nu-utils", version = "0.60.1" }
|
||||
pretty_env_logger = "0.4.0"
|
||||
rayon = "1.5.1"
|
||||
reedline = { git = "https://github.com/nushell/reedline" }
|
||||
#reedline = "0.3.0"
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
is_executable = "1.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -12,13 +12,16 @@ nu-path = { path = "../nu-path", version = "0.60.1" }
|
|||
nu-parser = { path = "../nu-parser", version = "0.60.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.60.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.60.1" }
|
||||
nu-ansi-term = "0.45.0"
|
||||
nu-ansi-term = "0.45.1"
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.60.1" }
|
||||
|
||||
crossterm = "0.23.0"
|
||||
crossterm_winapi = "0.9.0"
|
||||
miette = { version = "4.1.0", features = ["fancy"] }
|
||||
thiserror = "1.0.29"
|
||||
reedline = { git = "https://github.com/nushell/reedline" }
|
||||
#reedline = "0.3.0"
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
|
||||
log = "0.4"
|
||||
is_executable = "1.0.1"
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
|||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
PipelineData, Span, Value, CONFIG_VARIABLE_ID,
|
||||
};
|
||||
use reedline::Completer;
|
||||
use reedline::{Completer, Suggestion};
|
||||
|
||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||
|
||||
|
@ -83,46 +83,49 @@ impl NuCompleter {
|
|||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
) -> Vec<(reedline::Span, String)> {
|
||||
) -> Vec<Suggestion> {
|
||||
let mut output = vec![];
|
||||
|
||||
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
||||
|
||||
for builtin in builtins {
|
||||
if builtin.as_bytes().starts_with(prefix) {
|
||||
output.push((
|
||||
reedline::Span {
|
||||
output.push(Suggestion {
|
||||
value: builtin.to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
builtin.to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for scope in &working_set.delta.scope {
|
||||
for v in &scope.vars {
|
||||
if v.0.starts_with(prefix) {
|
||||
output.push((
|
||||
reedline::Span {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(v.0).to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for scope in &self.engine_state.scope {
|
||||
for v in &scope.vars {
|
||||
if v.0.starts_with(prefix) {
|
||||
output.push((
|
||||
reedline::Span {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(v.0).to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,34 +141,32 @@ impl NuCompleter {
|
|||
span: Span,
|
||||
offset: usize,
|
||||
find_externals: bool,
|
||||
) -> Vec<(reedline::Span, String)> {
|
||||
) -> Vec<Suggestion> {
|
||||
let prefix = working_set.get_span_contents(span);
|
||||
|
||||
let results = working_set
|
||||
.find_commands_by_prefix(prefix)
|
||||
.into_iter()
|
||||
.map(move |x| {
|
||||
(
|
||||
reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(&x).to_string(),
|
||||
)
|
||||
.map(move |x| Suggestion {
|
||||
value: String::from_utf8_lossy(&x).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
|
||||
let results_aliases =
|
||||
working_set
|
||||
.find_aliases_by_prefix(prefix)
|
||||
.into_iter()
|
||||
.map(move |x| {
|
||||
(
|
||||
reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(&x).to_string(),
|
||||
)
|
||||
.map(move |x| Suggestion {
|
||||
value: String::from_utf8_lossy(&x).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
|
||||
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
|
||||
|
@ -176,19 +177,22 @@ impl NuCompleter {
|
|||
let results_external =
|
||||
self.external_command_completion(&prefix)
|
||||
.into_iter()
|
||||
.map(move |x| {
|
||||
(
|
||||
reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
x,
|
||||
)
|
||||
.map(move |x| Suggestion {
|
||||
value: x,
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
|
||||
for external in results_external {
|
||||
if results.contains(&external) {
|
||||
results.push((external.0, format!("^{}", external.1)))
|
||||
results.push(Suggestion {
|
||||
value: format!("^{}", external.value),
|
||||
description: None,
|
||||
span: external.span,
|
||||
})
|
||||
} else {
|
||||
results.push(external)
|
||||
}
|
||||
|
@ -200,7 +204,7 @@ impl NuCompleter {
|
|||
}
|
||||
}
|
||||
|
||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||
let offset = working_set.next_span_start();
|
||||
let mut line = line.to_string();
|
||||
|
@ -231,7 +235,7 @@ impl NuCompleter {
|
|||
if prefix.starts_with(b"$") {
|
||||
let mut output =
|
||||
self.complete_variables(&working_set, &prefix, new_span, offset);
|
||||
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
output.sort_by(|a, b| a.value.cmp(&b.value));
|
||||
return output;
|
||||
}
|
||||
if prefix.starts_with(b"-") {
|
||||
|
@ -248,13 +252,14 @@ impl NuCompleter {
|
|||
short.encode_utf8(&mut named);
|
||||
named.insert(0, b'-');
|
||||
if named.starts_with(&prefix) {
|
||||
output.push((
|
||||
reedline::Span {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: new_span.start - offset,
|
||||
end: new_span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(&named).to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,16 +271,17 @@ impl NuCompleter {
|
|||
named.insert(0, b'-');
|
||||
named.insert(0, b'-');
|
||||
if named.starts_with(&prefix) {
|
||||
output.push((
|
||||
reedline::Span {
|
||||
output.push(Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: new_span.start - offset,
|
||||
end: new_span.end - offset,
|
||||
},
|
||||
String::from_utf8_lossy(&named).to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
output.sort_by(|a, b| a.value.cmp(&b.value));
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
@ -317,18 +323,19 @@ impl NuCompleter {
|
|||
list: impl Iterator<Item = &'a Value>,
|
||||
new_span: Span,
|
||||
offset: usize,
|
||||
) -> Vec<(reedline::Span, String)> {
|
||||
) -> Vec<Suggestion> {
|
||||
list.filter_map(move |x| {
|
||||
let s = x.as_string();
|
||||
|
||||
match s {
|
||||
Ok(s) => Some((
|
||||
reedline::Span {
|
||||
Ok(s) => Some(Suggestion {
|
||||
value: s,
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: new_span.start - offset,
|
||||
end: new_span.end - offset,
|
||||
},
|
||||
s,
|
||||
)),
|
||||
}),
|
||||
Err(_) => None,
|
||||
}
|
||||
})
|
||||
|
@ -388,17 +395,19 @@ impl NuCompleter {
|
|||
_ => (vec![], CompletionOptions::default()),
|
||||
};
|
||||
|
||||
let mut completions: Vec<(reedline::Span, String)> = completions
|
||||
let mut completions: Vec<Suggestion> = completions
|
||||
.into_iter()
|
||||
.filter(|it| {
|
||||
// Minimise clones for new functionality
|
||||
match (options.case_sensitive, options.positional) {
|
||||
(true, true) => it.1.as_bytes().starts_with(&prefix),
|
||||
(true, false) => it.1.contains(
|
||||
(true, true) => {
|
||||
it.value.as_bytes().starts_with(&prefix)
|
||||
}
|
||||
(true, false) => it.value.contains(
|
||||
std::str::from_utf8(&prefix).unwrap_or(""),
|
||||
),
|
||||
(false, positional) => {
|
||||
let value = it.1.to_lowercase();
|
||||
let value = it.value.to_lowercase();
|
||||
let prefix = std::str::from_utf8(&prefix)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
|
@ -413,7 +422,7 @@ impl NuCompleter {
|
|||
.collect();
|
||||
|
||||
if options.sort {
|
||||
completions.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
completions.sort_by(|a, b| a.value.cmp(&b.value));
|
||||
}
|
||||
|
||||
return completions;
|
||||
|
@ -431,17 +440,16 @@ impl NuCompleter {
|
|||
let mut output: Vec<_> =
|
||||
file_path_completion(new_span, &prefix, &cwd)
|
||||
.into_iter()
|
||||
.map(move |x| {
|
||||
(
|
||||
reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
end: x.0.end - offset,
|
||||
},
|
||||
x.1,
|
||||
)
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
end: x.0.end - offset,
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
output.sort_by(|a, b| a.value.cmp(&b.value));
|
||||
return output;
|
||||
}
|
||||
flat_shape => {
|
||||
|
@ -542,20 +550,19 @@ impl NuCompleter {
|
|||
(x.0, x.1)
|
||||
}
|
||||
})
|
||||
.map(move |x| {
|
||||
(
|
||||
reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
end: x.0.end - offset,
|
||||
},
|
||||
x.1,
|
||||
)
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
description: None,
|
||||
span: reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
end: x.0.end - offset,
|
||||
},
|
||||
})
|
||||
.chain(subcommands.into_iter())
|
||||
.chain(commands.into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
//output.dedup_by(|a, b| a.1 == b.1);
|
||||
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
output.sort_by(|a, b| a.value.cmp(&b.value));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -570,7 +577,7 @@ impl NuCompleter {
|
|||
}
|
||||
|
||||
impl Completer for NuCompleter {
|
||||
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
||||
fn complete(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||
self.completion_helper(line, pos)
|
||||
}
|
||||
}
|
||||
|
|
101
crates/nu-cli/src/help_completions.rs
Normal file
101
crates/nu-cli/src/help_completions.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use nu_engine::documentation::get_flags_section;
|
||||
use nu_protocol::engine::EngineState;
|
||||
use reedline::{Completer, Suggestion};
|
||||
|
||||
pub const EXAMPLE_MARKER: &str = ">>>>>>";
|
||||
pub const EXAMPLE_NEW_LINE: &str = "%%%%%%";
|
||||
|
||||
pub struct NuHelpCompleter {
|
||||
engine_state: EngineState,
|
||||
}
|
||||
|
||||
impl NuHelpCompleter {
|
||||
pub fn new(engine_state: EngineState) -> Self {
|
||||
Self { engine_state }
|
||||
}
|
||||
|
||||
fn completion_helper(&self, line: &str, _pos: usize) -> Vec<Suggestion> {
|
||||
let full_commands = self.engine_state.get_signatures_with_examples(false);
|
||||
|
||||
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||
full_commands
|
||||
.iter()
|
||||
.filter(|(sig, _, _, _)| {
|
||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||
|| sig
|
||||
.extra_usage
|
||||
.to_lowercase()
|
||||
.contains(&line.to_lowercase())
|
||||
})
|
||||
.map(|(sig, examples, _, _)| {
|
||||
let mut long_desc = String::new();
|
||||
|
||||
let usage = &sig.usage;
|
||||
if !usage.is_empty() {
|
||||
long_desc.push_str(usage);
|
||||
long_desc.push_str("\r\n\r\n");
|
||||
}
|
||||
|
||||
let extra_usage = &sig.extra_usage;
|
||||
if !extra_usage.is_empty() {
|
||||
long_desc.push_str(extra_usage);
|
||||
long_desc.push_str("\r\n\r\n");
|
||||
}
|
||||
|
||||
long_desc.push_str(&format!("Usage:\r\n > {}\r\n", sig.call_signature()));
|
||||
|
||||
if !sig.named.is_empty() {
|
||||
long_desc.push_str(&get_flags_section(sig))
|
||||
}
|
||||
|
||||
if !sig.required_positional.is_empty()
|
||||
|| !sig.optional_positional.is_empty()
|
||||
|| sig.rest_positional.is_some()
|
||||
{
|
||||
long_desc.push_str("\r\nParameters:\r\n");
|
||||
for positional in &sig.required_positional {
|
||||
long_desc
|
||||
.push_str(&format!(" {}: {}\r\n", positional.name, positional.desc));
|
||||
}
|
||||
for positional in &sig.optional_positional {
|
||||
long_desc.push_str(&format!(
|
||||
" (optional) {}: {}\r\n",
|
||||
positional.name, positional.desc
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(rest_positional) = &sig.rest_positional {
|
||||
long_desc.push_str(&format!(
|
||||
" ...{}: {}\r\n",
|
||||
rest_positional.name, rest_positional.desc
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for example in examples {
|
||||
long_desc.push_str(&format!(
|
||||
"{}{}\r\n",
|
||||
EXAMPLE_MARKER,
|
||||
example.example.replace('\n', EXAMPLE_NEW_LINE)
|
||||
))
|
||||
}
|
||||
|
||||
Suggestion {
|
||||
value: sig.name.clone(),
|
||||
description: Some(long_desc),
|
||||
span: reedline::Span {
|
||||
start: 0,
|
||||
end: sig.name.len(),
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for NuHelpCompleter {
|
||||
fn complete(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||
self.completion_helper(line, pos)
|
||||
}
|
||||
}
|
718
crates/nu-cli/src/help_menu.rs
Normal file
718
crates/nu-cli/src/help_menu.rs
Normal file
|
@ -0,0 +1,718 @@
|
|||
use {
|
||||
crate::help_completions::{EXAMPLE_MARKER, EXAMPLE_NEW_LINE},
|
||||
nu_ansi_term::{ansi::RESET, Style},
|
||||
reedline::{
|
||||
menu_functions::string_difference, Completer, History, LineBuffer, Menu, MenuEvent,
|
||||
MenuTextStyle, Painter, Suggestion,
|
||||
},
|
||||
};
|
||||
|
||||
/// Default values used as reference for the menu. These values are set during
|
||||
/// the initial declaration of the menu and are always kept as reference for the
|
||||
/// changeable [`WorkingDetails`]
|
||||
struct DefaultMenuDetails {
|
||||
/// Number of columns that the menu will have
|
||||
pub columns: u16,
|
||||
/// Column width
|
||||
pub col_width: Option<usize>,
|
||||
/// Column padding
|
||||
pub col_padding: usize,
|
||||
/// Number of rows for commands
|
||||
pub selection_rows: u16,
|
||||
/// Number of rows allowed to display the description
|
||||
pub description_rows: usize,
|
||||
}
|
||||
|
||||
impl Default for DefaultMenuDetails {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
columns: 4,
|
||||
col_width: None,
|
||||
col_padding: 2,
|
||||
selection_rows: 4,
|
||||
description_rows: 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the actual column conditions of the menu. These conditions change
|
||||
/// since they need to accommodate possible different line sizes for the column values
|
||||
#[derive(Default)]
|
||||
struct WorkingDetails {
|
||||
/// Number of columns that the menu will have
|
||||
pub columns: u16,
|
||||
/// Column width
|
||||
pub col_width: usize,
|
||||
/// Number of rows for description
|
||||
pub description_rows: usize,
|
||||
}
|
||||
|
||||
/// Completion menu definition
|
||||
pub struct NuHelpMenu {
|
||||
active: bool,
|
||||
/// Menu coloring
|
||||
color: MenuTextStyle,
|
||||
/// Default column details that are set when creating the menu
|
||||
/// These values are the reference for the working details
|
||||
default_details: DefaultMenuDetails,
|
||||
/// Number of minimum rows that are displayed when
|
||||
/// the required lines is larger than the available lines
|
||||
min_rows: u16,
|
||||
/// Working column details keep changing based on the collected values
|
||||
working_details: WorkingDetails,
|
||||
/// Menu cached values
|
||||
values: Vec<Suggestion>,
|
||||
/// column position of the cursor. Starts from 0
|
||||
col_pos: u16,
|
||||
/// row position in the menu. Starts from 0
|
||||
row_pos: u16,
|
||||
/// Menu marker when active
|
||||
marker: String,
|
||||
/// Event sent to the menu
|
||||
event: Option<MenuEvent>,
|
||||
/// String collected after the menu is activated
|
||||
input: Option<String>,
|
||||
/// Examples to select
|
||||
examples: Vec<String>,
|
||||
/// Example index
|
||||
example_index: Option<usize>,
|
||||
/// Examples may not be shown if there is not enough space in the screen
|
||||
show_examples: bool,
|
||||
/// Skipped description rows
|
||||
skipped_rows: usize,
|
||||
}
|
||||
|
||||
impl Default for NuHelpMenu {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active: false,
|
||||
color: MenuTextStyle::default(),
|
||||
default_details: DefaultMenuDetails::default(),
|
||||
min_rows: 3,
|
||||
working_details: WorkingDetails::default(),
|
||||
values: Vec::new(),
|
||||
col_pos: 0,
|
||||
row_pos: 0,
|
||||
marker: "| ".to_string(),
|
||||
event: None,
|
||||
input: None,
|
||||
examples: Vec::new(),
|
||||
example_index: None,
|
||||
show_examples: true,
|
||||
skipped_rows: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NuHelpMenu {
|
||||
/// Menu builder with new value for text style
|
||||
pub fn with_text_style(mut self, text_style: Style) -> Self {
|
||||
self.color.text_style = text_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new value for text style
|
||||
pub fn with_selected_text_style(mut self, selected_text_style: Style) -> Self {
|
||||
self.color.selected_text_style = selected_text_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new value for text style
|
||||
pub fn with_description_text_style(mut self, description_text_style: Style) -> Self {
|
||||
self.color.description_style = description_text_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new columns value
|
||||
pub fn with_columns(mut self, columns: u16) -> Self {
|
||||
self.default_details.columns = columns;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new column width value
|
||||
pub fn with_column_width(mut self, col_width: Option<usize>) -> Self {
|
||||
self.default_details.col_width = col_width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new column width value
|
||||
pub fn with_column_padding(mut self, col_padding: usize) -> Self {
|
||||
self.default_details.col_padding = col_padding;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new selection rows value
|
||||
pub fn with_selection_rows(mut self, selection_rows: u16) -> Self {
|
||||
self.default_details.selection_rows = selection_rows;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with new description rows value
|
||||
pub fn with_description_rows(mut self, description_rows: usize) -> Self {
|
||||
self.default_details.description_rows = description_rows;
|
||||
self
|
||||
}
|
||||
|
||||
/// Menu builder with marker
|
||||
pub fn with_marker(mut self, marker: String) -> Self {
|
||||
self.marker = marker;
|
||||
self
|
||||
}
|
||||
|
||||
/// Move menu cursor to the next element
|
||||
fn move_next(&mut self) {
|
||||
let mut new_col = self.col_pos + 1;
|
||||
let mut new_row = self.row_pos;
|
||||
|
||||
if new_col >= self.get_cols() {
|
||||
new_row += 1;
|
||||
new_col = 0;
|
||||
}
|
||||
|
||||
if new_row >= self.get_rows() {
|
||||
new_row = 0;
|
||||
new_col = 0;
|
||||
}
|
||||
|
||||
let position = new_row * self.get_cols() + new_col;
|
||||
if position >= self.get_values().len() as u16 {
|
||||
self.reset_position();
|
||||
} else {
|
||||
self.col_pos = new_col;
|
||||
self.row_pos = new_row;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move menu cursor to the previous element
|
||||
fn move_previous(&mut self) {
|
||||
let new_col = self.col_pos.checked_sub(1);
|
||||
|
||||
let (new_col, new_row) = match new_col {
|
||||
Some(col) => (col, self.row_pos),
|
||||
None => match self.row_pos.checked_sub(1) {
|
||||
Some(row) => (self.get_cols().saturating_sub(1), row),
|
||||
None => (
|
||||
self.get_cols().saturating_sub(1),
|
||||
self.get_rows().saturating_sub(1),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
let position = new_row * self.get_cols() + new_col;
|
||||
if position >= self.get_values().len() as u16 {
|
||||
self.col_pos = (self.get_values().len() as u16 % self.get_cols()).saturating_sub(1);
|
||||
self.row_pos = self.get_rows().saturating_sub(1);
|
||||
} else {
|
||||
self.col_pos = new_col;
|
||||
self.row_pos = new_row;
|
||||
}
|
||||
}
|
||||
|
||||
/// Menu index based on column and row position
|
||||
fn index(&self) -> usize {
|
||||
let index = self.row_pos * self.get_cols() + self.col_pos;
|
||||
index as usize
|
||||
}
|
||||
|
||||
/// Get selected value from the menu
|
||||
fn get_value(&self) -> Option<Suggestion> {
|
||||
self.get_values().get(self.index()).cloned()
|
||||
}
|
||||
|
||||
/// Calculates how many rows the Menu will use
|
||||
fn get_rows(&self) -> u16 {
|
||||
let values = self.get_values().len() as u16;
|
||||
|
||||
if values == 0 {
|
||||
// When the values are empty the no_records_msg is shown, taking 1 line
|
||||
return 1;
|
||||
}
|
||||
|
||||
let rows = values / self.get_cols();
|
||||
if values % self.get_cols() != 0 {
|
||||
rows + 1
|
||||
} else {
|
||||
rows
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns working details col width
|
||||
fn get_width(&self) -> usize {
|
||||
self.working_details.col_width
|
||||
}
|
||||
|
||||
/// Reset menu position
|
||||
fn reset_position(&mut self) {
|
||||
self.col_pos = 0;
|
||||
self.row_pos = 0;
|
||||
self.skipped_rows = 0;
|
||||
}
|
||||
|
||||
fn no_records_msg(&self, use_ansi_coloring: bool) -> String {
|
||||
let msg = "TYPE TO START SEACH";
|
||||
if use_ansi_coloring {
|
||||
format!(
|
||||
"{}{}{}",
|
||||
self.color.selected_text_style.prefix(),
|
||||
msg,
|
||||
RESET
|
||||
)
|
||||
} else {
|
||||
msg.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns working details columns
|
||||
fn get_cols(&self) -> u16 {
|
||||
self.working_details.columns.max(1)
|
||||
}
|
||||
|
||||
/// End of line for menu
|
||||
fn end_of_line(&self, column: u16, index: usize) -> &str {
|
||||
let is_last = index == self.values.len().saturating_sub(1);
|
||||
if column == self.get_cols().saturating_sub(1) || is_last {
|
||||
"\r\n"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/// Update list of examples from the actual value
|
||||
fn update_examples(&mut self) {
|
||||
let examples = self
|
||||
.get_value()
|
||||
.and_then(|suggestion| suggestion.description)
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.lines()
|
||||
.filter(|line| line.starts_with(EXAMPLE_MARKER))
|
||||
.map(|line| {
|
||||
line.replace(EXAMPLE_MARKER, "")
|
||||
.replace(EXAMPLE_NEW_LINE, "\r\n")
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
self.examples = examples;
|
||||
self.example_index = None;
|
||||
}
|
||||
|
||||
/// Creates default string that represents one suggestion from the menu
|
||||
fn create_entry_string(
|
||||
&self,
|
||||
suggestion: &Suggestion,
|
||||
index: usize,
|
||||
column: u16,
|
||||
empty_space: usize,
|
||||
use_ansi_coloring: bool,
|
||||
) -> String {
|
||||
if use_ansi_coloring {
|
||||
if index == self.index() {
|
||||
format!(
|
||||
"{}{}{:>empty$}{}{}",
|
||||
self.color.selected_text_style.prefix(),
|
||||
&suggestion.value,
|
||||
"",
|
||||
RESET,
|
||||
self.end_of_line(column, index),
|
||||
empty = empty_space,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}{}{:>empty$}{}{}",
|
||||
self.color.text_style.prefix(),
|
||||
&suggestion.value,
|
||||
"",
|
||||
RESET,
|
||||
self.end_of_line(column, index),
|
||||
empty = empty_space,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// If no ansi coloring is found, then the selection word is
|
||||
// the line in uppercase
|
||||
let (marker, empty_space) = if index == self.index() {
|
||||
(">", empty_space.saturating_sub(1))
|
||||
} else {
|
||||
("", empty_space)
|
||||
};
|
||||
|
||||
let line = format!(
|
||||
"{}{}{:>empty$}{}",
|
||||
marker,
|
||||
&suggestion.value,
|
||||
"",
|
||||
self.end_of_line(column, index),
|
||||
empty = empty_space,
|
||||
);
|
||||
|
||||
if index == self.index() {
|
||||
line.to_uppercase()
|
||||
} else {
|
||||
line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Description string with color
|
||||
fn create_description_string(&self, use_ansi_coloring: bool) -> String {
|
||||
let description = self
|
||||
.get_value()
|
||||
.and_then(|suggestion| suggestion.description)
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.lines()
|
||||
.filter(|line| !line.starts_with(EXAMPLE_MARKER))
|
||||
.skip(self.skipped_rows)
|
||||
.take(self.working_details.description_rows)
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\r\n");
|
||||
|
||||
if use_ansi_coloring && !description.is_empty() {
|
||||
format!(
|
||||
"{}{}{}",
|
||||
self.color.description_style.prefix(),
|
||||
description,
|
||||
RESET,
|
||||
)
|
||||
} else {
|
||||
description
|
||||
}
|
||||
}
|
||||
|
||||
/// Selectable list of examples from the actual value
|
||||
fn create_example_string(&self, use_ansi_coloring: bool) -> String {
|
||||
if !self.show_examples {
|
||||
return "".into();
|
||||
}
|
||||
|
||||
let examples: String = self
|
||||
.examples
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, example)| {
|
||||
if let Some(example_index) = self.example_index {
|
||||
if index == example_index {
|
||||
format!(
|
||||
" {}{}{}\r\n",
|
||||
self.color.selected_text_style.prefix(),
|
||||
example,
|
||||
RESET
|
||||
)
|
||||
} else {
|
||||
format!(" {}\r\n", example)
|
||||
}
|
||||
} else {
|
||||
format!(" {}\r\n", example)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if examples.is_empty() {
|
||||
"".into()
|
||||
} else if use_ansi_coloring {
|
||||
format!(
|
||||
"{}\r\n\r\nExamples:\r\n{}{}",
|
||||
self.color.description_style.prefix(),
|
||||
RESET,
|
||||
examples,
|
||||
)
|
||||
} else {
|
||||
format!("\r\n\r\nExamples:\r\n{}", examples,)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Menu for NuHelpMenu {
|
||||
/// Menu name
|
||||
fn name(&self) -> &str {
|
||||
"help_menu"
|
||||
}
|
||||
|
||||
/// Menu indicator
|
||||
fn indicator(&self) -> &str {
|
||||
self.marker.as_str()
|
||||
}
|
||||
|
||||
/// Deactivates context menu
|
||||
fn is_active(&self) -> bool {
|
||||
self.active
|
||||
}
|
||||
|
||||
/// The help menu stays active even with one record
|
||||
fn can_quick_complete(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// The help menu does not need to partially complete
|
||||
fn can_partially_complete(
|
||||
&mut self,
|
||||
_values_updated: bool,
|
||||
_line_buffer: &mut LineBuffer,
|
||||
_history: &dyn History,
|
||||
_completer: &dyn Completer,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Selects what type of event happened with the menu
|
||||
fn menu_event(&mut self, event: MenuEvent) {
|
||||
match &event {
|
||||
MenuEvent::Activate(_) => self.active = true,
|
||||
MenuEvent::Deactivate => {
|
||||
self.active = false;
|
||||
self.input = None;
|
||||
self.values = Vec::new();
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
self.event = Some(event);
|
||||
}
|
||||
|
||||
/// Updates menu values
|
||||
fn update_values(
|
||||
&mut self,
|
||||
line_buffer: &mut LineBuffer,
|
||||
_history: &dyn History,
|
||||
completer: &dyn Completer,
|
||||
) {
|
||||
if let Some(old_string) = &self.input {
|
||||
let (start, input) = string_difference(line_buffer.get_buffer(), old_string);
|
||||
if !input.is_empty() {
|
||||
self.reset_position();
|
||||
self.values = completer
|
||||
.complete(input, line_buffer.insertion_point())
|
||||
.into_iter()
|
||||
.map(|suggestion| Suggestion {
|
||||
value: suggestion.value,
|
||||
description: suggestion.description,
|
||||
span: reedline::Span {
|
||||
start,
|
||||
end: start + input.len(),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The working details for the menu changes based on the size of the lines
|
||||
/// collected from the completer
|
||||
fn update_working_details(
|
||||
&mut self,
|
||||
line_buffer: &mut LineBuffer,
|
||||
history: &dyn History,
|
||||
completer: &dyn Completer,
|
||||
painter: &Painter,
|
||||
) {
|
||||
if let Some(event) = self.event.take() {
|
||||
// Updating all working parameters from the menu before executing any of the
|
||||
// possible event
|
||||
let max_width = self.get_values().iter().fold(0, |acc, suggestion| {
|
||||
let str_len = suggestion.value.len() + self.default_details.col_padding;
|
||||
if str_len > acc {
|
||||
str_len
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
});
|
||||
|
||||
// If no default width is found, then the total screen width is used to estimate
|
||||
// the column width based on the default number of columns
|
||||
let default_width = if let Some(col_width) = self.default_details.col_width {
|
||||
col_width
|
||||
} else {
|
||||
let col_width = painter.screen_width() / self.default_details.columns;
|
||||
col_width as usize
|
||||
};
|
||||
|
||||
// Adjusting the working width of the column based the max line width found
|
||||
// in the menu values
|
||||
if max_width > default_width {
|
||||
self.working_details.col_width = max_width;
|
||||
} else {
|
||||
self.working_details.col_width = default_width;
|
||||
};
|
||||
|
||||
// The working columns is adjusted based on possible number of columns
|
||||
// that could be fitted in the screen with the calculated column width
|
||||
let possible_cols = painter.screen_width() / self.working_details.col_width as u16;
|
||||
if possible_cols > self.default_details.columns {
|
||||
self.working_details.columns = self.default_details.columns.max(1);
|
||||
} else {
|
||||
self.working_details.columns = possible_cols;
|
||||
}
|
||||
|
||||
// Updating the working rows to display the description
|
||||
if self.menu_required_lines(painter.screen_width()) <= painter.remaining_lines() {
|
||||
self.working_details.description_rows = self.default_details.description_rows;
|
||||
self.show_examples = true;
|
||||
} else {
|
||||
self.working_details.description_rows = painter
|
||||
.remaining_lines()
|
||||
.saturating_sub(self.default_details.selection_rows + 1)
|
||||
as usize;
|
||||
|
||||
self.show_examples = false;
|
||||
}
|
||||
|
||||
match event {
|
||||
MenuEvent::Activate(_) => {
|
||||
self.reset_position();
|
||||
self.input = Some(line_buffer.get_buffer().to_string());
|
||||
self.update_values(line_buffer, history, completer);
|
||||
}
|
||||
MenuEvent::Deactivate => self.active = false,
|
||||
MenuEvent::Edit(_) => {
|
||||
self.reset_position();
|
||||
self.update_values(line_buffer, history, completer);
|
||||
self.update_examples()
|
||||
}
|
||||
MenuEvent::NextElement => {
|
||||
self.skipped_rows = 0;
|
||||
self.move_next();
|
||||
self.update_examples();
|
||||
}
|
||||
MenuEvent::PreviousElement => {
|
||||
self.skipped_rows = 0;
|
||||
self.move_previous();
|
||||
self.update_examples();
|
||||
}
|
||||
MenuEvent::MoveUp => {
|
||||
if let Some(example_index) = self.example_index {
|
||||
if let Some(index) = example_index.checked_sub(1) {
|
||||
self.example_index = Some(index);
|
||||
} else {
|
||||
self.example_index = Some(self.examples.len().saturating_sub(1));
|
||||
}
|
||||
} else {
|
||||
self.example_index = Some(0);
|
||||
}
|
||||
}
|
||||
MenuEvent::MoveDown => {
|
||||
if let Some(example_index) = self.example_index {
|
||||
let index = example_index + 1;
|
||||
if index < self.examples.len() {
|
||||
self.example_index = Some(index);
|
||||
} else {
|
||||
self.example_index = Some(0);
|
||||
}
|
||||
} else {
|
||||
self.example_index = Some(0);
|
||||
}
|
||||
}
|
||||
MenuEvent::MoveLeft => self.skipped_rows = self.skipped_rows.saturating_sub(1),
|
||||
MenuEvent::MoveRight => {
|
||||
let skipped = self.skipped_rows + 1;
|
||||
let description_rows = self
|
||||
.get_value()
|
||||
.and_then(|suggestion| suggestion.description)
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.lines()
|
||||
.filter(|line| !line.starts_with(EXAMPLE_MARKER))
|
||||
.count();
|
||||
|
||||
let allowed_skips =
|
||||
description_rows.saturating_sub(self.working_details.description_rows);
|
||||
|
||||
if skipped < allowed_skips {
|
||||
self.skipped_rows = skipped;
|
||||
} else {
|
||||
self.skipped_rows = allowed_skips;
|
||||
}
|
||||
}
|
||||
MenuEvent::PreviousPage | MenuEvent::NextPage => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The buffer gets replaced in the Span location
|
||||
fn replace_in_buffer(&self, line_buffer: &mut LineBuffer) {
|
||||
if let Some(Suggestion { value, span, .. }) = self.get_value() {
|
||||
let string_len = if let Some(example_index) = self.example_index {
|
||||
let example = self
|
||||
.examples
|
||||
.get(example_index)
|
||||
.expect("the example index is always checked");
|
||||
line_buffer.replace(span.start..span.end, example);
|
||||
example.len()
|
||||
} else {
|
||||
line_buffer.replace(span.start..span.end, &value);
|
||||
value.len()
|
||||
};
|
||||
|
||||
let mut offset = line_buffer.insertion_point();
|
||||
offset += string_len.saturating_sub(span.end - span.start);
|
||||
line_buffer.set_insertion_point(offset);
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimum rows that should be displayed by the menu
|
||||
fn min_rows(&self) -> u16 {
|
||||
self.get_rows().min(self.min_rows)
|
||||
}
|
||||
|
||||
/// Gets values from filler that will be displayed in the menu
|
||||
fn get_values(&self) -> &[Suggestion] {
|
||||
&self.values
|
||||
}
|
||||
|
||||
fn menu_required_lines(&self, _terminal_columns: u16) -> u16 {
|
||||
let example_lines = self
|
||||
.examples
|
||||
.iter()
|
||||
.fold(0, |acc, example| example.lines().count() + acc);
|
||||
|
||||
self.default_details.selection_rows
|
||||
+ self.default_details.description_rows as u16
|
||||
+ example_lines as u16
|
||||
+ 3
|
||||
}
|
||||
|
||||
fn menu_string(&self, _available_lines: u16, use_ansi_coloring: bool) -> String {
|
||||
if self.get_values().is_empty() {
|
||||
self.no_records_msg(use_ansi_coloring)
|
||||
} else {
|
||||
// The skip values represent the number of lines that should be skipped
|
||||
// while printing the menu
|
||||
let available_lines = self.default_details.selection_rows;
|
||||
let skip_values = if self.row_pos >= available_lines {
|
||||
let skip_lines = self.row_pos.saturating_sub(available_lines) + 1;
|
||||
(skip_lines * self.get_cols()) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// It seems that crossterm prefers to have a complete string ready to be printed
|
||||
// rather than looping through the values and printing multiple things
|
||||
// This reduces the flickering when printing the menu
|
||||
let available_values = (available_lines * self.get_cols()) as usize;
|
||||
let selection_values: String = self
|
||||
.get_values()
|
||||
.iter()
|
||||
.skip(skip_values)
|
||||
.take(available_values)
|
||||
.enumerate()
|
||||
.map(|(index, suggestion)| {
|
||||
// Correcting the enumerate index based on the number of skipped values
|
||||
let index = index + skip_values;
|
||||
let column = index as u16 % self.get_cols();
|
||||
let empty_space = self.get_width().saturating_sub(suggestion.value.len());
|
||||
|
||||
self.create_entry_string(
|
||||
suggestion,
|
||||
index,
|
||||
column,
|
||||
empty_space,
|
||||
use_ansi_coloring,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
format!(
|
||||
"{}{}{}",
|
||||
selection_values,
|
||||
self.create_description_string(use_ansi_coloring),
|
||||
self.create_example_string(use_ansi_coloring)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ mod completions;
|
|||
mod config_files;
|
||||
mod errors;
|
||||
mod eval_file;
|
||||
mod help_completions;
|
||||
mod help_menu;
|
||||
mod nu_highlight;
|
||||
mod print;
|
||||
mod prompt;
|
||||
|
@ -18,6 +20,8 @@ pub use completions::NuCompleter;
|
|||
pub use config_files::eval_config_contents;
|
||||
pub use errors::CliError;
|
||||
pub use eval_file::evaluate_file;
|
||||
pub use help_completions::NuHelpCompleter;
|
||||
pub use help_menu::NuHelpMenu;
|
||||
pub use nu_highlight::NuHighlight;
|
||||
pub use print::Print;
|
||||
pub use prompt::NushellPrompt;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use super::NuHelpMenu;
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use nu_color_config::lookup_ansi_color_style;
|
||||
use nu_protocol::{extract_value, Config, ParsedKeybinding, ShellError, Span, Value};
|
||||
use reedline::{
|
||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
CompletionMenu, EditCommand, HistoryMenu, Keybindings, Reedline, ReedlineEvent,
|
||||
Completer, CompletionMenu, EditCommand, HistoryMenu, Keybindings, Reedline, ReedlineEvent,
|
||||
};
|
||||
|
||||
// Creates an input object for the completion menu based on the dictionary
|
||||
|
@ -64,7 +65,7 @@ pub(crate) fn add_completion_menu(line_editor: Reedline, config: &Config) -> Ree
|
|||
None => completion_menu,
|
||||
};
|
||||
|
||||
line_editor.with_menu(Box::new(completion_menu))
|
||||
line_editor.with_menu(Box::new(completion_menu), None)
|
||||
}
|
||||
|
||||
// Creates an input object for the history menu based on the dictionary
|
||||
|
@ -120,10 +121,119 @@ pub(crate) fn add_history_menu(line_editor: Reedline, config: &Config) -> Reedli
|
|||
None => history_menu,
|
||||
};
|
||||
|
||||
line_editor.with_menu(Box::new(history_menu))
|
||||
line_editor.with_menu(Box::new(history_menu), None)
|
||||
}
|
||||
|
||||
// Creates an input object for the help menu based on the dictionary
|
||||
// stored in the config variable
|
||||
pub(crate) fn add_help_menu(
|
||||
line_editor: Reedline,
|
||||
help_completer: Box<dyn Completer>,
|
||||
config: &Config,
|
||||
) -> Reedline {
|
||||
let mut help_menu = NuHelpMenu::default();
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("columns")
|
||||
.and_then(|value| value.as_integer().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_columns(value as u16),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = help_menu.with_column_width(
|
||||
config
|
||||
.help_config
|
||||
.get("col_width")
|
||||
.and_then(|value| value.as_integer().ok())
|
||||
.map(|value| value as usize),
|
||||
);
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("col_padding")
|
||||
.and_then(|value| value.as_integer().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_column_padding(value as usize),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("selection_rows")
|
||||
.and_then(|value| value.as_integer().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_selection_rows(value as u16),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("description_rows")
|
||||
.and_then(|value| value.as_integer().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_description_rows(value as usize),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("text_style")
|
||||
.and_then(|value| value.as_string().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_text_style(lookup_ansi_color_style(&value)),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("selected_text_style")
|
||||
.and_then(|value| value.as_string().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_selected_text_style(lookup_ansi_color_style(&value)),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("description_text_style")
|
||||
.and_then(|value| value.as_string().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_description_text_style(lookup_ansi_color_style(&value)),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
help_menu = match config
|
||||
.help_config
|
||||
.get("marker")
|
||||
.and_then(|value| value.as_string().ok())
|
||||
{
|
||||
Some(value) => help_menu.with_marker(value),
|
||||
None => help_menu,
|
||||
};
|
||||
|
||||
line_editor.with_menu(Box::new(help_menu), Some(help_completer))
|
||||
}
|
||||
|
||||
fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
||||
// Completer menu keybindings
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::NONE,
|
||||
KeyCode::Tab,
|
||||
ReedlineEvent::UntilFound(vec![
|
||||
ReedlineEvent::Menu("completer_menu".to_string()),
|
||||
ReedlineEvent::MenuNext,
|
||||
]),
|
||||
);
|
||||
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::SHIFT,
|
||||
KeyCode::BackTab,
|
||||
ReedlineEvent::MenuPrevious,
|
||||
);
|
||||
|
||||
// History menu keybinding
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::CONTROL,
|
||||
KeyCode::Char('x'),
|
||||
|
@ -142,19 +252,11 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
|||
]),
|
||||
);
|
||||
|
||||
// Help menu keybinding
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::NONE,
|
||||
KeyCode::Tab,
|
||||
ReedlineEvent::UntilFound(vec![
|
||||
ReedlineEvent::Menu("completion_menu".to_string()),
|
||||
ReedlineEvent::MenuNext,
|
||||
]),
|
||||
);
|
||||
|
||||
keybindings.add_binding(
|
||||
KeyModifiers::SHIFT,
|
||||
KeyCode::BackTab,
|
||||
ReedlineEvent::MenuPrevious,
|
||||
KeyModifiers::CONTROL,
|
||||
KeyCode::Char('i'),
|
||||
ReedlineEvent::Menu("help_menu".to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::reedline_config::{add_completion_menu, add_history_menu};
|
||||
use crate::{prompt_update, reedline_config};
|
||||
use crate::reedline_config::{add_completion_menu, add_help_menu, add_history_menu};
|
||||
use crate::{prompt_update, reedline_config, NuHelpCompleter};
|
||||
use crate::{
|
||||
reedline_config::KeybindingsMode,
|
||||
util::{eval_source, report_error},
|
||||
|
@ -160,6 +160,9 @@ pub fn evaluate_repl(
|
|||
line_editor = add_completion_menu(line_editor, &config);
|
||||
line_editor = add_history_menu(line_editor, &config);
|
||||
|
||||
let help_completer = Box::new(NuHelpCompleter::new(engine_state.clone()));
|
||||
line_editor = add_help_menu(line_editor, help_completer, &config);
|
||||
|
||||
if is_perf_true {
|
||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ version = "0.60.1"
|
|||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.60.1" }
|
||||
nu-ansi-term = "0.45.0"
|
||||
nu-ansi-term = "0.45.1"
|
||||
nu-json = { path = "../nu-json", version = "0.60.1" }
|
||||
nu-table = { path = "../nu-table", version = "0.60.1" }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
|
|
|
@ -23,7 +23,7 @@ nu-table = { path = "../nu-table", version = "0.60.1" }
|
|||
nu-term-grid = { path = "../nu-term-grid", version = "0.60.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.60.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.60.1" }
|
||||
nu-ansi-term = "0.45.0"
|
||||
nu-ansi-term = "0.45.1"
|
||||
|
||||
# Potential dependencies for extras
|
||||
base64 = "0.13.0"
|
||||
|
@ -77,7 +77,8 @@ unicode-segmentation = "1.8.0"
|
|||
url = "2.2.1"
|
||||
uuid = { version = "0.8.2", features = ["v4"] }
|
||||
which = { version = "4.2.2", optional = true }
|
||||
reedline = { git = "https://github.com/nushell/reedline" }
|
||||
#reedline = "0.3.0"
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
zip = { version="0.5.9", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
|
|
@ -43,7 +43,7 @@ impl Command for UpdateCells {
|
|||
example: r#"[
|
||||
["2021-04-16", "2021-06-10", "2021-09-18", "2021-10-15", "2021-11-16", "2021-11-17", "2021-11-18"];
|
||||
[ 37, 0, 0, 0, 37, 0, 0]
|
||||
] | update cells {|value|
|
||||
] | update cells { |value|
|
||||
if $value == 0 {
|
||||
""
|
||||
} else {
|
||||
|
@ -80,7 +80,7 @@ impl Command for UpdateCells {
|
|||
example: r#"[
|
||||
["2021-04-16", "2021-06-10", "2021-09-18", "2021-10-15", "2021-11-16", "2021-11-17", "2021-11-18"];
|
||||
[ 37, 0, 0, 0, 37, 0, 0]
|
||||
] | update cells -c ["2021-11-18", "2021-11-17"] {|value|
|
||||
] | update cells -c ["2021-11-18", "2021-11-17"] { |value|
|
||||
if $value == 0 {
|
||||
""
|
||||
} else {
|
||||
|
|
|
@ -33,6 +33,7 @@ pub struct Config {
|
|||
pub menu_config: HashMap<String, Value>,
|
||||
pub keybindings: Vec<ParsedKeybinding>,
|
||||
pub history_config: HashMap<String, Value>,
|
||||
pub help_config: HashMap<String, Value>,
|
||||
pub rm_always_trash: bool,
|
||||
}
|
||||
|
||||
|
@ -55,8 +56,9 @@ impl Default for Config {
|
|||
max_history_size: 1000,
|
||||
log_level: String::new(),
|
||||
menu_config: HashMap::new(),
|
||||
keybindings: Vec::new(),
|
||||
history_config: HashMap::new(),
|
||||
help_config: HashMap::new(),
|
||||
keybindings: Vec::new(),
|
||||
rm_always_trash: false,
|
||||
}
|
||||
}
|
||||
|
@ -211,13 +213,6 @@ impl Value {
|
|||
eprintln!("$config.menu_config is not a record")
|
||||
}
|
||||
}
|
||||
"keybindings" => {
|
||||
if let Ok(keybindings) = create_keybindings(value, &config) {
|
||||
config.keybindings = keybindings;
|
||||
} else {
|
||||
eprintln!("$config.keybindings is not a valid keybindings list")
|
||||
}
|
||||
}
|
||||
"history_config" => {
|
||||
if let Ok(map) = create_map(value, &config) {
|
||||
config.history_config = map;
|
||||
|
@ -225,6 +220,20 @@ impl Value {
|
|||
eprintln!("$config.history_config is not a record")
|
||||
}
|
||||
}
|
||||
"help_config" => {
|
||||
if let Ok(map) = create_map(value, &config) {
|
||||
config.help_config = map;
|
||||
} else {
|
||||
eprintln!("$config.help_config is not a record")
|
||||
}
|
||||
}
|
||||
"keybindings" => {
|
||||
if let Ok(keybindings) = create_keybindings(value, &config) {
|
||||
config.keybindings = keybindings;
|
||||
} else {
|
||||
eprintln!("$config.keybindings is not a valid keybindings list")
|
||||
}
|
||||
}
|
||||
x => {
|
||||
eprintln!("$config.{} is an unknown config setting", x)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ name = "table"
|
|||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
nu-ansi-term = "0.45.0"
|
||||
nu-ansi-term = "0.45.1"
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.60.1" }
|
||||
regex = "1.4"
|
||||
unicode-width = "0.1.8"
|
||||
|
|
|
@ -212,6 +212,17 @@ let $config = {
|
|||
selected_text_style: green_reverse
|
||||
marker: "? "
|
||||
}
|
||||
help_config: {
|
||||
columns: 4
|
||||
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
|
||||
col_padding: 2
|
||||
selection_rows: 4
|
||||
description_rows: 10
|
||||
text_style: green
|
||||
selected_text_style: green_reverse
|
||||
description_text_style: yellow
|
||||
marker: "% "
|
||||
}
|
||||
keybindings: [
|
||||
{
|
||||
name: completion_menu
|
||||
|
|
Loading…
Reference in a new issue