Merge main into sort-custom

This commit is contained in:
132ikl 2024-07-30 14:18:40 -04:00
commit ec2e06c5a3
590 changed files with 15683 additions and 5048 deletions

View File

@ -26,6 +26,13 @@ updates:
patterns:
- "polars"
- "polars-*"
# uutils/coreutils also versions all their workspace crates the same at the moment
# Most of them have bleeding edge version requirements (some not)
# see: https://github.com/uutils/coreutils/blob/main/Cargo.toml
uutils:
patterns:
- "uucore"
- "uu_*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:

View File

@ -99,13 +99,13 @@ jobs:
extra: msi
os: windows-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
@ -161,7 +161,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release
# Create a release only in nushell/nightly repo
- name: Publish Archive
uses: softprops/action-gh-release@v2.0.6
uses: softprops/action-gh-release@v2.0.8
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
with:
prerelease: true

View File

@ -161,8 +161,12 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
let releaseStem = $'($bin)-($version)-($target)'
print $'(char nl)Download less related stuffs...'; hr-line
# todo: less-v661 is out but is released as a zip file. maybe we should switch to that and extract it?
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
# the below was renamed because it was failing to download for darren. it should work but it wasn't
# todo: maybe we should get rid of this aria2c dependency and just use http get?
#aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
aria2c https://github.com/jftuga/less-Windows/blob/master/LICENSE -o LICENSE-for-less.txt
# Create Windows msi release package
if (get-env _EXTRA_) == 'msi' {

View File

@ -49,13 +49,13 @@ jobs:
extra: msi
os: windows-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
@ -91,7 +91,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive
uses: softprops/action-gh-release@v2.0.6
uses: softprops/action-gh-release@v2.0.8
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
draft: true

View File

@ -10,4 +10,4 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Check spelling
uses: crate-ci/typos@v1.22.9
uses: crate-ci/typos@v1.23.3

189
Cargo.lock generated
View File

@ -1219,24 +1219,24 @@ dependencies = [
]
[[package]]
name = "dirs-next"
version = "2.0.0"
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"cfg-if",
"dirs-sys-next",
"dirs-sys",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"winapi",
"windows-sys 0.48.0",
]
[[package]]
@ -2342,9 +2342,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.154"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libflate"
@ -2382,9 +2382,9 @@ dependencies = [
[[package]]
name = "libloading"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
"windows-targets 0.52.5",
@ -2868,12 +2868,12 @@ dependencies = [
[[package]]
name = "nu"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"assert_cmd",
"crossterm",
"ctrlc",
"dirs-next",
"dirs",
"log",
"miette",
"mimalloc",
@ -2900,6 +2900,7 @@ dependencies = [
"openssl",
"pretty_assertions",
"reedline",
"regex",
"rstest",
"serde_json",
"serial_test",
@ -2912,16 +2913,16 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
version = "0.50.0"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
name = "nu-cli"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"chrono",
"crossterm",
@ -2956,7 +2957,7 @@ dependencies = [
[[package]]
name = "nu-cmd-base"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"indexmap",
"miette",
@ -2968,7 +2969,7 @@ dependencies = [
[[package]]
name = "nu-cmd-extra"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"fancy-regex",
"heck 0.5.0",
@ -2993,7 +2994,7 @@ dependencies = [
[[package]]
name = "nu-cmd-lang"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"itertools 0.12.1",
"nu-engine",
@ -3005,7 +3006,7 @@ dependencies = [
[[package]]
name = "nu-cmd-plugin"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"itertools 0.12.1",
"nu-engine",
@ -3016,7 +3017,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-ansi-term",
"nu-engine",
@ -3028,7 +3029,7 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"alphanumeric-sort",
"base64 0.22.1",
@ -3046,7 +3047,7 @@ dependencies = [
"deunicode",
"dialoguer",
"digest",
"dirs-next",
"dirs",
"dtparse",
"encoding_rs",
"fancy-regex",
@ -3138,7 +3139,7 @@ dependencies = [
[[package]]
name = "nu-derive-value"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"convert_case",
"proc-macro-error",
@ -3149,8 +3150,9 @@ dependencies = [
[[package]]
name = "nu-engine"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"log",
"nu-glob",
"nu-path",
"nu-protocol",
@ -3159,7 +3161,7 @@ dependencies = [
[[package]]
name = "nu-explore"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"ansi-str",
"anyhow",
@ -3184,16 +3186,19 @@ dependencies = [
[[package]]
name = "nu-glob"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"doc-comment",
]
[[package]]
name = "nu-json"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"fancy-regex",
"linked-hash-map",
"nu-path",
"nu-test-support",
"num-traits",
"serde",
"serde_json",
@ -3201,7 +3206,7 @@ dependencies = [
[[package]]
name = "nu-lsp"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"assert-json-diff",
"crossbeam-channel",
@ -3222,7 +3227,7 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"bytesize",
"chrono",
@ -3238,16 +3243,16 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"dirs-next",
"dirs",
"omnipath",
"pwd",
]
[[package]]
name = "nu-plugin"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"log",
"nix",
@ -3255,6 +3260,7 @@ dependencies = [
"nu-plugin-core",
"nu-plugin-protocol",
"nu-protocol",
"nu-utils",
"serde",
"thiserror",
"typetag",
@ -3262,7 +3268,7 @@ dependencies = [
[[package]]
name = "nu-plugin-core"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"interprocess",
"log",
@ -3276,7 +3282,7 @@ dependencies = [
[[package]]
name = "nu-plugin-engine"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"log",
"nu-engine",
@ -3284,6 +3290,7 @@ dependencies = [
"nu-plugin-protocol",
"nu-protocol",
"nu-system",
"nu-utils",
"serde",
"typetag",
"windows 0.54.0",
@ -3291,7 +3298,7 @@ dependencies = [
[[package]]
name = "nu-plugin-protocol"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"bincode",
"nu-protocol",
@ -3303,7 +3310,7 @@ dependencies = [
[[package]]
name = "nu-plugin-test-support"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-ansi-term",
"nu-cmd-lang",
@ -3321,7 +3328,7 @@ dependencies = [
[[package]]
name = "nu-pretty-hex"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"heapless",
"nu-ansi-term",
@ -3330,15 +3337,18 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"brotli",
"byte-unit",
"chrono",
"chrono-humanize",
"convert_case",
"dirs",
"dirs-sys",
"fancy-regex",
"indexmap",
"log",
"lru",
"miette",
"nix",
@ -3359,11 +3369,12 @@ dependencies = [
"tempfile",
"thiserror",
"typetag",
"windows-sys 0.48.0",
]
[[package]]
name = "nu-std"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"log",
"miette",
@ -3374,7 +3385,7 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"chrono",
"itertools 0.12.1",
@ -3392,7 +3403,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"fancy-regex",
"nu-ansi-term",
@ -3406,7 +3417,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-utils",
"unicode-width",
@ -3414,7 +3425,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-glob",
"nu-path",
@ -3426,7 +3437,7 @@ dependencies = [
[[package]]
name = "nu-utils"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"crossterm_winapi",
"log",
@ -3452,7 +3463,7 @@ dependencies = [
[[package]]
name = "nu_plugin_example"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-cmd-lang",
"nu-plugin",
@ -3462,7 +3473,7 @@ dependencies = [
[[package]]
name = "nu_plugin_formats"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"eml-parser",
"ical",
@ -3475,7 +3486,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"git2",
"nu-plugin",
@ -3484,7 +3495,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -3493,7 +3504,7 @@ dependencies = [
[[package]]
name = "nu_plugin_polars"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"chrono",
"chrono-tz 0.9.0",
@ -3527,7 +3538,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"gjson",
"nu-plugin",
@ -3542,7 +3553,7 @@ dependencies = [
[[package]]
name = "nu_plugin_stress_internals"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"interprocess",
"serde",
@ -3668,7 +3679,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "nuon"
version = "0.95.1"
version = "0.96.2"
dependencies = [
"chrono",
"fancy-regex",
@ -3765,9 +3776,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "open"
version = "5.1.2"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32"
checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3"
dependencies = [
"is-wsl",
"libc",
@ -3776,9 +3787,9 @@ dependencies = [
[[package]]
name = "openssl"
version = "0.10.64"
version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.5.0",
"cfg-if",
@ -3817,9 +3828,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
version = "0.9.102"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
@ -3828,6 +3839,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-multimap"
version = "0.7.3"
@ -4987,9 +5004,9 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.32.0"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf59e4c97b5049ba96b052cdb652368305a2eddcbce9bf1c16f9d003139eeea"
checksum = "2f8c676a3f3814a23c6a0fc9dff6b6c35b2e04df8134aae6f3929cc34de21a53"
dependencies = [
"arboard",
"chrono",
@ -5203,9 +5220,9 @@ dependencies = [
[[package]]
name = "rust-embed"
version = "8.4.0"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@ -5572,9 +5589,9 @@ dependencies = [
[[package]]
name = "shadow-rs"
version = "0.29.0"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a600f795d0894cda22235b44eea4b85c2a35b405f65523645ac8e35b306817a"
checksum = "d253e54681d4be0161e965db57974ae642a0b6aaeb18a999424c4dab062be8c5"
dependencies = [
"const_format",
"is_debug",
@ -6466,9 +6483,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "ureq"
version = "2.9.7"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
dependencies = [
"base64 0.22.1",
"encoding_rs",
@ -6513,9 +6530,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uu_cp"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31fc5c95f7668999e129464a29e9080f69ba01ccf7a0ae43ff2cfdb15baa340"
checksum = "6fb99d355ccb02e8c514e4a1d93e4aa4eedea9837de24635dfd24c165971444e"
dependencies = [
"clap",
"filetime",
@ -6529,9 +6546,9 @@ dependencies = [
[[package]]
name = "uu_mkdir"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496d95e0e3121e4d424ba62019eb84a6f1102213ca8ca16c0a2f8c652c7236c3"
checksum = "219588fbc146f18188781208ac4034616c51cf151677b4e1f9caf63ca8a7f2cf"
dependencies = [
"clap",
"uucore",
@ -6539,9 +6556,9 @@ dependencies = [
[[package]]
name = "uu_mktemp"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28a0d9744bdc28ceaf13f70b959bacded91aedfd008402d72fa1e3224158653"
checksum = "b1e79ad2c5911908fce23a6069c52ca82e1997e2ed4bf6abf2d867c79c3dc73f"
dependencies = [
"clap",
"rand",
@ -6551,9 +6568,9 @@ dependencies = [
[[package]]
name = "uu_mv"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53680908b01c5ac3cc0ee8a376de3e51a36dde2c5a5227a115a3d0977cc4539b"
checksum = "cd57c8d02f8a99ed56ed9f6fddab403ee0e2bf9e8f3a5ca8f0f9e4d6e3e392a0"
dependencies = [
"clap",
"fs_extra",
@ -6563,9 +6580,9 @@ dependencies = [
[[package]]
name = "uu_uname"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f4125fb4f286313bca8f222abaefe39db54d65179ea788c91ebd3162345f4e"
checksum = "ad1ca90f9b292bccaad0de70e6feccac5182c6713a5e1ca72d97bf3555b608b4"
dependencies = [
"clap",
"platform-info",
@ -6574,9 +6591,9 @@ dependencies = [
[[package]]
name = "uu_whoami"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7b313901a15cfde2d88f434fcd077903d690f73cc36d1cec20f47906960aec"
checksum = "bc7c52e42e0425710461700adc1063f468f2ba8a8ff83ee69ba661095ab7b77a"
dependencies = [
"clap",
"libc",
@ -6586,9 +6603,9 @@ dependencies = [
[[package]]
name = "uucore"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb2ea2f77699e5ff5c7e001af588ceb34cae8b5f9af5496bea5a6476aaa8e780"
checksum = "7b54aad02cf7e96f5fafabb6b836efa73eef934783b17530095a29ffd4fdc154"
dependencies = [
"clap",
"dunce",
@ -6625,9 +6642,9 @@ checksum = "425a23c7b7145bc7620c9c445817c37b1f78b6790aee9f208133f3c028975b60"
[[package]]
name = "uuid"
version = "1.9.1"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [
"getrandom",
"serde",

View File

@ -11,7 +11,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.77.2"
version = "0.95.1"
version = "0.96.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -83,7 +83,8 @@ ctrlc = "3.4"
deunicode = "1.6.0"
dialoguer = { default-features = false, version = "0.11" }
digest = { default-features = false, version = "0.10" }
dirs-next = "2.0"
dirs = "5.0"
dirs-sys = "0.4"
dtparse = "2.0"
encoding_rs = "0.8"
fancy-regex = "0.13"
@ -115,12 +116,12 @@ mockito = { version = "1.4", default-features = false }
native-tls = "0.2"
nix = { version = "0.28", default-features = false }
notify-debouncer-full = { version = "0.3", default-features = false }
nu-ansi-term = "0.50.0"
nu-ansi-term = "0.50.1"
num-format = "0.4"
num-traits = "0.2"
omnipath = "0.1"
once_cell = "1.18"
open = "5.1"
open = "5.3"
os_pipe = { version = "1.2", features = ["io_safety"] }
pathdiff = "0.2"
percent-encoding = "2"
@ -137,7 +138,7 @@ quote = "1.0"
rand = "0.8"
ratatui = "0.26"
rayon = "1.10"
reedline = "0.32.0"
reedline = "0.33.0"
regex = "1.9.5"
rmp = "0.8"
rmp-serde = "1.3"
@ -145,7 +146,7 @@ ropey = "1.6.1"
roxmltree = "0.19"
rstest = { version = "0.18", default-features = false }
rusqlite = "0.31"
rust-embed = "8.4.0"
rust-embed = "8.5.0"
same-file = "1.0"
serde = { version = "1.0", default-features = false }
serde_json = "1.0"
@ -164,45 +165,45 @@ trash = "3.3"
umask = "2.1"
unicode-segmentation = "1.11"
unicode-width = "0.1"
ureq = { version = "2.9", default-features = false }
ureq = { version = "2.10", default-features = false }
url = "2.2"
uu_cp = "0.0.26"
uu_mkdir = "0.0.26"
uu_mktemp = "0.0.26"
uu_mv = "0.0.26"
uu_whoami = "0.0.26"
uu_uname = "0.0.26"
uucore = "0.0.26"
uuid = "1.9.1"
uu_cp = "0.0.27"
uu_mkdir = "0.0.27"
uu_mktemp = "0.0.27"
uu_mv = "0.0.27"
uu_whoami = "0.0.27"
uu_uname = "0.0.27"
uucore = "0.0.27"
uuid = "1.10.0"
v_htmlescape = "0.15.0"
wax = "0.6"
which = "6.0.0"
windows = "0.54"
windows-sys = "0.48"
winreg = "0.52"
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.95.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.95.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.95.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.95.1", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.95.1" }
nu-command = { path = "./crates/nu-command", version = "0.95.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.95.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.95.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.95.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.95.1" }
nu-path = { path = "./crates/nu-path", version = "0.95.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.95.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.95.1" }
nu-std = { path = "./crates/nu-std", version = "0.95.1" }
nu-system = { path = "./crates/nu-system", version = "0.95.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.95.1" }
nu-cli = { path = "./crates/nu-cli", version = "0.96.2" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.96.2" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.96.2" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.96.2", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.96.2" }
nu-command = { path = "./crates/nu-command", version = "0.96.2" }
nu-engine = { path = "./crates/nu-engine", version = "0.96.2" }
nu-explore = { path = "./crates/nu-explore", version = "0.96.2" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.96.2" }
nu-parser = { path = "./crates/nu-parser", version = "0.96.2" }
nu-path = { path = "./crates/nu-path", version = "0.96.2" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.96.2" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.96.2" }
nu-std = { path = "./crates/nu-std", version = "0.96.2" }
nu-system = { path = "./crates/nu-system", version = "0.96.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.96.2" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true }
ctrlc = { workspace = true }
dirs-next = { workspace = true }
dirs = { workspace = true }
log = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
mimalloc = { version = "0.1.42", default-features = false, optional = true }
@ -226,13 +227,14 @@ nix = { workspace = true, default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.95.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.95.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.95.1" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.96.2" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.96.2" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.96.2" }
assert_cmd = "2.0"
dirs-next = { workspace = true }
dirs = { workspace = true }
tango-bench = "0.5"
pretty_assertions = { workspace = true }
regex = { workspace = true }
rstest = { workspace = true, default-features = false }
serial_test = "3.1"
tempfile = { workspace = true }
@ -303,7 +305,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# [patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}

29
SECURITY.md Normal file
View File

@ -0,0 +1,29 @@
# Security Policy
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
We want to fix those issues without exposing our users to unnecessary risk. Thus we want to explain our security policy.
Additional issues may be part of *safety* where the behavior of Nushell as designed and implemented can cause unintended harm or a bug causes damage without the involvement of a third party.
## Supported Versions
As Nushell is still under very active pre-stable development, the only version the core team prioritizes for security and safety fixes is the [most recent version as published on GitHub](https://github.com/nushell/nushell/releases/latest).
Only if you provide a strong reasoning and the necessary resources, will we consider blessing a backported fix with an official patch release for a previous version.
## Reporting a Vulnerability
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
Either reach out to the core team on [our Discord server](https://discord.gg/NtAbbGn) to arrange a private channel or use the [GitHub vulnerability reporting form](https://github.com/nushell/nushell/security/advisories/new).
Please try to answer the following questions:
- How can we reach you for further questions?
- What is the bug? Which system of Nushell may be affected?
- Do you have proof-of-concept for a potential exploit or have you observed an exploit in the wild?
- What is your assessment of the severity based on what could be impacted should the bug be exploited?
- Are additional people aware of the issue or deserve credit for identifying the issue?
We will try to get back to you within a week with:
- acknowledging the receipt of the report
- an initial plan of how we want to address this including the primary points of contact for further communication
- our preliminary assessment of how severe we judge the issue
- a proposal for how we can coordinate responsible disclosure (e.g. how we ship the bugfix, if we need to coordinate with distribution maintainers, when you can release a blog post if you want to etc.)
For purely *safety* related issues where the impact is severe by direct user action instead of malicious input or third parties, feel free to open a regular issue. If we deem that there may be an additional *security* risk on a *safety* issue we may continue discussions in a restricted forum.

View File

@ -4,11 +4,14 @@ use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, Span, Spanned, Value,
PipelineData, Signals, Span, Spanned, Value,
};
use nu_std::load_standard_library;
use nu_utils::{get_default_config, get_default_env};
use std::rc::Rc;
use std::{
rc::Rc,
sync::{atomic::AtomicBool, Arc},
};
use std::hint::black_box;
@ -42,6 +45,10 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
};
let mut stack = Stack::new();
// Support running benchmarks with IR mode
stack.use_ir = std::env::var_os("NU_USE_IR").is_some();
evaluate_commands(
&commands,
&mut engine,
@ -248,14 +255,12 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
)
}
fn bench_eval_interleave_with_ctrlc(n: i32) -> impl IntoBenchmarks {
fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
let mut engine = setup_engine();
engine.ctrlc = Some(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
false,
)));
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
let stack = Stack::new();
bench_command(
&format!("eval_interleave_with_ctrlc_{n}"),
&format!("eval_interleave_with_interrupt_{n}"),
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
stack,
engine,
@ -443,9 +448,9 @@ tango_benchmarks!(
bench_eval_interleave(100),
bench_eval_interleave(1_000),
bench_eval_interleave(10_000),
bench_eval_interleave_with_ctrlc(100),
bench_eval_interleave_with_ctrlc(1_000),
bench_eval_interleave_with_ctrlc(10_000),
bench_eval_interleave_with_interrupt(100),
bench_eval_interleave_with_interrupt(1_000),
bench_eval_interleave_with_interrupt(10_000),
// For
bench_eval_for(1),
bench_eval_for(10),

View File

@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.95.1"
version = "0.96.2"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" }
nu-command = { path = "../nu-command", version = "0.95.1" }
nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.2" }
nu-command = { path = "../nu-command", version = "0.96.2" }
nu-test-support = { path = "../nu-test-support", version = "0.96.2" }
rstest = { workspace = true, default-features = false }
tempfile = { workspace = true }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.95.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.95.1" }
nu-color-config = { path = "../nu-color-config", version = "0.95.1" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.2" }
nu-engine = { path = "../nu-engine", version = "0.96.2" }
nu-path = { path = "../nu-path", version = "0.96.2" }
nu-parser = { path = "../nu-parser", version = "0.96.2" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.96.2", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
nu-utils = { path = "../nu-utils", version = "0.96.2" }
nu-color-config = { path = "../nu-color-config", version = "0.96.2" }
nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }

7
crates/nu-cli/README.md Normal file
View File

@ -0,0 +1,7 @@
This crate implements the core functionality of the interactive Nushell REPL and interfaces with `reedline`.
Currently implements the syntax highlighting and completions logic.
Furthermore includes a few commands that are specific to `reedline`
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

@ -47,7 +47,7 @@ impl Command for History {
if let Some(config_path) = nu_path::config_dir() {
let clear = call.has_flag(engine_state, stack, "clear")?;
let long = call.has_flag(engine_state, stack, "long")?;
let ctrlc = engine_state.ctrlc.clone();
let signals = engine_state.signals().clone();
let mut history_path = config_path;
history_path.push("nushell");
@ -107,7 +107,7 @@ impl Command for History {
file: history_path.display().to_string(),
span: head,
})?
.into_pipeline_data(head, ctrlc)),
.into_pipeline_data(head, signals)),
HistoryFileFormat::Sqlite => Ok(history_reader
.and_then(|h| {
h.search(SearchQuery::everything(SearchDirection::Forward, None))
@ -122,7 +122,7 @@ impl Command for History {
file: history_path.display().to_string(),
span: head,
})?
.into_pipeline_data(head, ctrlc)),
.into_pipeline_data(head, signals)),
}
}
} else {

View File

@ -49,22 +49,26 @@ impl Command for KeybindingsList {
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let records = if call.named_len() == 0 {
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
all_options
.iter()
.flat_map(|argument| get_records(argument, call.head))
.collect()
} else {
call.named_iter()
.flat_map(|(argument, _, _)| get_records(argument.item.as_str(), call.head))
.collect()
};
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
let presence = all_options
.iter()
.map(|option| call.has_flag(engine_state, stack, option))
.collect::<Result<Vec<_>, ShellError>>()?;
let no_option_specified = presence.iter().all(|present| !*present);
let records = all_options
.iter()
.zip(presence)
.filter(|(_, present)| no_option_specified || *present)
.flat_map(|(option, _)| get_records(option, call.head))
.collect();
Ok(Value::list(records, call.head).into_pipeline_data())
}

View File

@ -1,13 +1,12 @@
use crate::completions::{CompletionOptions, SortBy};
use crate::completions::CompletionOptions;
use nu_protocol::{
engine::{Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use reedline::Suggestion;
// Completer trait represents the three stages of the completion
// fetch, filter and sort
pub trait Completer {
/// Fetch, filter, and sort completions
#[allow(clippy::too_many_arguments)]
fn fetch(
&mut self,
@ -19,32 +18,6 @@ pub trait Completer {
pos: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion>;
fn get_sort_by(&self) -> SortBy {
SortBy::Ascending
}
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
let mut filtered_items = items;
// Sort items
match self.get_sort_by() {
SortBy::LevenshteinDistance => {
filtered_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
filtered_items.sort_by(|a, b| a.suggestion.value.cmp(&b.suggestion.value));
}
SortBy::None => {}
};
filtered_items
}
}
#[derive(Debug, Default, PartialEq)]

View File

@ -9,7 +9,7 @@ use nu_protocol::{
};
use reedline::Suggestion;
use super::SemanticSuggestion;
use super::{completion_common::sort_suggestions, SemanticSuggestion};
pub struct CommandCompletion {
flattened: Vec<(Span, FlatShape)>,
@ -99,10 +99,9 @@ impl CommandCompletion {
suggestion: Suggestion {
value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
..Suggestion::default()
},
kind: Some(SuggestionKind::Command(x.2)),
})
@ -118,11 +117,9 @@ impl CommandCompletion {
.map(move |x| SemanticSuggestion {
suggestion: Suggestion {
value: x,
description: None,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
..Suggestion::default()
},
// TODO: is there a way to create a test?
kind: None,
@ -136,11 +133,9 @@ impl CommandCompletion {
results.push(SemanticSuggestion {
suggestion: Suggestion {
value: format!("^{}", external.suggestion.value),
description: None,
style: None,
extra: None,
span: external.suggestion.span,
append_whitespace: true,
..Suggestion::default()
},
kind: external.kind,
})
@ -161,7 +156,7 @@ impl Completer for CommandCompletion {
&mut self,
working_set: &StateWorkingSet,
_stack: &Stack,
_prefix: Vec<u8>,
prefix: Vec<u8>,
span: Span,
offset: usize,
pos: usize,
@ -198,7 +193,11 @@ impl Completer for CommandCompletion {
};
if !subcommands.is_empty() {
return subcommands;
return sort_suggestions(
&String::from_utf8_lossy(&prefix),
subcommands,
SortBy::LevenshteinDistance,
);
}
let config = working_set.get_config();
@ -223,11 +222,11 @@ impl Completer for CommandCompletion {
vec![]
};
subcommands.into_iter().chain(commands).collect::<Vec<_>>()
}
fn get_sort_by(&self) -> SortBy {
SortBy::LevenshteinDistance
sort_suggestions(
&String::from_utf8_lossy(&prefix),
commands,
SortBy::LevenshteinDistance,
)
}
}

View File

@ -51,8 +51,7 @@ impl NuCompleter {
..Default::default()
};
// Fetch
let mut suggestions = completer.fetch(
completer.fetch(
working_set,
&self.stack,
prefix.clone(),
@ -60,12 +59,7 @@ impl NuCompleter {
offset,
pos,
&options,
);
// Sort
suggestions = completer.sort(suggestions, prefix);
suggestions
)
}
fn external_completion(
@ -449,14 +443,11 @@ pub fn map_value_completions<'a>(
return Some(SemanticSuggestion {
suggestion: Suggestion {
value: s,
description: None,
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(x.get_type())),
});
@ -466,14 +457,11 @@ pub fn map_value_completions<'a>(
if let Ok(record) = x.as_record() {
let mut suggestion = Suggestion {
value: String::from(""), // Initialize with empty string
description: None,
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
..Suggestion::default()
};
// Iterate the cols looking for `value` and `description`

View File

@ -1,15 +1,18 @@
use crate::completions::{matches, CompletionOptions};
use crate::{
completions::{matches, CompletionOptions},
SemanticSuggestion,
};
use nu_ansi_term::Style;
use nu_engine::env_to_string;
use nu_path::{expand_to_real_path, home_dir};
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Span,
levenshtein_distance, Span,
};
use nu_utils::get_ls_colors;
use std::path::{
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
};
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
use super::SortBy;
#[derive(Clone, Default)]
pub struct PathBuiltFromString {
@ -45,6 +48,7 @@ fn complete_rec(
return completions;
};
let mut entries = Vec::new();
for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned();
let entry_isdir = entry.path().is_dir();
@ -53,20 +57,26 @@ fn complete_rec(
built.isdir = entry_isdir;
if !dir || entry_isdir {
match partial.split_first() {
Some((base, rest)) => {
if matches(base, &entry_name, options) {
if !rest.is_empty() || isdir {
completions
.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
} else {
completions.push(built);
}
entries.push((entry_name, built));
}
}
let prefix = partial.first().unwrap_or(&"");
let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
for (entry_name, built) in sorted_entries {
match partial.split_first() {
Some((base, rest)) => {
if matches(base, &entry_name, options) {
if !rest.is_empty() || isdir {
completions.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
} else {
completions.push(built);
}
}
None => {
completions.push(built);
}
}
None => {
completions.push(built);
}
}
}
@ -81,16 +91,16 @@ enum OriginalCwd {
}
impl OriginalCwd {
fn apply(&self, mut p: PathBuiltFromString) -> String {
fn apply(&self, mut p: PathBuiltFromString, path_separator: char) -> String {
match self {
Self::None => {}
Self::Home => p.parts.insert(0, "~".to_string()),
Self::Prefix(s) => p.parts.insert(0, s.clone()),
};
let mut ret = p.parts.join(MAIN_SEPARATOR_STR);
let mut ret = p.parts.join(&path_separator.to_string());
if p.isdir {
ret.push(SEP);
ret.push(path_separator);
}
ret
}
@ -121,6 +131,14 @@ pub fn complete_item(
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
let partial = surround_remove(partial);
let isdir = partial.ends_with(is_separator);
#[cfg(unix)]
let path_separator = SEP;
#[cfg(windows)]
let path_separator = partial
.chars()
.rfind(|c: &char| is_separator(*c))
.unwrap_or(SEP);
let cwd_pathbuf = Path::new(cwd).to_path_buf();
let ls_colors = (engine_state.config.use_ls_colors_completions
&& engine_state.config.use_ansi_coloring)
@ -183,7 +201,7 @@ pub fn complete_item(
)
.into_iter()
.map(|p| {
let path = original_cwd.apply(p);
let path = original_cwd.apply(p, path_separator);
let style = ls_colors.as_ref().map(|lsc| {
lsc.style_for_path_with_metadata(
&path,
@ -256,3 +274,38 @@ pub fn adjust_if_intermediate(
readjusted,
}
}
/// Convenience function to sort suggestions using [`sort_completions`]
pub fn sort_suggestions(
prefix: &str,
items: Vec<SemanticSuggestion>,
sort_by: SortBy,
) -> Vec<SemanticSuggestion> {
sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
}
/// # Arguments
/// * `prefix` - What the user's typed, for sorting by Levenshtein distance
pub fn sort_completions<T>(
prefix: &str,
mut items: Vec<T>,
sort_by: SortBy,
get_value: fn(&T) -> &str,
) -> Vec<T> {
// Sort items
match sort_by {
SortBy::LevenshteinDistance => {
items.sort_by(|a, b| {
let a_distance = levenshtein_distance(prefix, get_value(a));
let b_distance = levenshtein_distance(prefix, get_value(b));
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
SortBy::None => {}
};
items
}

View File

@ -12,6 +12,8 @@ use nu_protocol::{
use nu_utils::IgnoreCaseExt;
use std::collections::HashMap;
use super::completion_common::sort_suggestions;
pub struct CustomCompletion {
stack: Stack,
decl_id: usize,
@ -122,15 +124,12 @@ impl Completer for CustomCompletion {
})
.unwrap_or_default();
if let Some(custom_completion_options) = custom_completion_options {
let suggestions = if let Some(custom_completion_options) = custom_completion_options {
filter(&prefix, suggestions, &custom_completion_options)
} else {
filter(&prefix, suggestions, completion_options)
}
}
fn get_sort_by(&self) -> SortBy {
self.sort_by
};
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
}
}

View File

@ -1,14 +1,14 @@
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
Completer, CompletionOptions,
};
use nu_ansi_term::Style;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::path::Path;
use super::SemanticSuggestion;
@ -36,7 +36,7 @@ impl Completer for DirectoryCompletion {
// Filter only the folders
#[allow(deprecated)]
let output: Vec<_> = directory_completion(
let items: Vec<_> = directory_completion(
span,
&prefix,
&working_set.permanent_state.current_work_dir(),
@ -48,55 +48,23 @@ impl Completer for DirectoryCompletion {
.map(move |x| SemanticSuggestion {
suggestion: Suggestion {
value: x.1,
description: None,
style: x.2,
extra: None,
span: reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
append_whitespace: false,
..Suggestion::default()
},
// TODO????
kind: None,
})
.collect();
output
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() {
for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,4 @@
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy};
use crate::completions::{file_path_completion, Completer, CompletionOptions};
use nu_protocol::{
engine::{Stack, StateWorkingSet},
Span,
@ -6,7 +6,7 @@ use nu_protocol::{
use reedline::Suggestion;
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
use super::SemanticSuggestion;
use super::{completion_common::sort_suggestions, SemanticSuggestion, SortBy};
#[derive(Clone, Default)]
pub struct DotNuCompletion {}
@ -116,14 +116,13 @@ impl Completer for DotNuCompletion {
.map(move |x| SemanticSuggestion {
suggestion: Suggestion {
value: x.1,
description: None,
style: x.2,
extra: None,
span: reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
append_whitespace: true,
..Suggestion::default()
},
// TODO????
kind: None,
@ -131,10 +130,6 @@ impl Completer for DotNuCompletion {
})
.collect();
output
}
fn get_sort_by(&self) -> SortBy {
SortBy::LevenshteinDistance
sort_suggestions(&prefix_str, output, SortBy::Ascending)
}
}

View File

@ -1,15 +1,15 @@
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
Completer, CompletionOptions,
};
use nu_ansi_term::Style;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use nu_utils::IgnoreCaseExt;
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::path::Path;
use super::SemanticSuggestion;
@ -40,7 +40,7 @@ impl Completer for FileCompletion {
} = adjust_if_intermediate(&prefix, working_set, span);
#[allow(deprecated)]
let output: Vec<_> = complete_item(
let items: Vec<_> = complete_item(
readjusted,
span,
&prefix,
@ -53,55 +53,25 @@ impl Completer for FileCompletion {
.map(move |x| SemanticSuggestion {
suggestion: Suggestion {
value: x.1,
description: None,
style: x.2,
extra: None,
span: reedline::Span {
start: x.0.start - offset,
end: x.0.end - offset,
},
append_whitespace: false,
..Suggestion::default()
},
// TODO????
kind: None,
})
.collect();
output
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Sort results prioritizing the non hidden folders
// Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() {
for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,6 @@
use crate::completions::{Completer, CompletionOptions};
use crate::completions::{
completion_common::sort_suggestions, Completer, CompletionOptions, SortBy,
};
use nu_protocol::{
ast::{Expr, Expression},
engine::{Stack, StateWorkingSet},
@ -49,13 +51,12 @@ impl Completer for FlagCompletion {
suggestion: Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true,
..Suggestion::default()
},
// TODO????
kind: None,
@ -76,13 +77,12 @@ impl Completer for FlagCompletion {
suggestion: Suggestion {
value: String::from_utf8_lossy(&named).to_string(),
description: Some(flag_desc.to_string()),
style: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true,
..Suggestion::default()
},
// TODO????
kind: None,
@ -90,7 +90,7 @@ impl Completer for FlagCompletion {
}
}
return output;
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, SortBy::Ascending);
}
vec![]

View File

@ -9,6 +9,8 @@ use nu_protocol::{
use reedline::Suggestion;
use std::str;
use super::{completion_common::sort_suggestions, SortBy};
#[derive(Clone)]
pub struct VariableCompletion {
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
@ -40,6 +42,7 @@ impl Completer for VariableCompletion {
end: span.end - offset,
};
let sublevels_count = self.var_context.1.len();
let prefix_str = String::from_utf8_lossy(&prefix);
// Completions for the given variable
if !var_str.is_empty() {
@ -69,7 +72,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
} else {
// No nesting provided, return all env vars
@ -82,18 +85,15 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: env_var.0,
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
});
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
@ -117,7 +117,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
@ -139,7 +139,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
}
@ -154,11 +154,8 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: builtin.to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
// TODO is there a way to get the VarId to get the type???
kind: None,
@ -181,11 +178,8 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(),
@ -212,11 +206,8 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(),
@ -226,6 +217,8 @@ impl Completer for VariableCompletion {
}
}
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
output
@ -250,11 +243,8 @@ fn nested_suggestions(
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: col.clone(),
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
kind: Some(kind.clone()),
});
@ -267,11 +257,8 @@ fn nested_suggestions(
output.push(SemanticSuggestion {
suggestion: Suggestion {
value: column_name,
description: None,
style: None,
extra: None,
span: current_span,
append_whitespace: false,
..Suggestion::default()
},
kind: Some(kind.clone()),
});

View File

@ -53,9 +53,8 @@ pub fn evaluate_commands(
// Parse the source code
let (block, delta) = {
if let Some(ref t_mode) = table_mode {
let mut config = engine_state.get_config().clone();
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default();
engine_state.set_config(config);
Arc::make_mut(&mut engine_state.config).table_mode =
t_mode.coerce_str()?.parse().unwrap_or_default();
}
let mut working_set = StateWorkingSet::new(engine_state);
@ -70,6 +69,11 @@ pub fn evaluate_commands(
std::process::exit(1);
}
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render())
};

View File

@ -76,12 +76,21 @@ pub fn evaluate_file(
trace!("parsing file: {}", file_path_str);
let block = parse(&mut working_set, Some(file_path_str), &file, false);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
// If any parse errors were found, report the first error and exit.
if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err);
std::process::exit(1);
}
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
// Look for blocks whose name starts with "main" and replace it with the filename.
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
if block.signature.name == "main" {

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod commands;
mod completions;
mod config_files;

View File

@ -1,25 +1,31 @@
use nu_engine::documentation::get_flags_section;
use nu_protocol::{engine::EngineState, levenshtein_distance};
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
use nu_utils::IgnoreCaseExt;
use reedline::{Completer, Suggestion};
use std::{fmt::Write, sync::Arc};
pub struct NuHelpCompleter(Arc<EngineState>);
pub struct NuHelpCompleter {
engine_state: Arc<EngineState>,
config: Arc<Config>,
}
impl NuHelpCompleter {
pub fn new(engine_state: Arc<EngineState>) -> Self {
Self(engine_state)
pub fn new(engine_state: Arc<EngineState>, config: Arc<Config>) -> Self {
Self {
engine_state,
config,
}
}
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
let folded_line = line.to_folded_case();
let mut commands = self
.0
.engine_state
.get_decls_sorted(false)
.into_iter()
.filter_map(|(_, decl_id)| {
let decl = self.0.get_decl(decl_id);
let decl = self.engine_state.get_decl(decl_id);
(decl.name().to_folded_case().contains(&folded_line)
|| decl.usage().to_folded_case().contains(&folded_line)
|| decl
@ -54,9 +60,12 @@ impl NuHelpCompleter {
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(Some(&*self.0.clone()), &sig, |v| {
v.to_parsable_string(", ", &self.0.config)
}))
long_desc.push_str(&get_flags_section(
Some(&self.engine_state),
Some(&self.config),
&sig,
|v| v.to_parsable_string(", ", &self.config),
))
}
if !sig.required_positional.is_empty()
@ -71,7 +80,7 @@ impl NuHelpCompleter {
let opt_suffix = if let Some(value) = &positional.default_value {
format!(
" (optional, default: {})",
&value.to_parsable_string(", ", &self.0.config),
&value.to_parsable_string(", ", &self.config),
)
} else {
(" (optional)").to_string()
@ -101,13 +110,12 @@ impl NuHelpCompleter {
Suggestion {
value: decl.name().into(),
description: Some(long_desc),
style: None,
extra: Some(extra),
span: reedline::Span {
start: pos - line.len(),
end: pos,
},
append_whitespace: false,
..Suggestion::default()
}
})
.collect()
@ -138,7 +146,8 @@ mod test {
) {
let engine_state =
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
let mut completer = NuHelpCompleter::new(engine_state.into());
let config = engine_state.get_config().clone();
let mut completer = NuHelpCompleter::new(engine_state.into(), config);
let suggestions = completer.complete(line, end);
assert_eq!(

View File

@ -142,10 +142,9 @@ fn convert_to_suggestions(
vec![Suggestion {
value: text,
description,
style: None,
extra,
span,
append_whitespace: false,
..Suggestion::default()
}]
}
Value::List { vals, .. } => vals
@ -154,9 +153,6 @@ fn convert_to_suggestions(
.collect(),
_ => vec![Suggestion {
value: format!("Not a record: {value:?}"),
description: None,
style: None,
extra: None,
span: reedline::Span {
start: if only_buffer_difference {
pos - line.len()
@ -169,7 +165,7 @@ fn convert_to_suggestions(
line.len()
},
},
append_whitespace: false,
..Suggestion::default()
}],
}
}

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use nu_engine::command_prelude::*;
use reedline::{Highlighter, StyledText};
@ -32,14 +34,11 @@ impl Command for NuHighlight {
) -> Result<PipelineData, ShellError> {
let head = call.head;
let ctrlc = engine_state.ctrlc.clone();
let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone();
let signals = engine_state.signals();
let highlighter = crate::NuHighlighter {
engine_state,
stack: std::sync::Arc::new(stack.clone()),
config,
engine_state: Arc::new(engine_state.clone()),
stack: Arc::new(stack.clone()),
};
input.map(
@ -50,7 +49,7 @@ impl Command for NuHighlight {
}
Err(err) => Value::error(err, head),
},
ctrlc,
signals,
)
}

View File

@ -77,13 +77,19 @@ pub(crate) fn add_menus(
mut line_editor: Reedline,
engine_state_ref: Arc<EngineState>,
stack: &Stack,
config: &Config,
config: Arc<Config>,
) -> Result<Reedline, ShellError> {
//log::trace!("add_menus: config: {:#?}", &config);
line_editor = line_editor.clear_menus();
for menu in &config.menus {
line_editor = add_menu(line_editor, menu, engine_state_ref.clone(), stack, config)?
line_editor = add_menu(
line_editor,
menu,
engine_state_ref.clone(),
stack,
config.clone(),
)?
}
// Checking if the default menus have been added from the config file
@ -100,7 +106,7 @@ pub(crate) fn add_menus(
if !config
.menus
.iter()
.any(|menu| menu.name.to_expanded_string("", config) == name)
.any(|menu| menu.name.to_expanded_string("", &config) == name)
{
let (block, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state);
@ -137,7 +143,7 @@ pub(crate) fn add_menus(
&menu,
new_engine_state_ref.clone(),
stack,
config,
config.clone(),
)?;
}
}
@ -151,27 +157,27 @@ fn add_menu(
menu: &ParsedMenu,
engine_state: Arc<EngineState>,
stack: &Stack,
config: &Config,
config: Arc<Config>,
) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span();
if let Value::Record { val, .. } = &menu.menu_type {
let layout = extract_value("layout", val, span)?.to_expanded_string("", config);
let layout = extract_value("layout", val, span)?.to_expanded_string("", &config);
match layout.as_str() {
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config),
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, &config),
"list" => add_list_menu(line_editor, menu, engine_state, stack, config),
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
"description" => add_description_menu(line_editor, menu, engine_state, stack, config),
_ => Err(ShellError::UnsupportedConfigValue {
expected: "columnar, list, ide or description".to_string(),
value: menu.menu_type.to_abbreviated_string(config),
value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(),
}),
}
} else {
Err(ShellError::UnsupportedConfigValue {
expected: "only record type".to_string(),
value: menu.menu_type.to_abbreviated_string(config),
value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(),
})
}
@ -282,9 +288,9 @@ pub(crate) fn add_list_menu(
menu: &ParsedMenu,
engine_state: Arc<EngineState>,
stack: &Stack,
config: &Config,
config: Arc<Config>,
) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config);
let name = menu.name.to_expanded_string("", &config);
let mut list_menu = ListMenu::default().with_name(&name);
let span = menu.menu_type.span();
@ -311,7 +317,7 @@ pub(crate) fn add_list_menu(
}
}
let marker = menu.marker.to_expanded_string("", config);
let marker = menu.marker.to_expanded_string("", &config);
list_menu = list_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -337,7 +343,7 @@ pub(crate) fn add_list_menu(
}
_ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config),
value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(),
}),
}
@ -349,10 +355,10 @@ pub(crate) fn add_ide_menu(
menu: &ParsedMenu,
engine_state: Arc<EngineState>,
stack: &Stack,
config: &Config,
config: Arc<Config>,
) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span();
let name = menu.name.to_expanded_string("", config);
let name = menu.name.to_expanded_string("", &config);
let mut ide_menu = IdeMenu::default().with_name(&name);
if let Value::Record { val, .. } = &menu.menu_type {
@ -417,7 +423,7 @@ pub(crate) fn add_ide_menu(
} else {
return Err(ShellError::UnsupportedConfigValue {
expected: "bool or record".to_string(),
value: border.to_abbreviated_string(config),
value: border.to_abbreviated_string(&config),
span: border.span(),
});
}
@ -441,7 +447,7 @@ pub(crate) fn add_ide_menu(
_ => {
return Err(ShellError::UnsupportedConfigValue {
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
value: description_mode.to_abbreviated_string(config),
value: description_mode.to_abbreviated_string(&config),
span: description_mode.span(),
});
}
@ -509,7 +515,7 @@ pub(crate) fn add_ide_menu(
}
}
let marker = menu.marker.to_expanded_string("", config);
let marker = menu.marker.to_expanded_string("", &config);
ide_menu = ide_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -535,7 +541,7 @@ pub(crate) fn add_ide_menu(
}
_ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config),
value: menu.source.to_abbreviated_string(&config),
span,
}),
}
@ -547,9 +553,9 @@ pub(crate) fn add_description_menu(
menu: &ParsedMenu,
engine_state: Arc<EngineState>,
stack: &Stack,
config: &Config,
config: Arc<Config>,
) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config);
let name = menu.name.to_expanded_string("", &config);
let mut description_menu = DescriptionMenu::default().with_name(&name);
let span = menu.menu_type.span();
@ -608,7 +614,7 @@ pub(crate) fn add_description_menu(
}
}
let marker = menu.marker.to_expanded_string("", config);
let marker = menu.marker.to_expanded_string("", &config);
description_menu = description_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -617,7 +623,7 @@ pub(crate) fn add_description_menu(
let span = menu.source.span();
match &menu.source {
Value::Nothing { .. } => {
let completer = Box::new(NuHelpCompleter::new(engine_state));
let completer = Box::new(NuHelpCompleter::new(engine_state, config));
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(description_menu),
completer,
@ -638,7 +644,7 @@ pub(crate) fn add_description_menu(
}
_ => Err(ShellError::UnsupportedConfigValue {
expected: "closure or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config),
value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(),
}),
}

View File

@ -43,7 +43,7 @@ use std::{
io::{self, IsTerminal, Write},
panic::{catch_unwind, AssertUnwindSafe},
path::{Path, PathBuf},
sync::{atomic::Ordering, Arc},
sync::Arc,
time::{Duration, Instant},
};
use sysinfo::System;
@ -268,14 +268,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
report_error_new(engine_state, &err);
}
// Check whether $env.NU_USE_IR is set, so that the user can change it in the REPL
// Temporary while IR eval is optional
stack.use_ir = stack.has_env_var(engine_state, "NU_USE_IR");
perf!("merge env", start_time, use_color);
start_time = std::time::Instant::now();
// Reset the ctrl-c handler
if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst);
}
perf!("reset ctrlc", start_time, use_color);
engine_state.reset_signals();
perf!("reset signals", start_time, use_color);
start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user,
@ -297,7 +297,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("env-change hook", start_time, use_color);
let engine_reference = Arc::new(engine_state.clone());
let config = engine_state.get_config();
let config = stack.get_config(engine_state);
start_time = std::time::Instant::now();
// Find the configured cursor shapes for each mode
@ -323,7 +323,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
engine_state: engine_reference.clone(),
// STACK-REFERENCE 1
stack: stack_arc.clone(),
config: config.clone(),
}))
.with_validator(Box::new(NuValidator {
engine_state: engine_reference.clone(),
@ -336,6 +335,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring)
.with_cwd(Some(
engine_state
.cwd(None)
.map(|cwd| cwd.into_std_path_buf())
.unwrap_or_default()
.to_string_lossy()
.to_string(),
))
.with_cursor_config(cursor_config);
perf!("reedline builder", start_time, use_color);
@ -666,13 +673,14 @@ fn prepare_history_metadata(
line_editor: &mut Reedline,
) {
if !s.is_empty() && line_editor.has_last_command_context() {
#[allow(deprecated)]
let result = line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.map(str::to_string);
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
c.cwd = engine_state
.cwd(None)
.ok()
.map(|path| path.to_string_lossy().to_string());
c
})
.into_diagnostic();

View File

@ -6,7 +6,7 @@ use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::{
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
engine::{EngineState, Stack, StateWorkingSet},
Config, Span,
Span,
};
use reedline::{Highlighter, StyledText};
use std::sync::Arc;
@ -14,15 +14,14 @@ use std::sync::Arc;
pub struct NuHighlighter {
pub engine_state: Arc<EngineState>,
pub stack: Arc<Stack>,
pub config: Config,
}
impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line);
let highlight_resolved_externals =
self.engine_state.get_config().highlight_resolved_externals;
let config = self.stack.get_config(&self.engine_state);
let highlight_resolved_externals = config.highlight_resolved_externals;
let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false);
let (shapes, global_span_offset) = {
@ -88,7 +87,7 @@ impl Highlighter for NuHighlighter {
.to_string();
let mut add_colored_token = |shape: &FlatShape, text: String| {
output.push((get_shape_color(shape.as_str(), &self.config), text));
output.push((get_shape_color(shape.as_str(), &config), text));
};
match shape.1 {
@ -128,9 +127,9 @@ impl Highlighter for NuHighlighter {
let start = part.start - span.start;
let end = part.end - span.start;
let text = next_token[start..end].to_string();
let mut style = get_shape_color(shape.as_str(), &self.config);
let mut style = get_shape_color(shape.as_str(), &config);
if highlight {
style = get_matching_brackets_style(style, &self.config);
style = get_matching_brackets_style(style, &config);
}
output.push((style, text));
}
@ -430,6 +429,14 @@ fn find_matching_block_end_in_expr(
)
}),
Expr::Collect(_, expr) => find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
),
Expr::Block(block_id)
| Expr::Closure(block_id)
| Expr::RowCondition(block_id)

View File

@ -262,6 +262,11 @@ fn evaluate_source(
return Ok(Some(1));
}
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render())
};

View File

@ -0,0 +1,7 @@
use nu_test_support::nu;
#[test]
fn not_empty() {
let result = nu!("keybindings list | is-not-empty");
assert_eq!(result.out, "true");
}

View File

@ -1 +1,2 @@
mod keybindings_list;
mod nu_highlight;

View File

@ -11,7 +11,7 @@ use std::{
sync::Arc,
};
use support::{
completions_helpers::{new_partial_engine, new_quote_engine},
completions_helpers::{new_dotnu_engine, new_partial_engine, new_quote_engine},
file, folder, match_suggestions, new_engine,
};
@ -32,7 +32,6 @@ fn completer() -> NuCompleter {
fn completer_strings() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
def my-command [animal: string@animals] { print $animal }"#;
@ -85,8 +84,29 @@ fn custom_completer() -> NuCompleter {
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[fixture]
fn subcommand_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Use fuzzy matching, because subcommands are sorted by Levenshtein distance,
// and that's not very useful with prefix matching
let commands = r#"
$env.config.completions.algorithm = "fuzzy"
def foo [] {}
def "foo bar" [] {}
def "foo abaz" [] {}
def "foo aabrr" [] {}
def food [] {}
"#;
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[test]
fn variables_dollar_sign_with_varialblecompletion() {
fn variables_dollar_sign_with_variablecompletion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
@ -102,28 +122,28 @@ fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter
let suggestions = completer.complete("tst --", 6);
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
// dbg!(&expected, &suggestions);
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-c ", 4);
let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
@ -132,49 +152,48 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[test]
fn dotnu_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
let (_, _, engine, stack) = new_dotnu_engine();
// Instantiate a new completer
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let expected = vec![
"asdf.nu".into(),
"bar.nu".into(),
"bat.nu".into(),
"baz.nu".into(),
#[cfg(windows)]
"dir_module\\".into(),
#[cfg(not(windows))]
"dir_module/".into(),
"foo.nu".into(),
"spam.nu".into(),
"xyzzy.nu".into(),
];
// Test source completion
let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(&expected, &suggestions);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(&expected, &suggestions);
// Test overlay use completion
let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(&expected, &suggestions);
}
#[test]
@ -238,8 +257,22 @@ fn file_completions() {
folder(dir.join(".hidden_folder")),
];
#[cfg(windows)]
{
let separator = '/';
let target_dir = format!("cp {dir_str}{separator}");
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
let expected_slash_paths: Vec<String> = expected_paths
.iter()
.map(|s| s.replace('\\', "/"))
.collect();
match_suggestions(&expected_slash_paths, &slash_suggestions);
}
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completions for a file
let target_dir = format!("cp {}", folder(dir.join("another")));
@ -249,17 +282,91 @@ fn file_completions() {
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completions for hidden files
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
let target_dir = format!("ls {}{MAIN_SEPARATOR}.", folder(dir.join(".hidden_folder")));
let suggestions = completer.complete(&target_dir, target_dir.len());
let expected_paths: Vec<String> =
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))];
#[cfg(windows)]
{
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
let expected_slash: Vec<String> = expected_paths
.iter()
.map(|s| s.replace('\\', "/"))
.collect();
match_suggestions(&expected_slash, &slash_suggestions);
}
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
}
#[cfg(windows)]
#[test]
fn file_completions_with_mixed_separators() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_dotnu_engine();
// Instantiate a new completer
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
// Create Expected values
let expected_paths: Vec<String> = vec![
file(dir.join("lib-dir1").join("bar.nu")),
file(dir.join("lib-dir1").join("baz.nu")),
file(dir.join("lib-dir1").join("xyzzy.nu")),
];
let expecetd_slash_paths: Vec<String> = expected_paths
.iter()
.map(|s| s.replace(MAIN_SEPARATOR, "/"))
.collect();
let target_dir = format!("ls {dir_str}/lib-dir1/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
let target_dir = format!("cp {dir_str}\\lib-dir1/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}/lib-dir1\\/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}\\lib-dir1\\/");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expecetd_slash_paths, &suggestions);
let target_dir = format!("ls {dir_str}\\lib-dir1\\");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expected_paths, &suggestions);
let target_dir = format!("ls {dir_str}/lib-dir1\\");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expected_paths, &suggestions);
let target_dir = format!("ls {dir_str}/lib-dir1/\\");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expected_paths, &suggestions);
let target_dir = format!("ls {dir_str}\\lib-dir1/\\");
let suggestions = completer.complete(&target_dir, target_dir.len());
match_suggestions(&expected_paths, &suggestions);
}
#[test]
@ -276,13 +383,14 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("partial_a")),
folder(dir.join("partial_b")),
folder(dir.join("partial_c")),
folder(dir.join("partial")),
folder(dir.join("partial-a")),
folder(dir.join("partial-b")),
folder(dir.join("partial-c")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completions for the files whose name begin with "h"
// and are present under directories whose names begin with "pa"
@ -292,17 +400,18 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial_a").join("hello")),
file(dir.join("partial_a").join("hola")),
file(dir.join("partial_b").join("hello_b")),
file(dir.join("partial_b").join("hi_b")),
file(dir.join("partial_c").join("hello_c")),
file(dir.join("partial").join("hello.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial-a").join("hello")),
file(dir.join("partial-a").join("hola")),
file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completion for all files under directories whose names begin with "pa"
let dir_str = folder(dir.join("pa"));
@ -311,18 +420,19 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("anotherfile")),
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial_a").join("hello")),
file(dir.join("partial_a").join("hola")),
file(dir.join("partial_b").join("hello_b")),
file(dir.join("partial_b").join("hi_b")),
file(dir.join("partial_c").join("hello_c")),
file(dir.join("partial").join("hello.txt")),
file(dir.join("partial-a").join("anotherfile")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial-a").join("hello")),
file(dir.join("partial-a").join("hola")),
file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completion for a single file
let dir_str = file(dir.join("fi").join("so"));
@ -333,7 +443,7 @@ fn partial_completions() {
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completion where there is a sneaky `..` in the path
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
@ -343,19 +453,25 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(
dir.join("partial_a")
dir.join("partial")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial_b")
dir.join("partial-a")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial_c")
dir.join("partial-b")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial-c")
.join("..")
.join("final_partial")
.join("somefile"),
@ -363,35 +479,35 @@ fn partial_completions() {
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have"));
let file_str = file(dir.join("partial-a").join("have"));
let target_file = format!("rm {file_str}");
let suggestions = completer.complete(&target_file, target_file.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
// Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have_ext."));
let file_str = file(dir.join("partial-a").join("have_ext."));
let file_dir = format!("rm {file_str}");
let suggestions = completer.complete(&file_dir, file_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
}
#[test]
@ -426,15 +542,16 @@ fn command_ls_with_filecompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
let target_dir = "ls custom_completion.";
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions);
}
#[test]
fn command_open_with_filecompletion() {
let (_, _, engine, stack) = new_engine();
@ -467,14 +584,14 @@ fn command_open_with_filecompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
let target_dir = "open custom_completion.";
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions);
}
#[test]
@ -509,7 +626,7 @@ fn command_rm_with_globcompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -544,7 +661,7 @@ fn command_cp_with_globcompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -579,7 +696,7 @@ fn command_save_with_filecompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -614,7 +731,7 @@ fn command_touch_with_filecompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -649,7 +766,28 @@ fn command_watch_with_filecompletion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[rstest]
fn subcommand_completions(mut subcommand_completer: NuCompleter) {
let prefix = "foo br";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
&vec!["foo bar".to_string(), "foo aabrr".to_string()],
&suggestions,
);
let prefix = "foo b";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
&vec![
"foo bar".to_string(),
"foo abaz".to_string(),
"foo aabrr".to_string(),
],
&suggestions,
);
}
#[test]
@ -662,19 +800,19 @@ fn file_completion_quoted() {
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![
"\'[a] bc.txt\'".to_string(),
"`--help`".to_string(),
"`-42`".to_string(),
"`-inf`".to_string(),
"`4.2`".to_string(),
"\'[a] bc.txt\'".to_string(),
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
"`te(st).txt`".to_string(),
format!("`{}`", folder("test dir".into())),
format!("`{}`", folder("test dir")),
];
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
let dir: PathBuf = "test dir".into();
let target_dir = format!("open '{}'", folder(dir.clone()));
@ -685,7 +823,7 @@ fn file_completion_quoted() {
format!("`{}`", file(dir.join("single quote"))),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -720,7 +858,7 @@ fn flag_completions() {
];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[test]
@ -744,8 +882,21 @@ fn folder_with_directorycompletions() {
folder(dir.join(".hidden_folder")),
];
#[cfg(windows)]
{
let target_dir = format!("cd {dir_str}/");
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
let expected_slash_paths: Vec<String> = expected_paths
.iter()
.map(|s| s.replace('\\', "/"))
.collect();
match_suggestions(&expected_slash_paths, &slash_suggestions);
}
// Match the results
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
}
#[test]
@ -783,11 +934,11 @@ fn variables_completions() {
"plugin-path".into(),
"startup-time".into(),
"temp-path".into(),
"vendor-autoload-dir".into(),
"vendor-autoload-dirs".into(),
];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for $nu.h (filter)
let suggestions = completer.complete("$nu.h", 5);
@ -801,7 +952,7 @@ fn variables_completions() {
];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for $nu.os-info
let suggestions = completer.complete("$nu.os-info.", 12);
@ -813,7 +964,7 @@ fn variables_completions() {
"name".into(),
];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for custom var
let suggestions = completer.complete("$actor.", 7);
@ -823,7 +974,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["age".into(), "name".into()];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for custom var (filtering)
let suggestions = completer.complete("$actor.n", 8);
@ -833,7 +984,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["name".into()];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.", 5);
@ -846,7 +997,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.T", 6);
@ -856,7 +1007,12 @@ fn variables_completions() {
let expected: Vec<String> = vec!["TEST".into()];
// Match results
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
let suggestions = completer.complete("$", 1);
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
match_suggestions(&expected, &suggestions);
}
#[test]
@ -875,7 +1031,7 @@ fn alias_of_command_and_flags() {
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -894,7 +1050,7 @@ fn alias_of_basic_command() {
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[test]
@ -916,7 +1072,7 @@ fn alias_of_another_alias() {
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
@ -979,35 +1135,35 @@ fn unknown_command_completion() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
match_suggestions(&expected_paths, &suggestions)
}
#[rstest]
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c | ls", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h | ls", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[test]
@ -1041,77 +1197,77 @@ fn filecompletions_triggers_after_cursor() {
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions);
match_suggestions(&expected_paths, &suggestions);
}
#[rstest]
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam ", 5);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo=", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -f ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -b ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn extern_complete_flags(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -", 6);
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 8);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 8);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 11);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[rstest]
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar ", 12);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
match_suggestions(expected, suggestions);
match_suggestions(&expected, &suggestions);
}
#[ignore = "was reverted, still needs fixing"]

View File

@ -1,5 +1,6 @@
use nu_engine::eval_block;
use nu_parser::parse;
use nu_path::{AbsolutePathBuf, PathBuf};
use nu_protocol::{
debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet},
@ -7,14 +8,14 @@ use nu_protocol::{
};
use nu_test_support::fs;
use reedline::Suggestion;
use std::path::{PathBuf, MAIN_SEPARATOR};
use std::path::MAIN_SEPARATOR;
fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
}
// creates a new engine with the current path into the completions fixtures folder
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("completions");
let dir_str = dir
@ -68,7 +69,53 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
// creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
let dir_span = nu_protocol::Span::new(0, dir_str.len());
// Create a new engine with default context
let mut engine_state = create_default_context();
// Add $nu
engine_state.generate_nu_constant();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
stack.add_env_var(
"TEST".to_string(),
Value::string("NUSHELL".to_string(), dir_span),
);
stack.add_env_var(
"NU_LIB_DIRS".to_string(),
Value::List {
vals: vec![
Value::string(file(dir.join("lib-dir1")), dir_span),
Value::string(file(dir.join("lib-dir2")), dir_span),
Value::string(file(dir.join("lib-dir3")), dir_span),
],
internal_span: dir_span,
},
);
// Merge environment into the permanent state
let merge_result = engine_state.merge_env(&mut stack, &dir);
assert!(merge_result.is_ok());
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
let dir_str = dir
@ -103,7 +150,7 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack)
}
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("partial_completions");
let dir_str = dir
@ -139,7 +186,7 @@ pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
}
// match a list of suggestions with the expected values
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>) {
let expected_len = expected.len();
let suggestions_len = suggestions.len();
if expected_len != suggestions_len {
@ -149,22 +196,25 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
Expected: {expected:#?}\n"
)
}
expected.iter().zip(suggestions).for_each(|it| {
assert_eq!(it.0, &it.1.value);
});
let suggestoins_str = suggestions
.iter()
.map(|it| it.value.clone())
.collect::<Vec<_>>();
assert_eq!(expected, &suggestoins_str);
}
// append the separator to the converted path
pub fn folder(path: PathBuf) -> String {
pub fn folder(path: impl Into<PathBuf>) -> String {
let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR);
converted_path
}
// convert a given path to string
pub fn file(path: PathBuf) -> String {
path.into_os_string().into_string().unwrap_or_default()
pub fn file(path: impl Into<PathBuf>) -> String {
path.into().into_os_string().into_string().unwrap()
}
// merge_input executes the given input into the engine
@ -173,7 +223,7 @@ pub fn merge_input(
input: &[u8],
engine_state: &mut EngineState,
stack: &mut Stack,
dir: PathBuf,
dir: AbsolutePathBuf,
) -> Result<(), ShellError> {
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);

View File

@ -5,15 +5,15 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.95.1"
version = "0.96.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.96.2" }
nu-parser = { path = "../nu-parser", version = "0.96.2" }
nu-path = { path = "../nu-path", version = "0.96.2" }
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
indexmap = { workspace = true }
miette = { workspace = true }

View File

@ -0,0 +1,5 @@
Utilities used by the different `nu-command`/`nu-cmd-*` crates, should not contain any full `Command` implementations.
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

@ -1,5 +1,5 @@
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Span, Value};
use std::sync::{atomic::AtomicBool, Arc};
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
use std::sync::Arc;
pub trait CmdArgument {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
@ -40,7 +40,7 @@ pub fn operate<C, A>(
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
signals: &Signals,
) -> Result<PipelineData, ShellError>
where
A: CmdArgument + Send + Sync + 'static,
@ -55,7 +55,7 @@ where
_ => cmd(&v, &arg, span),
}
},
ctrlc,
signals,
),
Some(column_paths) => {
let arg = Arc::new(arg);
@ -79,7 +79,7 @@ where
}
v
},
ctrlc,
signals,
)
}
}

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
pub mod formats;
pub mod hook;
pub mod input_handler;

View File

@ -1,3 +1,4 @@
use nu_path::AbsolutePathBuf;
use nu_protocol::{
engine::{EngineState, Stack},
Range, ShellError, Span, Value,
@ -15,6 +16,7 @@ pub fn get_init_cwd() -> PathBuf {
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
engine_state
.cwd(Some(stack))
.map(AbsolutePathBuf::into_std_path_buf)
.unwrap_or(crate::util::get_init_cwd())
}

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.95.1"
version = "0.96.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,13 +13,13 @@ version = "0.95.1"
bench = false
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-json = { version = "0.95.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-pretty-hex = { version = "0.95.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.95.1" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.2" }
nu-engine = { path = "../nu-engine", version = "0.96.2" }
nu-json = { version = "0.96.2", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.96.2" }
nu-pretty-hex = { version = "0.96.2", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
nu-utils = { path = "../nu-utils", version = "0.96.2" }
# Potential dependencies for extras
heck = { workspace = true }
@ -33,6 +33,6 @@ v_htmlescape = { workspace = true }
itertools = { workspace = true }
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" }
nu-command = { path = "../nu-command", version = "0.95.1" }
nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.2" }
nu-command = { path = "../nu-command", version = "0.96.2" }
nu-test-support = { path = "../nu-test-support", version = "0.96.2" }

View File

@ -79,7 +79,7 @@ impl Command for BitsAnd {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -1,6 +1,9 @@
use std::io::{self, Read, Write};
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
use nu_protocol::Signals;
use num_traits::ToPrimitive;
pub struct Arguments {
@ -118,12 +121,43 @@ fn into_bits(
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
if let PipelineData::ByteStream(stream, ..) = input {
// TODO: in the future, we may want this to stream out, converting each to bytes
Ok(Value::binary(stream.into_bytes()?, head).into_pipeline_data())
if let PipelineData::ByteStream(stream, metadata) = input {
Ok(PipelineData::ByteStream(
byte_stream_to_bits(stream, head),
metadata,
))
} else {
let args = Arguments { cell_paths };
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
}
fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
if let Some(mut reader) = stream.reader() {
let mut is_first = true;
ByteStream::from_fn(
head,
Signals::empty(),
ByteStreamType::String,
move |buffer| {
let mut byte = [0];
if reader.read(&mut byte[..]).err_span(head)? > 0 {
// Format the byte as bits
if is_first {
is_first = false;
} else {
buffer.push(b' ');
}
write!(buffer, "{:08b}", byte[0]).expect("format failed");
Ok(true)
} else {
// EOF
Ok(false)
}
},
)
} else {
ByteStream::read(io::empty(), head, Signals::empty(), ByteStreamType::String)
}
}

View File

@ -82,7 +82,7 @@ impl Command for BitsNot {
number_size,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsOr {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -86,7 +86,7 @@ impl Command for BitsRol {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -86,7 +86,7 @@ impl Command for BitsRor {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShl {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShr {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsXor {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -59,7 +59,7 @@ fn fmt(
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {

View File

@ -89,7 +89,7 @@ impl Command for EachWhile {
}
})
.fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone()))
.into_pipeline_data(head, engine_state.signals().clone()))
}
PipelineData::ByteStream(stream, ..) => {
let span = stream.span();
@ -107,7 +107,7 @@ impl Command for EachWhile {
}
})
.fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone()))
.into_pipeline_data(head, engine_state.signals().clone()))
} else {
Ok(PipelineData::Empty)
}

View File

@ -108,7 +108,7 @@ impl Command for UpdateCells {
columns,
span: head,
}
.into_pipeline_data(head, engine_state.ctrlc.clone())
.into_pipeline_data(head, engine_state.signals().clone())
.set_metadata(metadata))
}
}

View File

@ -239,7 +239,7 @@ fn to_html(
let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = engine_state.get_config();
let config = &stack.get_config(engine_state);
let vec_of_values = input.into_iter().collect::<Vec<Value>>();
let headers = merge_descriptors(&vec_of_values);
@ -368,6 +368,7 @@ fn theme_demo(span: Span) -> PipelineData {
.collect();
Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata {
data_source: DataSource::HtmlThemes,
content_type: None,
})
}

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -140,7 +140,7 @@ fn operate(
ret
}
},
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -88,7 +88,7 @@ pub fn operate(
cell_paths,
};
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
general_operate(action, args, input, call.head, engine_state.signals())
}
fn action(

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, ListStream};
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
#[derive(Clone)]
pub struct FormatPattern;
@ -43,6 +43,8 @@ impl Command for FormatPattern {
let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false);
stack.add_var(it_id, input_val.clone());
let config = stack.get_config(engine_state);
match specified_pattern {
Err(e) => Err(e),
Ok(pattern) => {
@ -56,7 +58,7 @@ impl Command for FormatPattern {
string_span.start + 1,
)?;
format(input_val, &ops, engine_state, call.head)
format(input_val, &ops, engine_state, &config, call.head)
}
}
}
@ -181,33 +183,30 @@ fn format(
input_data: Value,
format_operations: &[FormatOperation],
engine_state: &EngineState,
config: &Config,
head_span: Span,
) -> Result<PipelineData, ShellError> {
let data_as_value = input_data;
// We can only handle a Record or a List of Records
match data_as_value {
Value::Record { .. } => {
match format_record(format_operations, &data_as_value, engine_state) {
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
Err(value) => Err(value),
}
}
Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
Err(value) => Err(value),
},
Value::List { vals, .. } => {
let mut list = vec![];
for val in vals.iter() {
match val {
Value::Record { .. } => {
match format_record(format_operations, val, engine_state) {
Ok(value) => {
list.push(Value::string(value, head_span));
}
Err(value) => {
return Err(value);
}
Value::Record { .. } => match format_record(format_operations, val, config) {
Ok(value) => {
list.push(Value::string(value, head_span));
}
}
Err(value) => {
return Err(value);
}
},
Value::Error { error, .. } => return Err(*error.clone()),
_ => {
return Err(ShellError::OnlySupportsThisInputType {
@ -220,7 +219,7 @@ fn format(
}
}
Ok(ListStream::new(list.into_iter(), head_span, engine_state.ctrlc.clone()).into())
Ok(ListStream::new(list.into_iter(), head_span, engine_state.signals().clone()).into())
}
// Unwrapping this ShellError is a bit unfortunate.
// Ideally, its Span would be preserved.
@ -237,9 +236,8 @@ fn format(
fn format_record(
format_operations: &[FormatOperation],
data_as_value: &Value,
engine_state: &EngineState,
config: &Config,
) -> Result<String, ShellError> {
let config = engine_state.get_config();
let mut output = String::new();
for op in format_operations {

View File

@ -44,7 +44,7 @@ where
case_operation,
cell_paths,
};
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
general_operate(action, args, input, call.head, engine_state.signals())
}
fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod example_test;
pub mod extra;
pub use extra::*;

View File

@ -0,0 +1,13 @@
use nu_test_support::nu;
#[test]
fn byte_stream_into_bits() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits");
assert_eq!("00000001 00000010 00000011", result.out);
}
#[test]
fn byte_stream_into_bits_is_stream() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits | describe");
assert_eq!("string (stream)", result.out);
}

View File

@ -0,0 +1 @@
mod into;

View File

@ -1 +1,2 @@
mod bits;
mod bytes;

View File

@ -6,22 +6,22 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021"
license = "MIT"
name = "nu-cmd-lang"
version = "0.95.1"
version = "0.96.2"
[lib]
bench = false
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.96.2" }
nu-parser = { path = "../nu-parser", version = "0.96.2" }
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
nu-utils = { path = "../nu-utils", version = "0.96.2" }
itertools = { workspace = true }
shadow-rs = { version = "0.29", default-features = false }
shadow-rs = { version = "0.30", default-features = false }
[build-dependencies]
shadow-rs = { version = "0.29", default-features = false }
shadow-rs = { version = "0.30", default-features = false }
[features]
mimalloc = []

View File

@ -21,7 +21,9 @@ impl Command for Break {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
https://www.nushell.sh/book/thinking_in_nu.html
break can only be used in while, loop, and for loops. It can not be used with each or other filter commands"#
}
fn command_type(&self) -> CommandType {

View File

@ -50,6 +50,7 @@ is particularly large, this can cause high memory usage."#
// check where some input came from.
Some(PipelineMetadata {
data_source: DataSource::FilePath(_),
content_type: None,
}) => None,
other => other,
};

View File

@ -46,6 +46,9 @@ impl Command for Const {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
id
} else {

View File

@ -21,7 +21,9 @@ impl Command for Continue {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
https://www.nushell.sh/book/thinking_in_nu.html
continue can only be used in while, loop, and for loops. It can not be used with each or other filter commands"#
}
fn command_type(&self) -> CommandType {

View File

@ -81,6 +81,10 @@ impl Command for Do {
bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
// Applies to all block evaluation once set true
callee_stack.use_ir = caller_stack.has_env_var(engine_state, "NU_USE_IR");
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
if has_env {

View File

@ -56,16 +56,7 @@ impl Command for ErrorMake {
Example {
description: "Create a simple custom error",
example: r#"error make {msg: "my custom error message"}"#,
result: Some(Value::error(
ShellError::GenericError {
error: "my custom error message".into(),
msg: "".into(),
span: None,
help: None,
inner: vec![],
},
Span::unknown(),
)),
result: None,
},
Example {
description: "Create a more complex custom error",
@ -82,16 +73,7 @@ impl Command for ErrorMake {
}
help: "A help string, suggesting a fix to the user" # optional
}"#,
result: Some(Value::error(
ShellError::GenericError {
error: "my custom error message".into(),
msg: "my custom label text".into(),
span: Some(Span::new(123, 456)),
help: Some("A help string, suggesting a fix to the user".into()),
inner: vec![],
},
Span::unknown(),
)),
result: None,
},
Example {
description:

View File

@ -1,6 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
use nu_protocol::engine::CommandType;
use nu_protocol::ParseWarning;
use nu_protocol::{engine::CommandType, Signals};
#[derive(Clone)]
pub struct For;
@ -29,11 +28,6 @@ impl Command for For {
"Range of the loop.",
)
.required("block", SyntaxShape::Block, "The block to run.")
.switch(
"numbered",
"DEPRECATED: return a numbered item ($it.index and $it.item)",
Some('n'),
)
.creates_scope()
.category(Category::Core)
}
@ -54,6 +48,9 @@ impl Command for For {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let var_id = call
.positional_nth(0)
@ -78,23 +75,6 @@ impl Command for For {
let value = eval_expression(engine_state, stack, keyword_expr)?;
let numbered = call.has_flag(engine_state, stack, "numbered")?;
if numbered {
nu_protocol::report_error_new(
engine_state,
&ParseWarning::DeprecatedWarning {
old_command: "--numbered/-n".into(),
new_suggestion: "use `enumerate`".into(),
span: call
.get_named_arg("numbered")
.expect("`get_named_arg` found `--numbered` but still failed")
.span,
url: "See `help for` examples".into(),
},
);
}
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let block = engine_state.get_block(block_id);
@ -103,29 +83,14 @@ impl Command for For {
let span = value.span();
match value {
Value::List { vals, .. } => {
for (idx, x) in vals.into_iter().enumerate() {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
break;
}
for x in vals.into_iter() {
engine_state.signals().check(head)?;
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
stack.add_var(
var_id,
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
stack.add_var(var_id, x);
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {
@ -151,21 +116,9 @@ impl Command for For {
}
}
Value::Range { val, .. } => {
for (idx, x) in val.into_range_iter(span, ctrlc).enumerate() {
stack.add_var(
var_id,
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
for x in val.into_range_iter(span, Signals::empty()) {
engine_state.signals().check(head)?;
stack.add_var(var_id, x);
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {

View File

@ -60,6 +60,9 @@ impl Command for If {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call
.positional_nth(1)
@ -99,6 +102,9 @@ impl Command for If {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call
.positional_nth(1)

View File

@ -46,6 +46,9 @@ impl Command for Let {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call
.positional_nth(0)
.expect("checked through parser")

View File

@ -37,6 +37,10 @@ impl Command for Loop {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let block_id = call
.positional_nth(0)
.expect("checked through parser")
@ -49,9 +53,7 @@ impl Command for Loop {
let stack = &mut stack.push_redirection(None, None);
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
engine_state.signals().check(head)?;
match eval_block(engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {

View File

@ -43,6 +43,9 @@ impl Command for Match {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let value: Value = call.req(engine_state, stack, 0)?;
let matches = call
.positional_nth(1)

View File

@ -46,6 +46,9 @@ impl Command for Mut {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call
.positional_nth(0)
.expect("checked through parser")

View File

@ -65,9 +65,9 @@ impl Command for OverlayUse {
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
if let Expr::Overlay(module_id) = &overlay_expr.expr {
module_id
*module_id
} else {
return Err(ShellError::NushellFailedSpanned {
msg: "Not an overlay".to_string(),
@ -110,7 +110,7 @@ impl Command for OverlayUse {
// a) adding a new overlay
// b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(*module_id);
let module = engine_state.get_module(module_id);
// Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block {
@ -118,7 +118,7 @@ impl Command for OverlayUse {
&name_arg.item,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(caller_stack, call),
)?;
let block = engine_state.get_block(block_id);

View File

@ -47,6 +47,9 @@ impl Command for Try {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let try_block = call
.positional_nth(0)
.expect("checked through parser")

View File

@ -57,7 +57,7 @@ This command is a parser keyword. For details, check:
let Some(Expression {
expr: Expr::ImportPattern(import_pattern),
..
}) = call.get_parser_info("import_pattern")
}) = call.get_parser_info(caller_stack, "import_pattern")
else {
return Err(ShellError::GenericError {
error: "Unexpected import".into(),
@ -68,6 +68,9 @@ This command is a parser keyword. For details, check:
});
};
// Necessary so that we can modify the stack.
let import_pattern = import_pattern.clone();
if let Some(module_id) = import_pattern.head.id {
// Add constants
for var_id in &import_pattern.constants {
@ -99,7 +102,7 @@ This command is a parser keyword. For details, check:
&module_arg_str,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
get_dirs_var_from_call(caller_stack, call),
)?;
let maybe_parent = maybe_file_path
.as_ref()

View File

@ -46,6 +46,10 @@ impl Command for While {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let cond = call.positional_nth(0).expect("checked through parser");
let block_id = call
.positional_nth(1)
@ -59,9 +63,7 @@ impl Command for While {
let stack = &mut stack.push_redirection(None, None);
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
engine_state.signals().check(head)?;
let result = eval_expression(engine_state, stack, cond)?;

View File

@ -4,7 +4,7 @@ use nu_protocol::{
ast::Block,
debugger::WithoutDebug,
engine::{StateDelta, StateWorkingSet},
Range,
report_error_new, Range,
};
use std::{
sync::Arc,
@ -124,7 +124,10 @@ pub fn eval_block(
nu_engine::eval_block::<WithoutDebug>(engine_state, &mut stack, &block, input)
.and_then(|data| data.into_value(Span::test_data()))
.unwrap_or_else(|err| panic!("test eval error in `{}`: {:?}", "TODO", err))
.unwrap_or_else(|err| {
report_error_new(engine_state, &err);
panic!("test eval error in `{}`: {:?}", "TODO", err)
})
}
pub fn check_example_evaluates_to_expected_output(

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod core_commands;
mod default_context;
pub mod example_support;

View File

@ -5,15 +5,15 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
version = "0.95.1"
version = "0.96.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.95.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.96.2" }
nu-path = { path = "../nu-path", version = "0.96.2" }
nu-protocol = { path = "../nu-protocol", version = "0.96.2", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.96.2" }
itertools = { workspace = true }

View File

@ -1,5 +1,3 @@
mod plugin;
mod register;
pub use plugin::*;
pub use register::Register;

View File

@ -72,7 +72,7 @@ impl Command for PluginStop {
error: format!("Failed to stop the `{}` plugin", name.item),
msg: "couldn't find a plugin with this name".into(),
span: Some(name.span),
help: Some("you may need to `register` the plugin first".into()),
help: Some("you may need to `plugin add` the plugin first".into()),
inner: vec![],
})
}

Some files were not shown because too many files have changed in this diff Show More