Merge branch 'main' into path-migration-3
This commit is contained in:
commit
d198b3b66c
2
.github/workflows/nightly-build.yml
vendored
2
.github/workflows/nightly-build.yml
vendored
|
@ -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
|
||||
|
|
6
.github/workflows/release-pkg.nu
vendored
6
.github/workflows/release-pkg.nu
vendored
|
@ -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' {
|
||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -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
|
||||
|
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
|
@ -10,4 +10,4 @@ jobs:
|
|||
uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@v1.23.2
|
||||
uses: crate-ci/typos@v1.23.5
|
||||
|
|
149
Cargo.lock
generated
149
Cargo.lock
generated
|
@ -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]]
|
||||
|
@ -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",
|
||||
|
@ -2913,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",
|
||||
|
@ -2957,7 +2957,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-cmd-base"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"miette",
|
||||
|
@ -2969,7 +2969,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-cmd-extra"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
"heck 0.5.0",
|
||||
|
@ -2994,7 +2994,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"nu-engine",
|
||||
|
@ -3006,7 +3006,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-cmd-plugin"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"nu-engine",
|
||||
|
@ -3017,7 +3017,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-color-config"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"nu-engine",
|
||||
|
@ -3029,7 +3029,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-command"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"alphanumeric-sort",
|
||||
"base64 0.22.1",
|
||||
|
@ -3047,7 +3047,7 @@ dependencies = [
|
|||
"deunicode",
|
||||
"dialoguer",
|
||||
"digest",
|
||||
"dirs-next",
|
||||
"dirs",
|
||||
"dtparse",
|
||||
"encoding_rs",
|
||||
"fancy-regex",
|
||||
|
@ -3139,7 +3139,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-derive-value"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro-error",
|
||||
|
@ -3150,7 +3150,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-engine"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nu-glob",
|
||||
|
@ -3161,7 +3161,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-explore"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"ansi-str",
|
||||
"anyhow",
|
||||
|
@ -3186,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",
|
||||
|
@ -3203,7 +3206,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-lsp"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"crossbeam-channel",
|
||||
|
@ -3224,7 +3227,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-parser"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
|
@ -3240,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",
|
||||
|
@ -3265,7 +3268,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-plugin-core"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"interprocess",
|
||||
"log",
|
||||
|
@ -3279,7 +3282,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-plugin-engine"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nu-engine",
|
||||
|
@ -3295,7 +3298,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-plugin-protocol"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"nu-protocol",
|
||||
|
@ -3307,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",
|
||||
|
@ -3325,7 +3328,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-pretty-hex"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"nu-ansi-term",
|
||||
|
@ -3334,13 +3337,15 @@ 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",
|
||||
|
@ -3364,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",
|
||||
|
@ -3379,7 +3385,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-system"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"itertools 0.12.1",
|
||||
|
@ -3397,7 +3403,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-table"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
"nu-ansi-term",
|
||||
|
@ -3411,7 +3417,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-term-grid"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"nu-utils",
|
||||
"unicode-width",
|
||||
|
@ -3419,7 +3425,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-test-support"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"nu-glob",
|
||||
"nu-path",
|
||||
|
@ -3431,7 +3437,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu-utils"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"crossterm_winapi",
|
||||
"log",
|
||||
|
@ -3457,7 +3463,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_example"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"nu-cmd-lang",
|
||||
"nu-plugin",
|
||||
|
@ -3467,7 +3473,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_formats"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"eml-parser",
|
||||
"ical",
|
||||
|
@ -3480,7 +3486,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_gstat"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"git2",
|
||||
"nu-plugin",
|
||||
|
@ -3489,7 +3495,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_inc"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
|
@ -3498,7 +3504,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_polars"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz 0.9.0",
|
||||
|
@ -3532,7 +3538,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_query"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"gjson",
|
||||
"nu-plugin",
|
||||
|
@ -3547,7 +3553,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nu_plugin_stress_internals"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"interprocess",
|
||||
"serde",
|
||||
|
@ -3673,7 +3679,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
|||
|
||||
[[package]]
|
||||
name = "nuon"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fancy-regex",
|
||||
|
@ -3770,9 +3776,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37"
|
||||
checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
|
@ -3781,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",
|
||||
|
@ -3822,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",
|
||||
|
@ -3833,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"
|
||||
|
@ -4992,8 +5004,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.32.0"
|
||||
source = "git+https://github.com/nushell/reedline?branch=main#480059a3f52cf919341cda88e8c544edd846bc73"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f8c676a3f3814a23c6a0fc9dff6b6c35b2e04df8134aae6f3929cc34de21a53"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"chrono",
|
||||
|
@ -5207,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",
|
||||
|
@ -5576,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",
|
||||
|
@ -5653,9 +5666,9 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
|||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
||||
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
|
@ -6629,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",
|
||||
|
|
64
Cargo.toml
64
Cargo.toml
|
@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
|
|||
license = "MIT"
|
||||
name = "nu"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
rust-version = "1.77.2"
|
||||
version = "0.95.1"
|
||||
rust-version = "1.78.0"
|
||||
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.2"
|
||||
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"
|
||||
|
@ -173,35 +174,36 @@ uu_mv = "0.0.27"
|
|||
uu_whoami = "0.0.27"
|
||||
uu_uname = "0.0.27"
|
||||
uucore = "0.0.27"
|
||||
uuid = "1.9.1"
|
||||
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 }
|
||||
|
@ -225,11 +227,11 @@ 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 }
|
||||
|
@ -303,8 +305,8 @@ 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]
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
# [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"}
|
||||
|
||||
# Run all benchmarks with `cargo bench`
|
||||
|
|
29
SECURITY.md
Normal file
29
SECURITY.md
Normal 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.
|
|
@ -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
7
crates/nu-cli/README.md
Normal 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.
|
|
@ -156,58 +156,34 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
|
|||
//2. Create a record of either short or long columns and values
|
||||
|
||||
let item_id_value = Value::int(
|
||||
match entry.id {
|
||||
Some(id) => {
|
||||
let ids = id.to_string();
|
||||
match ids.parse::<i64>() {
|
||||
Ok(i) => i,
|
||||
_ => 0i64,
|
||||
}
|
||||
}
|
||||
None => 0i64,
|
||||
},
|
||||
entry
|
||||
.id
|
||||
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||
.unwrap_or_default(),
|
||||
head,
|
||||
);
|
||||
let start_timestamp_value = Value::string(
|
||||
match entry.start_timestamp {
|
||||
Some(time) => time.to_string(),
|
||||
None => "".into(),
|
||||
},
|
||||
entry
|
||||
.start_timestamp
|
||||
.map(|time| time.to_string())
|
||||
.unwrap_or_default(),
|
||||
head,
|
||||
);
|
||||
let command_value = Value::string(entry.command_line, head);
|
||||
let session_id_value = Value::int(
|
||||
match entry.session_id {
|
||||
Some(sid) => {
|
||||
let sids = sid.to_string();
|
||||
match sids.parse::<i64>() {
|
||||
Ok(i) => i,
|
||||
_ => 0i64,
|
||||
}
|
||||
}
|
||||
None => 0i64,
|
||||
},
|
||||
head,
|
||||
);
|
||||
let hostname_value = Value::string(
|
||||
match entry.hostname {
|
||||
Some(host) => host,
|
||||
None => "".into(),
|
||||
},
|
||||
head,
|
||||
);
|
||||
let cwd_value = Value::string(
|
||||
match entry.cwd {
|
||||
Some(cwd) => cwd,
|
||||
None => "".into(),
|
||||
},
|
||||
entry
|
||||
.session_id
|
||||
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||
.unwrap_or_default(),
|
||||
head,
|
||||
);
|
||||
let hostname_value = Value::string(entry.hostname.unwrap_or_default(), head);
|
||||
let cwd_value = Value::string(entry.cwd.unwrap_or_default(), head);
|
||||
let duration_value = Value::duration(
|
||||
match entry.duration {
|
||||
Some(d) => d.as_nanos().try_into().unwrap_or(0),
|
||||
None => 0,
|
||||
},
|
||||
entry
|
||||
.duration
|
||||
.and_then(|d| d.as_nanos().try_into().ok())
|
||||
.unwrap_or(0),
|
||||
head,
|
||||
);
|
||||
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
||||
|
|
|
@ -61,10 +61,12 @@ impl Command for KeybindingsList {
|
|||
.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)| *present)
|
||||
.filter(|(_, present)| no_option_specified || *present)
|
||||
.flat_map(|(option, _)| get_records(option, call.head))
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -443,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())),
|
||||
});
|
||||
|
@ -460,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`
|
||||
|
|
|
@ -10,9 +10,7 @@ use nu_protocol::{
|
|||
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;
|
||||
|
||||
|
@ -93,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
|
||||
}
|
||||
|
@ -133,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)
|
||||
|
@ -195,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,
|
||||
|
|
|
@ -48,14 +48,12 @@ 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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -53,14 +53,12 @@ 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,
|
||||
|
|
|
@ -51,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,
|
||||
|
@ -78,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,
|
||||
|
|
|
@ -85,11 +85,8 @@ 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())),
|
||||
});
|
||||
|
@ -157,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,
|
||||
|
@ -184,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(),
|
||||
|
@ -215,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(),
|
||||
|
@ -255,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()),
|
||||
});
|
||||
|
@ -272,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()),
|
||||
});
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod commands;
|
||||
mod completions;
|
||||
mod config_files;
|
||||
|
|
|
@ -110,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()
|
||||
|
|
|
@ -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()
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -429,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)
|
||||
|
|
|
@ -321,16 +321,10 @@ mod test {
|
|||
|
||||
let env = engine_state.render_env_vars();
|
||||
|
||||
assert!(
|
||||
matches!(env.get(&"FOO".to_string()), Some(&Value::String { val, .. }) if val == "foo")
|
||||
);
|
||||
assert!(
|
||||
matches!(env.get(&"SYMBOLS".to_string()), Some(&Value::String { val, .. }) if val == symbols)
|
||||
);
|
||||
assert!(
|
||||
matches!(env.get(&symbols.to_string()), Some(&Value::String { val, .. }) if val == "symbols")
|
||||
);
|
||||
assert!(env.get(&"PWD".to_string()).is_some());
|
||||
assert!(matches!(env.get("FOO"), Some(&Value::String { val, .. }) if val == "foo"));
|
||||
assert!(matches!(env.get("SYMBOLS"), Some(&Value::String { val, .. }) if val == symbols));
|
||||
assert!(matches!(env.get(symbols), Some(&Value::String { val, .. }) if val == "symbols"));
|
||||
assert!(env.contains_key("PWD"));
|
||||
assert_eq!(env.len(), 4);
|
||||
}
|
||||
}
|
||||
|
|
7
crates/nu-cli/tests/commands/keybindings_list.rs
Normal file
7
crates/nu-cli/tests/commands/keybindings_list.rs
Normal 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");
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
mod keybindings_list;
|
||||
mod nu_highlight;
|
||||
|
|
|
@ -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 }"#;
|
||||
|
@ -123,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]
|
||||
|
@ -153,7 +152,7 @@ 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]
|
||||
|
@ -182,19 +181,19 @@ fn dotnu_completions() {
|
|||
let completion_str = "source-env ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
match_suggestions(expected.clone(), suggestions);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
|
||||
// Test use completion
|
||||
let completion_str = "use ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
match_suggestions(expected.clone(), suggestions);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
|
||||
// Test overlay use completion
|
||||
let completion_str = "overlay use ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
match_suggestions(expected, suggestions);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -258,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")));
|
||||
|
@ -269,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]
|
||||
|
@ -303,7 +390,7 @@ fn partial_completions() {
|
|||
];
|
||||
|
||||
// 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"
|
||||
|
@ -324,7 +411,7 @@ 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 dir_str = folder(dir.join("pa"));
|
||||
|
@ -345,7 +432,7 @@ fn partial_completions() {
|
|||
];
|
||||
|
||||
// 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"));
|
||||
|
@ -356,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"));
|
||||
|
@ -392,7 +479,7 @@ 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"));
|
||||
|
@ -406,7 +493,7 @@ 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_ext."));
|
||||
|
@ -420,7 +507,7 @@ fn partial_completions() {
|
|||
];
|
||||
|
||||
// Match the results
|
||||
match_suggestions(expected_paths, suggestions);
|
||||
match_suggestions(&expected_paths, &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -455,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();
|
||||
|
@ -496,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]
|
||||
|
@ -538,7 +626,7 @@ fn command_rm_with_globcompletion() {
|
|||
".hidden_folder/".to_string(),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -573,7 +661,7 @@ fn command_cp_with_globcompletion() {
|
|||
".hidden_folder/".to_string(),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -608,7 +696,7 @@ fn command_save_with_filecompletion() {
|
|||
".hidden_folder/".to_string(),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -643,7 +731,7 @@ fn command_touch_with_filecompletion() {
|
|||
".hidden_folder/".to_string(),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -678,7 +766,7 @@ fn command_watch_with_filecompletion() {
|
|||
".hidden_folder/".to_string(),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
@ -686,19 +774,19 @@ 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,
|
||||
&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![
|
||||
&vec![
|
||||
"foo bar".to_string(),
|
||||
"foo abaz".to_string(),
|
||||
"foo aabrr".to_string(),
|
||||
],
|
||||
suggestions,
|
||||
&suggestions,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -724,7 +812,7 @@ fn file_completion_quoted() {
|
|||
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()));
|
||||
|
@ -735,7 +823,7 @@ fn file_completion_quoted() {
|
|||
format!("`{}`", file(dir.join("single quote"))),
|
||||
];
|
||||
|
||||
match_suggestions(expected_paths, suggestions)
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -770,7 +858,7 @@ fn flag_completions() {
|
|||
];
|
||||
|
||||
// Match results
|
||||
match_suggestions(expected, suggestions);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -794,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]
|
||||
|
@ -833,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);
|
||||
|
@ -851,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);
|
||||
|
@ -863,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);
|
||||
|
@ -873,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);
|
||||
|
@ -883,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);
|
||||
|
@ -896,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);
|
||||
|
@ -906,12 +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);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -930,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]
|
||||
|
@ -949,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]
|
||||
|
@ -971,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> {
|
||||
|
@ -1034,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]
|
||||
|
@ -1096,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"]
|
||||
|
|
|
@ -186,7 +186,7 @@ pub fn new_partial_engine() -> (AbsolutePathBuf, 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 {
|
||||
|
@ -196,13 +196,13 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
|||
Expected: {expected:#?}\n"
|
||||
)
|
||||
}
|
||||
assert_eq!(
|
||||
expected,
|
||||
suggestions
|
||||
.into_iter()
|
||||
.map(|it| it.value)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
let suggestoins_str = suggestions
|
||||
.iter()
|
||||
.map(|it| it.value.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(expected, &suggestoins_str);
|
||||
}
|
||||
|
||||
// append the separator to the converted path
|
||||
|
|
|
@ -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 }
|
||||
|
|
5
crates/nu-cmd-base/README.md
Normal file
5
crates/nu-cmd-base/README.md
Normal 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.
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
pub mod formats;
|
||||
pub mod hook;
|
||||
pub mod input_handler;
|
||||
|
|
|
@ -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" }
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod example_test;
|
||||
pub mod extra;
|
||||
pub use extra::*;
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod core_commands;
|
||||
mod default_context;
|
||||
pub mod example_support;
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-color-config"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.95.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.95.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||
nu-json = { path = "../nu-json", version = "0.96.2" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.96.2" }
|
5
crates/nu-color-config/README.md
Normal file
5
crates/nu-color-config/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
Logic to resolve colors for syntax highlighting and output formatting
|
||||
|
||||
## Internal Nushell crate
|
||||
|
||||
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod color_config;
|
||||
mod matching_brackets_style;
|
||||
mod nu_style;
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
license = "MIT"
|
||||
name = "nu-command"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||
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,21 +13,21 @@ version = "0.95.1"
|
|||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.95.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.95.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.95.1" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.95.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.95.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.95.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.95.1" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.95.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
|
||||
nu-system = { path = "../nu-system", version = "0.95.1" }
|
||||
nu-table = { path = "../nu-table", version = "0.95.1" }
|
||||
nu-term-grid = { path = "../nu-term-grid", 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-color-config = { path = "../nu-color-config", version = "0.96.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.96.2" }
|
||||
nu-json = { path = "../nu-json", version = "0.96.2" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||
nu-path = { path = "../nu-path", version = "0.96.2" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.96.2" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||
nu-system = { path = "../nu-system", version = "0.96.2" }
|
||||
nu-table = { path = "../nu-table", version = "0.96.2" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.96.2" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
nuon = { path = "../nuon", version = "0.95.1" }
|
||||
nuon = { path = "../nuon", version = "0.96.2" }
|
||||
|
||||
alphanumeric-sort = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
|
@ -137,10 +137,10 @@ sqlite = ["rusqlite"]
|
|||
trash-support = ["trash"]
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", 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-test-support = { path = "../nu-test-support", version = "0.96.2" }
|
||||
|
||||
dirs-next = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
mockito = { workspace = true, default-features = false }
|
||||
quickcheck = { workspace = true }
|
||||
quickcheck_macros = { workspace = true }
|
||||
|
|
7
crates/nu-command/README.md
Normal file
7
crates/nu-command/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
This crate contains the majority of our commands
|
||||
|
||||
We allow ourselves to move some of the commands in `nu-command` to `nu-cmd-*` crates as needed.
|
||||
|
||||
## Internal Nushell crate
|
||||
|
||||
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -150,13 +150,9 @@ fn fill(
|
|||
FillAlignment::Left
|
||||
};
|
||||
|
||||
let width = if let Some(arg) = width_arg { arg } else { 1 };
|
||||
let width = width_arg.unwrap_or(1);
|
||||
|
||||
let character = if let Some(arg) = character_arg {
|
||||
arg
|
||||
} else {
|
||||
" ".to_string()
|
||||
};
|
||||
let character = character_arg.unwrap_or_else(|| " ".to_string());
|
||||
|
||||
let arg = Arguments {
|
||||
width,
|
||||
|
|
|
@ -424,11 +424,7 @@ pub fn value_to_sql(value: Value) -> Result<Box<dyn rusqlite::ToSql>, ShellError
|
|||
Value::Filesize { val, .. } => Box::new(val),
|
||||
Value::Duration { val, .. } => Box::new(val),
|
||||
Value::Date { val, .. } => Box::new(val),
|
||||
Value::String { val, .. } => {
|
||||
// don't store ansi escape sequences in the database
|
||||
// escape single quotes
|
||||
Box::new(nu_utils::strip_ansi_unlikely(&val).into_owned())
|
||||
}
|
||||
Value::String { val, .. } => Box::new(val),
|
||||
Value::Binary { val, .. } => Box::new(val),
|
||||
Value::Nothing { .. } => Box::new(rusqlite::types::Null),
|
||||
val => {
|
||||
|
|
|
@ -42,32 +42,32 @@ impl Command for MetadataSet {
|
|||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
mut input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let ds_fp: Option<String> = call.get_flag(engine_state, stack, "datasource-filepath")?;
|
||||
let ds_ls = call.has_flag(engine_state, stack, "datasource-ls")?;
|
||||
let content_type: Option<String> = call.get_flag(engine_state, stack, "content-type")?;
|
||||
let signals = engine_state.signals().clone();
|
||||
let metadata = input
|
||||
.metadata()
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.with_content_type(content_type);
|
||||
|
||||
let mut metadata = match &mut input {
|
||||
PipelineData::Value(_, metadata)
|
||||
| PipelineData::ListStream(_, metadata)
|
||||
| PipelineData::ByteStream(_, metadata) => metadata.take().unwrap_or_default(),
|
||||
PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: head }),
|
||||
};
|
||||
|
||||
if let Some(content_type) = content_type {
|
||||
metadata.content_type = Some(content_type);
|
||||
}
|
||||
|
||||
match (ds_fp, ds_ls) {
|
||||
(Some(path), false) => Ok(input.into_pipeline_data_with_metadata(
|
||||
head,
|
||||
signals,
|
||||
metadata.with_data_source(DataSource::FilePath(path.into())),
|
||||
)),
|
||||
(None, true) => Ok(input.into_pipeline_data_with_metadata(
|
||||
head,
|
||||
signals,
|
||||
metadata.with_data_source(DataSource::Ls),
|
||||
)),
|
||||
_ => Ok(input.into_pipeline_data_with_metadata(head, signals, metadata)),
|
||||
(Some(path), false) => metadata.data_source = DataSource::FilePath(path.into()),
|
||||
(None, true) => metadata.data_source = DataSource::Ls,
|
||||
(Some(_), true) => (), // TODO: error here
|
||||
(None, false) => (),
|
||||
}
|
||||
|
||||
Ok(input.set_metadata(Some(metadata)))
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -85,7 +85,9 @@ impl Command for MetadataSet {
|
|||
Example {
|
||||
description: "Set the metadata of a file path",
|
||||
example: "'crates' | metadata set --content-type text/plain | metadata",
|
||||
result: Some(Value::record(record!("content_type" => Value::string("text/plain", Span::test_data())), Span::test_data())),
|
||||
result: Some(Value::test_record(record! {
|
||||
"content_type" => Value::test_string("text/plain"),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use nu_engine::{command_prelude::*, ClosureEvalOnce};
|
||||
use nu_protocol::{debugger::Profiler, engine::Closure};
|
||||
use nu_protocol::{
|
||||
debugger::{Profiler, ProfilerOptions},
|
||||
engine::Closure,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugProfile;
|
||||
|
@ -28,6 +31,7 @@ impl Command for DebugProfile {
|
|||
Some('v'),
|
||||
)
|
||||
.switch("expr", "Collect expression types", Some('x'))
|
||||
.switch("instructions", "Collect IR instructions", Some('i'))
|
||||
.switch("lines", "Collect line numbers", Some('l'))
|
||||
.named(
|
||||
"max-depth",
|
||||
|
@ -91,19 +95,23 @@ confusing the id/parent_id hierarchy. The --expr flag is helpful for investigati
|
|||
let collect_expanded_source = call.has_flag(engine_state, stack, "expanded-source")?;
|
||||
let collect_values = call.has_flag(engine_state, stack, "values")?;
|
||||
let collect_exprs = call.has_flag(engine_state, stack, "expr")?;
|
||||
let collect_instructions = call.has_flag(engine_state, stack, "instructions")?;
|
||||
let collect_lines = call.has_flag(engine_state, stack, "lines")?;
|
||||
let max_depth = call
|
||||
.get_flag(engine_state, stack, "max-depth")?
|
||||
.unwrap_or(2);
|
||||
|
||||
let profiler = Profiler::new(
|
||||
max_depth,
|
||||
collect_spans,
|
||||
true,
|
||||
collect_expanded_source,
|
||||
collect_values,
|
||||
collect_exprs,
|
||||
collect_lines,
|
||||
ProfilerOptions {
|
||||
max_depth,
|
||||
collect_spans,
|
||||
collect_source: true,
|
||||
collect_expanded_source,
|
||||
collect_values,
|
||||
collect_exprs,
|
||||
collect_instructions,
|
||||
collect_lines,
|
||||
},
|
||||
call.span(),
|
||||
);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
All,
|
||||
Any,
|
||||
Append,
|
||||
Chunks,
|
||||
Columns,
|
||||
Compact,
|
||||
Default,
|
||||
|
@ -290,7 +291,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
ToText,
|
||||
ToToml,
|
||||
ToTsv,
|
||||
Touch,
|
||||
Upsert,
|
||||
Where,
|
||||
ToXml,
|
||||
|
|
|
@ -31,7 +31,7 @@ mod test_examples {
|
|||
check_example_evaluates_to_expected_output,
|
||||
check_example_input_and_output_types_match_command_signature,
|
||||
};
|
||||
use nu_cmd_lang::{Break, Echo, If, Let, Mut};
|
||||
use nu_cmd_lang::{Break, Describe, Echo, If, Let, Mut};
|
||||
use nu_protocol::{
|
||||
engine::{Command, EngineState, StateWorkingSet},
|
||||
Type,
|
||||
|
@ -81,6 +81,7 @@ mod test_examples {
|
|||
working_set.add_decl(Box::new(Break));
|
||||
working_set.add_decl(Box::new(Date));
|
||||
working_set.add_decl(Box::new(Default));
|
||||
working_set.add_decl(Box::new(Describe));
|
||||
working_set.add_decl(Box::new(Each));
|
||||
working_set.add_decl(Box::new(Echo));
|
||||
working_set.add_decl(Box::new(Enumerate));
|
||||
|
|
|
@ -121,9 +121,11 @@ impl Command for Save {
|
|||
} else {
|
||||
match stderr {
|
||||
ChildPipe::Pipe(mut pipe) => {
|
||||
io::copy(&mut pipe, &mut io::sink())
|
||||
io::copy(&mut pipe, &mut io::stderr())
|
||||
}
|
||||
ChildPipe::Tee(mut tee) => {
|
||||
io::copy(&mut tee, &mut io::stderr())
|
||||
}
|
||||
ChildPipe::Tee(mut tee) => io::copy(&mut tee, &mut io::sink()),
|
||||
}
|
||||
.err_span(span)?;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@ impl Command for UMv {
|
|||
example: "mv test.txt my/subdirectory",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Move only if source file is newer than target file",
|
||||
example: "mv -u new/test.txt old/",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Move many files into a directory",
|
||||
example: "mv *.txt my/subdirectory",
|
||||
|
@ -49,6 +54,11 @@ impl Command for UMv {
|
|||
.switch("verbose", "explain what is being done.", Some('v'))
|
||||
.switch("progress", "display a progress bar", Some('p'))
|
||||
.switch("interactive", "prompt before overwriting", Some('i'))
|
||||
.switch(
|
||||
"update",
|
||||
"move and overwrite only when the SOURCE file is newer than the destination file or when the destination file is missing",
|
||||
Some('u')
|
||||
)
|
||||
.switch("no-clobber", "do not overwrite an existing file", Some('n'))
|
||||
.rest(
|
||||
"paths",
|
||||
|
@ -77,6 +87,11 @@ impl Command for UMv {
|
|||
} else {
|
||||
uu_mv::OverwriteMode::Force
|
||||
};
|
||||
let update = if call.has_flag(engine_state, stack, "update")? {
|
||||
UpdateMode::ReplaceIfOlder
|
||||
} else {
|
||||
UpdateMode::ReplaceAll
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
@ -164,7 +179,7 @@ impl Command for UMv {
|
|||
verbose,
|
||||
suffix: String::from("~"),
|
||||
backup: BackupMode::NoBackup,
|
||||
update: UpdateMode::ReplaceAll,
|
||||
update,
|
||||
target_dir: None,
|
||||
no_target_dir: false,
|
||||
strip_slashes: false,
|
||||
|
|
|
@ -61,6 +61,7 @@ impl Command for Watch {
|
|||
"Watch all directories under `<path>` recursively. Will be ignored if `<path>` is a file (default: true)",
|
||||
Some('r'),
|
||||
)
|
||||
.switch("quiet", "Hide the initial status message (default: false)", Some('q'))
|
||||
.switch("verbose", "Operate in verbose mode (default: false)", Some('v'))
|
||||
.category(Category::FileSystem)
|
||||
}
|
||||
|
@ -94,6 +95,8 @@ impl Command for Watch {
|
|||
|
||||
let verbose = call.has_flag(engine_state, stack, "verbose")?;
|
||||
|
||||
let quiet = call.has_flag(engine_state, stack, "quiet")?;
|
||||
|
||||
let debounce_duration_flag: Option<Spanned<i64>> =
|
||||
call.get_flag(engine_state, stack, "debounce-ms")?;
|
||||
let debounce_duration = match debounce_duration_flag {
|
||||
|
@ -161,7 +164,9 @@ impl Command for Watch {
|
|||
// need to cache to make sure that rename event works.
|
||||
debouncer.cache().add_root(&path, recursive_mode);
|
||||
|
||||
eprintln!("Now watching files at {path:?}. Press ctrl+c to abort.");
|
||||
if !quiet {
|
||||
eprintln!("Now watching files at {path:?}. Press ctrl+c to abort.");
|
||||
}
|
||||
|
||||
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||
|
||||
|
|
161
crates/nu-command/src/filters/chunks.rs
Normal file
161
crates/nu-command/src/filters/chunks.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ListStream;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chunks;
|
||||
|
||||
impl Command for Chunks {
|
||||
fn name(&self) -> &str {
|
||||
"chunks"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("chunks")
|
||||
.input_output_types(vec![
|
||||
(Type::table(), Type::list(Type::table())),
|
||||
(Type::list(Type::Any), Type::list(Type::list(Type::Any))),
|
||||
])
|
||||
.required("chunk_size", SyntaxShape::Int, "The size of each chunk.")
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Divide a list or table into chunks of `chunk_size`."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"This command will error if `chunk_size` is negative or zero."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["batch", "group"]
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
example: "[1 2 3 4] | chunks 2",
|
||||
description: "Chunk a list into pairs",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
|
||||
Value::test_list(vec![Value::test_int(3), Value::test_int(4)]),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
example: "[[foo bar]; [0 1] [2 3] [4 5] [6 7] [8 9]] | chunks 3",
|
||||
description: "Chunk the rows of a table into triplets",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"foo" => Value::test_int(0),
|
||||
"bar" => Value::test_int(1),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"foo" => Value::test_int(2),
|
||||
"bar" => Value::test_int(3),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"foo" => Value::test_int(4),
|
||||
"bar" => Value::test_int(5),
|
||||
}),
|
||||
]),
|
||||
Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"foo" => Value::test_int(6),
|
||||
"bar" => Value::test_int(7),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"foo" => Value::test_int(8),
|
||||
"bar" => Value::test_int(9),
|
||||
}),
|
||||
]),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let chunk_size: Value = call.req(engine_state, stack, 0)?;
|
||||
|
||||
let size =
|
||||
usize::try_from(chunk_size.as_int()?).map_err(|_| ShellError::NeedsPositiveValue {
|
||||
span: chunk_size.span(),
|
||||
})?;
|
||||
|
||||
let size = NonZeroUsize::try_from(size).map_err(|_| ShellError::IncorrectValue {
|
||||
msg: "`chunk_size` cannot be zero".into(),
|
||||
val_span: chunk_size.span(),
|
||||
call_span: head,
|
||||
})?;
|
||||
|
||||
chunks(engine_state, input, size, head)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunks(
|
||||
engine_state: &EngineState,
|
||||
input: PipelineData,
|
||||
chunk_size: NonZeroUsize,
|
||||
span: Span,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
match input {
|
||||
PipelineData::Value(Value::List { vals, .. }, metadata) => {
|
||||
let chunks = ChunksIter::new(vals, chunk_size, span);
|
||||
let stream = ListStream::new(chunks, span, engine_state.signals().clone());
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
PipelineData::ListStream(stream, metadata) => {
|
||||
let stream = stream.modify(|iter| ChunksIter::new(iter, chunk_size, span));
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
input => Err(input.unsupported_input_error("list", span)),
|
||||
}
|
||||
}
|
||||
|
||||
struct ChunksIter<I: Iterator<Item = Value>> {
|
||||
iter: I,
|
||||
size: usize,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Value>> ChunksIter<I> {
|
||||
fn new(iter: impl IntoIterator<IntoIter = I>, size: NonZeroUsize, span: Span) -> Self {
|
||||
Self {
|
||||
iter: iter.into_iter(),
|
||||
size: size.into(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Value>> Iterator for ChunksIter<I> {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let first = self.iter.next()?;
|
||||
let mut chunk = Vec::with_capacity(self.size); // delay allocation to optimize for empty iter
|
||||
chunk.push(first);
|
||||
chunk.extend((&mut self.iter).take(self.size - 1));
|
||||
Some(Value::list(chunk, self.span))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Chunks {})
|
||||
}
|
||||
}
|
|
@ -51,11 +51,11 @@ impl Command for Default {
|
|||
description:
|
||||
"Get the env value of `MY_ENV` with a default value 'abc' if not present",
|
||||
example: "$env | get --ignore-errors MY_ENV | default 'abc'",
|
||||
result: None, // Some(Value::test_string("abc")),
|
||||
result: Some(Value::test_string("abc")),
|
||||
},
|
||||
Example {
|
||||
description: "Replace the `null` value in a list",
|
||||
example: "[1, 2, null, 4] | default 3",
|
||||
example: "[1, 2, null, 4] | each { default 3 }",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
Value::test_int(1),
|
||||
|
@ -113,15 +113,7 @@ fn default(
|
|||
} else if input.is_nothing() {
|
||||
Ok(value.into_pipeline_data())
|
||||
} else {
|
||||
input
|
||||
.map(
|
||||
move |item| match item {
|
||||
Value::Nothing { .. } => value.clone(),
|
||||
x => x,
|
||||
},
|
||||
engine_state.signals(),
|
||||
)
|
||||
.map(|x| x.set_metadata(metadata))
|
||||
Ok(input)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -132,8 +132,6 @@ with 'transpose' first."#
|
|||
Ok(data) => Some(data.into_value(head).unwrap_or_else(|err| {
|
||||
Value::error(chain_error_with_input(err, is_error, span), span)
|
||||
})),
|
||||
Err(ShellError::Continue { span }) => Some(Value::nothing(span)),
|
||||
Err(ShellError::Break { .. }) => None,
|
||||
Err(error) => {
|
||||
let error = chain_error_with_input(error, is_error, span);
|
||||
Some(Value::error(error, span))
|
||||
|
@ -149,10 +147,6 @@ with 'transpose' first."#
|
|||
.map_while(move |value| {
|
||||
let value = match value {
|
||||
Ok(value) => value,
|
||||
Err(ShellError::Continue { span }) => {
|
||||
return Some(Value::nothing(span))
|
||||
}
|
||||
Err(ShellError::Break { .. }) => return None,
|
||||
Err(err) => return Some(Value::error(err, head)),
|
||||
};
|
||||
|
||||
|
@ -163,8 +157,6 @@ with 'transpose' first."#
|
|||
.and_then(|data| data.into_value(head))
|
||||
{
|
||||
Ok(value) => Some(value),
|
||||
Err(ShellError::Continue { span }) => Some(Value::nothing(span)),
|
||||
Err(ShellError::Break { .. }) => None,
|
||||
Err(error) => {
|
||||
let error = chain_error_with_input(error, is_error, span);
|
||||
Some(Value::error(error, span))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ValueIterator;
|
||||
use nu_protocol::{report_warning_new, ParseWarning, ValueIterator};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group;
|
||||
|
@ -54,6 +54,17 @@ impl Command for Group {
|
|||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
|
||||
report_warning_new(
|
||||
engine_state,
|
||||
&ParseWarning::DeprecatedWarning {
|
||||
old_command: "group".into(),
|
||||
new_suggestion: "the new `chunks` command".into(),
|
||||
span: head,
|
||||
url: "`help chunks`".into(),
|
||||
},
|
||||
);
|
||||
|
||||
let group_size: Spanned<usize> = call.req(engine_state, stack, 0)?;
|
||||
let metadata = input.metadata();
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ impl Command for Items {
|
|||
|
||||
match result {
|
||||
Ok(value) => Some(value),
|
||||
Err(ShellError::Break { .. }) => None,
|
||||
Err(err) => {
|
||||
let err = chain_error_with_input(err, false, span);
|
||||
Some(Value::error(err, head))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod all;
|
||||
mod any;
|
||||
mod append;
|
||||
mod chunks;
|
||||
mod columns;
|
||||
mod compact;
|
||||
mod default;
|
||||
|
@ -58,6 +59,7 @@ mod zip;
|
|||
pub use all::All;
|
||||
pub use any::Any;
|
||||
pub use append::Append;
|
||||
pub use chunks::Chunks;
|
||||
pub use columns::Columns;
|
||||
pub use compact::Compact;
|
||||
pub use default::Default;
|
||||
|
|
|
@ -36,7 +36,7 @@ impl Command for Reduce {
|
|||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Aggregate a list to a single value using an accumulator closure."
|
||||
"Aggregate a list (starting from the left) to a single value using an accumulator closure."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
|
@ -50,6 +50,11 @@ impl Command for Reduce {
|
|||
description: "Sum values of a list (same as 'math sum')",
|
||||
result: Some(Value::test_int(10)),
|
||||
},
|
||||
Example {
|
||||
example: "[ 1 2 3 4 ] | reduce {|it, acc| $acc - $it }",
|
||||
description: r#"`reduce` accumulates value from left to right, equivalent to (((1 - 2) - 3) - 4)."#,
|
||||
result: Some(Value::test_int(-8)),
|
||||
},
|
||||
Example {
|
||||
example:
|
||||
"[ 8 7 6 ] | enumerate | reduce --fold 0 {|it, acc| $acc + $it.item + $it.index }",
|
||||
|
@ -61,6 +66,11 @@ impl Command for Reduce {
|
|||
description: "Sum values with a starting value (fold)",
|
||||
result: Some(Value::test_int(20)),
|
||||
},
|
||||
Example {
|
||||
example: r#"[[foo baz] [baz quux]] | reduce --fold "foobar" {|it, acc| $acc | str replace $it.0 $it.1}"#,
|
||||
description: "Iteratively perform string replace (from left to right): 'foobar' -> 'bazbar' -> 'quuxbar'",
|
||||
result: Some(Value::test_string("quuxbar")),
|
||||
},
|
||||
Example {
|
||||
example: r#"[ i o t ] | reduce --fold "Arthur, King of the Britons" {|it, acc| $acc | str replace --all $it "X" }"#,
|
||||
description: "Replace selected characters in a string with 'X'",
|
||||
|
@ -110,8 +120,7 @@ impl Command for Reduce {
|
|||
engine_state.signals().check(head)?;
|
||||
acc = closure
|
||||
.add_arg(value)
|
||||
.add_arg(acc)
|
||||
.run_with_input(PipelineData::Empty)?
|
||||
.run_with_value(acc)?
|
||||
.into_value(head)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ fn select(
|
|||
//FIXME: improve implementation to not clone
|
||||
match input_val.clone().follow_cell_path(&path.members, false) {
|
||||
Ok(fetcher) => {
|
||||
record.push(path.to_string().replace('.', "_"), fetcher);
|
||||
record.push(path.to_string(), fetcher);
|
||||
if !columns_with_value.contains(&path) {
|
||||
columns_with_value.push(path);
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ fn select(
|
|||
// FIXME: remove clone
|
||||
match v.clone().follow_cell_path(&cell_path.members, false) {
|
||||
Ok(result) => {
|
||||
record.push(cell_path.to_string().replace('.', "_"), result);
|
||||
record.push(cell_path.to_string(), result);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ fn select(
|
|||
//FIXME: improve implementation to not clone
|
||||
match x.clone().follow_cell_path(&path.members, false) {
|
||||
Ok(value) => {
|
||||
record.push(path.to_string().replace('.', "_"), value);
|
||||
record.push(path.to_string(), value);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ValueIterator;
|
||||
use nu_protocol::ListStream;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Window;
|
||||
|
@ -12,8 +13,8 @@ impl Command for Window {
|
|||
fn signature(&self) -> Signature {
|
||||
Signature::build("window")
|
||||
.input_output_types(vec![(
|
||||
Type::List(Box::new(Type::Any)),
|
||||
Type::List(Box::new(Type::List(Box::new(Type::Any)))),
|
||||
Type::list(Type::Any),
|
||||
Type::list(Type::list(Type::Any)),
|
||||
)])
|
||||
.required("window_size", SyntaxShape::Int, "The size of each window.")
|
||||
.named(
|
||||
|
@ -34,72 +35,41 @@ impl Command for Window {
|
|||
"Creates a sliding window of `window_size` that slide by n rows/elements across input."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"This command will error if `window_size` or `stride` are negative or zero."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let stream_test_1 = vec![
|
||||
Value::list(
|
||||
vec![Value::test_int(1), Value::test_int(2)],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::list(
|
||||
vec![Value::test_int(2), Value::test_int(3)],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::list(
|
||||
vec![Value::test_int(3), Value::test_int(4)],
|
||||
Span::test_data(),
|
||||
),
|
||||
];
|
||||
|
||||
let stream_test_2 = vec![
|
||||
Value::list(
|
||||
vec![Value::test_int(1), Value::test_int(2)],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::list(
|
||||
vec![Value::test_int(4), Value::test_int(5)],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::list(
|
||||
vec![Value::test_int(7), Value::test_int(8)],
|
||||
Span::test_data(),
|
||||
),
|
||||
];
|
||||
|
||||
let stream_test_3 = vec![
|
||||
Value::list(
|
||||
vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
Span::test_data(),
|
||||
),
|
||||
Value::list(
|
||||
vec![Value::test_int(4), Value::test_int(5)],
|
||||
Span::test_data(),
|
||||
),
|
||||
];
|
||||
|
||||
vec![
|
||||
Example {
|
||||
example: "[1 2 3 4] | window 2",
|
||||
description: "A sliding window of two elements",
|
||||
result: Some(Value::list(
|
||||
stream_test_1,
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
|
||||
Value::test_list(vec![Value::test_int(2), Value::test_int(3)]),
|
||||
Value::test_list(vec![Value::test_int(3), Value::test_int(4)]),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
example: "[1, 2, 3, 4, 5, 6, 7, 8] | window 2 --stride 3",
|
||||
description: "A sliding window of two elements, with a stride of 3",
|
||||
result: Some(Value::list(
|
||||
stream_test_2,
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_list(vec![Value::test_int(1), Value::test_int(2)]),
|
||||
Value::test_list(vec![Value::test_int(4), Value::test_int(5)]),
|
||||
Value::test_list(vec![Value::test_int(7), Value::test_int(8)]),
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
example: "[1, 2, 3, 4, 5] | window 3 --stride 3 --remainder",
|
||||
description: "A sliding window of equal stride that includes remainder. Equivalent to chunking",
|
||||
result: Some(Value::list(
|
||||
stream_test_3,
|
||||
Span::test_data(),
|
||||
)),
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_list(vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(2),
|
||||
Value::test_int(3),
|
||||
]),
|
||||
Value::test_list(vec![Value::test_int(4), Value::test_int(5)]),
|
||||
])),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -112,116 +82,169 @@ impl Command for Window {
|
|||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let group_size: Spanned<usize> = call.req(engine_state, stack, 0)?;
|
||||
let metadata = input.metadata();
|
||||
let stride: Option<usize> = call.get_flag(engine_state, stack, "stride")?;
|
||||
let window_size: Value = call.req(engine_state, stack, 0)?;
|
||||
let stride: Option<Value> = call.get_flag(engine_state, stack, "stride")?;
|
||||
let remainder = call.has_flag(engine_state, stack, "remainder")?;
|
||||
|
||||
let stride = stride.unwrap_or(1);
|
||||
let size =
|
||||
usize::try_from(window_size.as_int()?).map_err(|_| ShellError::NeedsPositiveValue {
|
||||
span: window_size.span(),
|
||||
})?;
|
||||
|
||||
//FIXME: add in support for external redirection when engine-q supports it generally
|
||||
let size = NonZeroUsize::try_from(size).map_err(|_| ShellError::IncorrectValue {
|
||||
msg: "`window_size` cannot be zero".into(),
|
||||
val_span: window_size.span(),
|
||||
call_span: head,
|
||||
})?;
|
||||
|
||||
let each_group_iterator = EachWindowIterator {
|
||||
group_size: group_size.item,
|
||||
input: Box::new(input.into_iter()),
|
||||
span: head,
|
||||
previous: None,
|
||||
stride,
|
||||
remainder,
|
||||
let stride = if let Some(stride_val) = stride {
|
||||
let stride = usize::try_from(stride_val.as_int()?).map_err(|_| {
|
||||
ShellError::NeedsPositiveValue {
|
||||
span: stride_val.span(),
|
||||
}
|
||||
})?;
|
||||
|
||||
NonZeroUsize::try_from(stride).map_err(|_| ShellError::IncorrectValue {
|
||||
msg: "`stride` cannot be zero".into(),
|
||||
val_span: stride_val.span(),
|
||||
call_span: head,
|
||||
})?
|
||||
} else {
|
||||
NonZeroUsize::MIN
|
||||
};
|
||||
|
||||
Ok(each_group_iterator.into_pipeline_data_with_metadata(
|
||||
head,
|
||||
engine_state.signals().clone(),
|
||||
metadata,
|
||||
))
|
||||
if remainder && size == stride {
|
||||
super::chunks::chunks(engine_state, input, size, head)
|
||||
} else if stride >= size {
|
||||
match input {
|
||||
PipelineData::Value(Value::List { vals, .. }, metadata) => {
|
||||
let chunks = WindowGapIter::new(vals, size, stride, remainder, head);
|
||||
let stream = ListStream::new(chunks, head, engine_state.signals().clone());
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
PipelineData::ListStream(stream, metadata) => {
|
||||
let stream = stream
|
||||
.modify(|iter| WindowGapIter::new(iter, size, stride, remainder, head));
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
input => Err(input.unsupported_input_error("list", head)),
|
||||
}
|
||||
} else {
|
||||
match input {
|
||||
PipelineData::Value(Value::List { vals, .. }, metadata) => {
|
||||
let chunks = WindowOverlapIter::new(vals, size, stride, remainder, head);
|
||||
let stream = ListStream::new(chunks, head, engine_state.signals().clone());
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
PipelineData::ListStream(stream, metadata) => {
|
||||
let stream = stream
|
||||
.modify(|iter| WindowOverlapIter::new(iter, size, stride, remainder, head));
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
input => Err(input.unsupported_input_error("list", head)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EachWindowIterator {
|
||||
group_size: usize,
|
||||
input: ValueIterator,
|
||||
span: Span,
|
||||
previous: Option<Vec<Value>>,
|
||||
struct WindowOverlapIter<I: Iterator<Item = Value>> {
|
||||
iter: I,
|
||||
window: Vec<Value>,
|
||||
stride: usize,
|
||||
remainder: bool,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl Iterator for EachWindowIterator {
|
||||
impl<I: Iterator<Item = Value>> WindowOverlapIter<I> {
|
||||
fn new(
|
||||
iter: impl IntoIterator<IntoIter = I>,
|
||||
size: NonZeroUsize,
|
||||
stride: NonZeroUsize,
|
||||
remainder: bool,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
Self {
|
||||
iter: iter.into_iter(),
|
||||
window: Vec::with_capacity(size.into()),
|
||||
stride: stride.into(),
|
||||
remainder,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Value>> Iterator for WindowOverlapIter<I> {
|
||||
type Item = Value;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut group = self.previous.take().unwrap_or_else(|| {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
// We default to a Vec of capacity size + stride as striding pushes n extra elements to the end
|
||||
vec.try_reserve(self.group_size + self.stride).ok();
|
||||
|
||||
vec
|
||||
});
|
||||
let mut current_count = 0;
|
||||
|
||||
if group.is_empty() {
|
||||
loop {
|
||||
let item = self.input.next();
|
||||
|
||||
match item {
|
||||
Some(v) => {
|
||||
group.push(v);
|
||||
|
||||
current_count += 1;
|
||||
if current_count >= self.group_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if self.remainder {
|
||||
break;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let len = if self.window.is_empty() {
|
||||
self.window.capacity()
|
||||
} else {
|
||||
// our historic buffer is already full, so stride instead
|
||||
self.stride
|
||||
};
|
||||
|
||||
loop {
|
||||
let item = self.input.next();
|
||||
self.window.extend((&mut self.iter).take(len));
|
||||
|
||||
match item {
|
||||
Some(v) => {
|
||||
group.push(v);
|
||||
|
||||
current_count += 1;
|
||||
if current_count >= self.stride {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if self.remainder {
|
||||
break;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now have elements + stride in our group, and need to
|
||||
// drop the skipped elements. Drain to preserve allocation and capacity
|
||||
// Dropping this iterator consumes it.
|
||||
group.drain(..self.stride.min(group.len()));
|
||||
if self.window.len() == self.window.capacity()
|
||||
|| (self.remainder && !self.window.is_empty())
|
||||
{
|
||||
let mut next = Vec::with_capacity(self.window.len());
|
||||
next.extend(self.window.iter().skip(self.stride).cloned());
|
||||
let window = std::mem::replace(&mut self.window, next);
|
||||
Some(Value::list(window, self.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if group.is_empty() {
|
||||
return None;
|
||||
struct WindowGapIter<I: Iterator<Item = Value>> {
|
||||
iter: I,
|
||||
size: usize,
|
||||
skip: usize,
|
||||
first: bool,
|
||||
remainder: bool,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Value>> WindowGapIter<I> {
|
||||
fn new(
|
||||
iter: impl IntoIterator<IntoIter = I>,
|
||||
size: NonZeroUsize,
|
||||
stride: NonZeroUsize,
|
||||
remainder: bool,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
let size = size.into();
|
||||
Self {
|
||||
iter: iter.into_iter(),
|
||||
size,
|
||||
skip: stride.get() - size,
|
||||
first: true,
|
||||
remainder,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let return_group = group.clone();
|
||||
self.previous = Some(group);
|
||||
impl<I: Iterator<Item = Value>> Iterator for WindowGapIter<I> {
|
||||
type Item = Value;
|
||||
|
||||
Some(Value::list(return_group, self.span))
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut window = Vec::with_capacity(self.size);
|
||||
window.extend(
|
||||
(&mut self.iter)
|
||||
.skip(if self.first { 0 } else { self.skip })
|
||||
.take(self.size),
|
||||
);
|
||||
|
||||
self.first = false;
|
||||
|
||||
if window.len() == self.size || (self.remainder && !window.is_empty()) {
|
||||
Some(Value::list(window, self.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,12 @@ impl Command for Generate {
|
|||
fn signature(&self) -> Signature {
|
||||
Signature::build("generate")
|
||||
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::Any)))])
|
||||
.required("initial", SyntaxShape::Any, "Initial value.")
|
||||
.required(
|
||||
"closure",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||
"Generator function.",
|
||||
)
|
||||
.optional("initial", SyntaxShape::Any, "Initial value.")
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Generators)
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
example: "generate 0 {|i| if $i <= 10 { {out: $i, next: ($i + 2)} }}",
|
||||
example: "generate {|i| if $i <= 10 { {out: $i, next: ($i + 2)} }} 0",
|
||||
description: "Generate a sequence of numbers",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
|
@ -57,10 +57,17 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
},
|
||||
Example {
|
||||
example:
|
||||
"generate [0, 1] {|fib| {out: $fib.0, next: [$fib.1, ($fib.0 + $fib.1)]} }",
|
||||
"generate {|fib| {out: $fib.0, next: [$fib.1, ($fib.0 + $fib.1)]} } [0, 1]",
|
||||
description: "Generate a continuous stream of Fibonacci numbers",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
example:
|
||||
"generate {|fib=[0, 1]| {out: $fib.0, next: [$fib.1, ($fib.0 + $fib.1)]} }",
|
||||
description:
|
||||
"Generate a continuous stream of Fibonacci numbers, using default parameters",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -72,15 +79,15 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let initial: Value = call.req(engine_state, stack, 0)?;
|
||||
let closure: Closure = call.req(engine_state, stack, 1)?;
|
||||
|
||||
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||
let initial: Option<Value> = call.opt(engine_state, stack, 1)?;
|
||||
let block = engine_state.get_block(closure.block_id);
|
||||
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||
|
||||
// A type of Option<S> is used to represent state. Invocation
|
||||
// will stop on None. Using Option<S> allows functions to output
|
||||
// one final value before stopping.
|
||||
let mut state = Some(initial);
|
||||
let mut state = Some(get_initial_state(initial, &block.signature, call.head)?);
|
||||
let iter = std::iter::from_fn(move || {
|
||||
let arg = state.take()?;
|
||||
|
||||
|
@ -170,6 +177,38 @@ used as the next argument to the closure, otherwise generation stops.
|
|||
}
|
||||
}
|
||||
|
||||
fn get_initial_state(
|
||||
initial: Option<Value>,
|
||||
signature: &Signature,
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
match initial {
|
||||
Some(v) => Ok(v),
|
||||
None => {
|
||||
// the initial state should be referred from signature
|
||||
if !signature.optional_positional.is_empty()
|
||||
&& signature.optional_positional[0].default_value.is_some()
|
||||
{
|
||||
Ok(signature.optional_positional[0]
|
||||
.default_value
|
||||
.clone()
|
||||
.expect("Already checked default value"))
|
||||
} else {
|
||||
Err(ShellError::GenericError {
|
||||
error: "The initial value is missing".to_string(),
|
||||
msg: "Missing initial value".to_string(),
|
||||
span: Some(span),
|
||||
help: Some(
|
||||
"Provide an <initial> value as an argument to generate, or assign a default value to the closure parameter"
|
||||
.to_string(),
|
||||
),
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod bytes;
|
||||
mod charting;
|
||||
mod conversions;
|
||||
|
|
|
@ -29,6 +29,10 @@ impl Command for SubCommand {
|
|||
vec!["square", "root"]
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -44,6 +48,23 @@ impl Command for SubCommand {
|
|||
input.map(move |value| operate(value, head), engine_state.signals())
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
// This doesn't match explicit nulls
|
||||
if matches!(input, PipelineData::Empty) {
|
||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||
}
|
||||
input.map(
|
||||
move |value| operate(value, head),
|
||||
working_set.permanent().signals(),
|
||||
)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Compute the square root of each number in a list",
|
||||
|
|
|
@ -141,17 +141,17 @@ pub fn request_add_authorization_header(
|
|||
let login = match (user, password) {
|
||||
(Some(user), Some(password)) => {
|
||||
let mut enc_str = String::new();
|
||||
base64_engine.encode_string(&format!("{user}:{password}"), &mut enc_str);
|
||||
base64_engine.encode_string(format!("{user}:{password}"), &mut enc_str);
|
||||
Some(enc_str)
|
||||
}
|
||||
(Some(user), _) => {
|
||||
let mut enc_str = String::new();
|
||||
base64_engine.encode_string(&format!("{user}:"), &mut enc_str);
|
||||
base64_engine.encode_string(format!("{user}:"), &mut enc_str);
|
||||
Some(enc_str)
|
||||
}
|
||||
(_, Some(password)) => {
|
||||
let mut enc_str = String::new();
|
||||
base64_engine.encode_string(&format!(":{password}"), &mut enc_str);
|
||||
base64_engine.encode_string(format!(":{password}"), &mut enc_str);
|
||||
Some(enc_str)
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
@ -15,7 +15,11 @@ impl Command for SubCommand {
|
|||
Signature::build("random int")
|
||||
.input_output_types(vec![(Type::Nothing, Type::Int)])
|
||||
.allow_variants_without_examples(true)
|
||||
.optional("range", SyntaxShape::Range, "Range of values.")
|
||||
.optional(
|
||||
"range",
|
||||
SyntaxShape::Range,
|
||||
"Range of potential values, inclusive of both start and end values.",
|
||||
)
|
||||
.category(Category::Random)
|
||||
}
|
||||
|
||||
|
@ -40,12 +44,12 @@ impl Command for SubCommand {
|
|||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Generate an unconstrained random integer",
|
||||
description: "Generate a non-negative random integer",
|
||||
example: "random int",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Generate a random integer less than or equal to 500",
|
||||
description: "Generate a random integer between 0 (inclusive) and 500 (inclusive)",
|
||||
example: "random int ..500",
|
||||
result: None,
|
||||
},
|
||||
|
@ -55,8 +59,8 @@ impl Command for SubCommand {
|
|||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Generate a random integer between 1 and 10",
|
||||
example: "random int 1..10",
|
||||
description: "Generate a random integer between -10 (inclusive) and 10 (inclusive)",
|
||||
example: "random int (-10)..10",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@ use nu_engine::command_prelude::*;
|
|||
|
||||
use nu_protocol::Signals;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashSet;
|
||||
|
||||
// Character used to separate directories in a Path Environment variable on windows is ";"
|
||||
#[cfg(target_family = "windows")]
|
||||
|
@ -149,6 +150,24 @@ static CHAR_MAP: Lazy<IndexMap<&'static str, String>> = Lazy::new(|| {
|
|||
}
|
||||
});
|
||||
|
||||
static NO_OUTPUT_CHARS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
||||
[
|
||||
// If the character is in the this set, we don't output it to prevent
|
||||
// the broken of `char --list` command table format and alignment.
|
||||
"newline",
|
||||
"enter",
|
||||
"nl",
|
||||
"line_feed",
|
||||
"lf",
|
||||
"cr",
|
||||
"crlf",
|
||||
"bel",
|
||||
"backspace",
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
});
|
||||
|
||||
impl Command for Char {
|
||||
fn name(&self) -> &str {
|
||||
"char"
|
||||
|
@ -297,6 +316,11 @@ fn generate_character_list(signals: Signals, call_span: Span) -> PipelineData {
|
|||
CHAR_MAP
|
||||
.iter()
|
||||
.map(move |(name, s)| {
|
||||
let character = if NO_OUTPUT_CHARS.contains(name) {
|
||||
Value::string("", call_span)
|
||||
} else {
|
||||
Value::string(s, call_span)
|
||||
};
|
||||
let unicode = Value::string(
|
||||
s.chars()
|
||||
.map(|c| format!("{:x}", c as u32))
|
||||
|
@ -306,7 +330,7 @@ fn generate_character_list(signals: Signals, call_span: Span) -> PipelineData {
|
|||
);
|
||||
let record = record! {
|
||||
"name" => Value::string(*name, call_span),
|
||||
"character" => Value::string(s, call_span),
|
||||
"character" => character,
|
||||
"unicode" => unicode,
|
||||
};
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ fn split_words_helper(v: &Value, word_length: Option<usize>, span: Span, graphem
|
|||
// [^[:alpha:]\'] = do not match any uppercase or lowercase letters or apostrophes
|
||||
// [^\p{L}\'] = do not match any unicode uppercase or lowercase letters or apostrophes
|
||||
// Let's go with the unicode one in hopes that it works on more than just ascii characters
|
||||
let regex_replace = Regex::new(r"[^\p{L}\']").expect("regular expression error");
|
||||
let regex_replace = Regex::new(r"[^\p{L}\p{N}\']").expect("regular expression error");
|
||||
let v_span = v.span();
|
||||
|
||||
match v {
|
||||
|
@ -422,4 +422,9 @@ mod test {
|
|||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
#[test]
|
||||
fn mixed_letter_number() {
|
||||
let actual = nu!(r#"echo "a1 b2 c3" | split words | str join ','"#);
|
||||
assert_eq!(actual.out, "a1,b2,c3");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -606,7 +606,7 @@ mod test {
|
|||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = expand_glob("~/foo.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
||||
let home = dirs_next::home_dir().expect("failed to get home dir");
|
||||
let home = dirs::home_dir().expect("failed to get home dir");
|
||||
let expected: Vec<OsString> = vec![home.join("foo.txt").into()];
|
||||
assert_eq!(actual, expected);
|
||||
})
|
||||
|
|
|
@ -15,12 +15,3 @@ fn break_while_loop() {
|
|||
|
||||
assert_eq!(actual.out, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn break_each() {
|
||||
let actual = nu!("
|
||||
[1, 2, 3, 4, 5] | each {|x| if $x > 3 { break }; $x} | math sum
|
||||
");
|
||||
|
||||
assert_eq!(actual.out, "6");
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ fn filesystem_change_to_home_directory() {
|
|||
"
|
||||
);
|
||||
|
||||
assert_eq!(Some(PathBuf::from(actual.out)), dirs_next::home_dir());
|
||||
assert_eq!(Some(PathBuf::from(actual.out)), dirs::home_dir());
|
||||
})
|
||||
}
|
||||
|
||||
|
|
43
crates/nu-command/tests/commands/chunks.rs
Normal file
43
crates/nu-command/tests/commands/chunks.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn chunk_size_negative() {
|
||||
let actual = nu!("[0 1 2] | chunks -1");
|
||||
assert!(actual.err.contains("positive"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_size_zero() {
|
||||
let actual = nu!("[0 1 2] | chunks 0");
|
||||
assert!(actual.err.contains("zero"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_size_not_int() {
|
||||
let actual = nu!("[0 1 2] | chunks (if true { 1sec })");
|
||||
assert!(actual.err.contains("can't convert"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let actual = nu!("[] | chunks 2 | is-empty");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_stream() {
|
||||
let actual = nu!("([0 1 2] | every 1 | chunks 2) == ([0 1 2] | chunks 2)");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_stream() {
|
||||
let actual = nu!("([[foo bar]; [0 1] [2 3] [4 5]] | every 1 | chunks 2) == ([[foo bar]; [0 1] [2 3] [4 5]] | chunks 2)");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_empty_chunks() {
|
||||
let actual = nu!("([0 1 2 3 4 5] | chunks 3 | length) == 2");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
|
@ -95,13 +95,13 @@ fn capture_error_with_both_stdout_stderr_messages_not_hang_nushell() {
|
|||
|
||||
#[test]
|
||||
fn combined_pipe_redirection() {
|
||||
let actual = nu!("$env.FOO = hello; $env.BAR = world; nu --testbin echo_env_mixed out-err FOO BAR o+e>| complete | get stdout");
|
||||
let actual = nu!("$env.FOO = 'hello'; $env.BAR = 'world'; nu --testbin echo_env_mixed out-err FOO BAR o+e>| complete | get stdout");
|
||||
assert_eq!(actual.out, "helloworld");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn err_pipe_redirection() {
|
||||
let actual =
|
||||
nu!("$env.FOO = hello; nu --testbin echo_env_stderr FOO e>| complete | get stdout");
|
||||
nu!("$env.FOO = 'hello'; nu --testbin echo_env_stderr FOO e>| complete | get stdout");
|
||||
assert_eq!(actual.out, "hello");
|
||||
}
|
||||
|
|
|
@ -32,3 +32,15 @@ fn default_after_empty_filter() {
|
|||
|
||||
assert_eq!(actual.out, "d");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_nulls_in_lists() {
|
||||
let actual = nu!(r#"[null, 2, 3] | default [] | to json -r"#);
|
||||
assert_eq!(actual.out, "[null,2,3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replaces_null() {
|
||||
let actual = nu!(r#"null | default 1"#);
|
||||
assert_eq!(actual.out, "1");
|
||||
}
|
||||
|
|
|
@ -58,22 +58,6 @@ fn each_while_uses_enumerate_index() {
|
|||
assert_eq!(actual.out, "[0, 1, 2, 3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_element_continue_command() {
|
||||
let actual =
|
||||
nu!("[1,2,3,4,6,7] | each { |x| if ($x mod 2 == 0) {continue} else { $x }} | to nuon");
|
||||
|
||||
assert_eq!(actual.out, "[1, 3, 7]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_element_break_command() {
|
||||
let actual =
|
||||
nu!("[1,2,5,4,6,7] | each { |x| if ($x mod 3 == 0) {break} else { $x }} | to nuon");
|
||||
|
||||
assert_eq!(actual.out, "[1, 2, 5, 4]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_in_nested_each_show() {
|
||||
let actual = nu!("[[1,2]] | each {|x| $x | each {|y| error make {msg: \"oh noes\"} } }");
|
||||
|
|
|
@ -3,7 +3,7 @@ use nu_test_support::{nu, pipeline};
|
|||
#[test]
|
||||
fn generate_no_next_break() {
|
||||
let actual = nu!(
|
||||
"generate 1 {|x| if $x == 3 { {out: $x}} else { {out: $x, next: ($x + 1)} }} | to nuon"
|
||||
"generate {|x| if $x == 3 { {out: $x}} else { {out: $x, next: ($x + 1)} }} 1 | to nuon"
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "[1, 2, 3]");
|
||||
|
@ -11,7 +11,7 @@ fn generate_no_next_break() {
|
|||
|
||||
#[test]
|
||||
fn generate_null_break() {
|
||||
let actual = nu!("generate 1 {|x| if $x <= 3 { {out: $x, next: ($x + 1)} }} | to nuon");
|
||||
let actual = nu!("generate {|x| if $x <= 3 { {out: $x, next: ($x + 1)} }} 1 | to nuon");
|
||||
|
||||
assert_eq!(actual.out, "[1, 2, 3]");
|
||||
}
|
||||
|
@ -20,13 +20,13 @@ fn generate_null_break() {
|
|||
fn generate_allows_empty_output() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate 0 {|x|
|
||||
generate {|x|
|
||||
if $x == 1 {
|
||||
{next: ($x + 1)}
|
||||
} else if $x < 3 {
|
||||
{out: $x, next: ($x + 1)}
|
||||
}
|
||||
} | to nuon
|
||||
} 0 | to nuon
|
||||
"#
|
||||
));
|
||||
|
||||
|
@ -37,11 +37,11 @@ fn generate_allows_empty_output() {
|
|||
fn generate_allows_no_output() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate 0 {|x|
|
||||
generate {|x|
|
||||
if $x < 3 {
|
||||
{next: ($x + 1)}
|
||||
}
|
||||
} | to nuon
|
||||
} 0 | to nuon
|
||||
"#
|
||||
));
|
||||
|
||||
|
@ -52,7 +52,7 @@ fn generate_allows_no_output() {
|
|||
fn generate_allows_null_state() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate 0 {|x|
|
||||
generate {|x|
|
||||
if $x == null {
|
||||
{out: "done"}
|
||||
} else if $x < 1 {
|
||||
|
@ -60,7 +60,7 @@ fn generate_allows_null_state() {
|
|||
} else {
|
||||
{out: "stopping", next: null}
|
||||
}
|
||||
} | to nuon
|
||||
} 0 | to nuon
|
||||
"#
|
||||
));
|
||||
|
||||
|
@ -71,7 +71,42 @@ fn generate_allows_null_state() {
|
|||
fn generate_allows_null_output() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate 0 {|x|
|
||||
generate {|x|
|
||||
if $x == 3 {
|
||||
{out: "done"}
|
||||
} else {
|
||||
{out: null, next: ($x + 1)}
|
||||
}
|
||||
} 0 | to nuon
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "[null, null, null, done]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_extra_keys() {
|
||||
let actual = nu!("generate {|x| {foo: bar, out: $x}} 0 ");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_list() {
|
||||
let actual = nu!("generate {|x| [$x, ($x + 1)]} 0 ");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_primitive() {
|
||||
let actual = nu!("generate {|x| 1} 0");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_allow_default_parameter() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate {|x = 0|
|
||||
if $x == 3 {
|
||||
{out: "done"}
|
||||
} else {
|
||||
|
@ -82,22 +117,34 @@ fn generate_allows_null_output() {
|
|||
));
|
||||
|
||||
assert_eq!(actual.out, "[null, null, null, done]");
|
||||
|
||||
// if initial is given, use initial value
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate {|x = 0|
|
||||
if $x == 3 {
|
||||
{out: "done"}
|
||||
} else {
|
||||
{out: null, next: ($x + 1)}
|
||||
}
|
||||
} 1 | to nuon
|
||||
"#
|
||||
));
|
||||
assert_eq!(actual.out, "[null, null, done]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_extra_keys() {
|
||||
let actual = nu!("generate 0 {|x| {foo: bar, out: $x}}");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_list() {
|
||||
let actual = nu!("generate 0 {|x| [$x, ($x + 1)]}");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_disallows_primitive() {
|
||||
let actual = nu!("generate 0 {|x| 1}");
|
||||
assert!(actual.err.contains("Invalid block return"));
|
||||
fn generate_raise_error_on_no_default_parameter_closure_and_init_val() {
|
||||
let actual = nu!(pipeline(
|
||||
r#"
|
||||
generate {|x|
|
||||
if $x == 3 {
|
||||
{out: "done"}
|
||||
} else {
|
||||
{out: null, next: ($x + 1)}
|
||||
}
|
||||
} | to nuon
|
||||
"#
|
||||
));
|
||||
assert!(actual.err.contains("The initial value is missing"));
|
||||
}
|
||||
|
|
|
@ -23,6 +23,13 @@ fn let_takes_pipeline() {
|
|||
assert_eq!(actual.out, "11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_takes_pipeline_with_declared_type() {
|
||||
let actual = nu!(r#"let x: list<string> = [] | append "hello world"; print $x.0"#);
|
||||
|
||||
assert_eq!(actual.out, "hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_pipeline_allows_in() {
|
||||
let actual =
|
||||
|
@ -38,6 +45,13 @@ fn mut_takes_pipeline() {
|
|||
assert_eq!(actual.out, "11");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mut_takes_pipeline_with_declared_type() {
|
||||
let actual = nu!(r#"mut x: list<string> = [] | append "hello world"; print $x.0"#);
|
||||
|
||||
assert_eq!(actual.out, "hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mut_pipeline_allows_in() {
|
||||
let actual =
|
||||
|
|
|
@ -7,6 +7,7 @@ mod break_;
|
|||
mod bytes;
|
||||
mod cal;
|
||||
mod cd;
|
||||
mod chunks;
|
||||
mod compact;
|
||||
mod complete;
|
||||
mod config_env_default;
|
||||
|
@ -114,6 +115,7 @@ mod try_;
|
|||
mod ucp;
|
||||
#[cfg(unix)]
|
||||
mod ulimit;
|
||||
mod window;
|
||||
|
||||
mod debug;
|
||||
mod umkdir;
|
||||
|
|
|
@ -3,5 +3,4 @@ mod chars;
|
|||
mod dice;
|
||||
mod float;
|
||||
mod int;
|
||||
#[cfg(feature = "uuid_crate")]
|
||||
mod uuid;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use nu_test_support::nu;
|
||||
use uuid_crate::Uuid;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[test]
|
||||
fn generates_valid_uuid4() {
|
||||
|
|
|
@ -439,3 +439,37 @@ fn no_duplicate_redirection() {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
#[case("let", "out>")]
|
||||
#[case("let", "err>")]
|
||||
#[case("let", "out+err>")]
|
||||
#[case("mut", "out>")]
|
||||
#[case("mut", "err>")]
|
||||
#[case("mut", "out+err>")]
|
||||
fn file_redirection_in_let_and_mut(#[case] keyword: &str, #[case] redir: &str) {
|
||||
Playground::setup("file redirection in let and mut", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
format!("$env.BAZ = 'foo'; {keyword} v = nu --testbin echo_env_mixed out-err BAZ BAZ {redir} result.txt")
|
||||
);
|
||||
assert!(actual.status.success());
|
||||
assert!(file_contents(dirs.test().join("result.txt")).contains("foo"));
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
#[case("let", "err>|", "foo3")]
|
||||
#[case("let", "out+err>|", "7")]
|
||||
#[case("mut", "err>|", "foo3")]
|
||||
#[case("mut", "out+err>|", "7")]
|
||||
fn pipe_redirection_in_let_and_mut(
|
||||
#[case] keyword: &str,
|
||||
#[case] redir: &str,
|
||||
#[case] output: &str,
|
||||
) {
|
||||
let actual = nu!(
|
||||
format!("$env.BAZ = 'foo'; {keyword} v = nu --testbin echo_env_mixed out-err BAZ BAZ {redir} str length; $v")
|
||||
);
|
||||
assert_eq!(actual.out, output);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#[cfg(not(windows))]
|
||||
use nu_path::AbsolutePath;
|
||||
use nu_test_support::fs::{files_exist_at, Stub::EmptyFile};
|
||||
use nu_test_support::nu;
|
||||
use nu_test_support::playground::Playground;
|
||||
use rstest::rstest;
|
||||
#[cfg(not(windows))]
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
|
@ -144,7 +146,7 @@ fn errors_if_attempting_to_delete_home() {
|
|||
Playground::setup("rm_test_8", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
"$env.HOME = myhome ; rm -rf ~"
|
||||
"$env.HOME = 'myhome' ; rm -rf ~"
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("please use -I or -i"));
|
||||
|
@ -405,16 +407,19 @@ fn removes_file_after_cd() {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
struct Cleanup<'a> {
|
||||
dir_to_clean: &'a AbsolutePath,
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn set_dir_read_only(directory: &AbsolutePath, read_only: bool) {
|
||||
let mut permissions = fs::metadata(directory).unwrap().permissions();
|
||||
permissions.set_readonly(read_only);
|
||||
fs::set_permissions(directory, permissions).expect("failed to set directory permissions");
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl<'a> Drop for Cleanup<'a> {
|
||||
/// Restores write permissions to the given directory so that the Playground can be successfully
|
||||
/// cleaned up.
|
||||
|
|
|
@ -309,7 +309,7 @@ fn external_arg_expand_tilde() {
|
|||
"#
|
||||
));
|
||||
|
||||
let home = dirs_next::home_dir().expect("failed to find home dir");
|
||||
let home = dirs::home_dir().expect("failed to find home dir");
|
||||
|
||||
assert_eq!(
|
||||
actual.out,
|
||||
|
|
|
@ -463,3 +463,65 @@ fn save_same_file_with_collect_and_filter() {
|
|||
assert_eq!("helloworld", actual.out);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_from_child_process_dont_sink_stderr() {
|
||||
Playground::setup("save_test_22", |dirs, sandbox| {
|
||||
sandbox.with_files(&[
|
||||
Stub::FileWithContent("log.txt", "Old"),
|
||||
Stub::FileWithContent("err.txt", "Old Err"),
|
||||
]);
|
||||
|
||||
let expected_file = dirs.test().join("log.txt");
|
||||
let expected_stderr_file = dirs.test().join("err.txt");
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
$env.FOO = " New";
|
||||
$env.BAZ = " New Err";
|
||||
do -i {nu -n -c 'nu --testbin echo_env FOO; nu --testbin echo_env_stderr BAZ'} | save -a -r save_test_22/log.txt"#,
|
||||
);
|
||||
assert_eq!(actual.err.trim_end(), " New Err");
|
||||
|
||||
let actual = file_contents(expected_file);
|
||||
assert_eq!(actual.trim_end(), "Old New");
|
||||
|
||||
let actual = file_contents(expected_stderr_file);
|
||||
assert_eq!(actual.trim_end(), "Old Err");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent_redirection_doesnt_affect_save() {
|
||||
Playground::setup("save_test_23", |dirs, sandbox| {
|
||||
sandbox.with_files(&[
|
||||
Stub::FileWithContent("log.txt", "Old"),
|
||||
Stub::FileWithContent("err.txt", "Old Err"),
|
||||
]);
|
||||
|
||||
let expected_file = dirs.test().join("log.txt");
|
||||
let expected_stderr_file = dirs.test().join("err.txt");
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
$env.FOO = " New";
|
||||
$env.BAZ = " New Err";
|
||||
def tttt [] {
|
||||
do -i {nu -n -c 'nu --testbin echo_env FOO; nu --testbin echo_env_stderr BAZ'} | save -a -r save_test_23/log.txt
|
||||
};
|
||||
tttt e> ("save_test_23" | path join empty_file)"#
|
||||
);
|
||||
assert_eq!(actual.err.trim_end(), " New Err");
|
||||
|
||||
let actual = file_contents(expected_file);
|
||||
assert_eq!(actual.trim_end(), "Old New");
|
||||
|
||||
let actual = file_contents(expected_stderr_file);
|
||||
assert_eq!(actual.trim_end(), "Old Err");
|
||||
|
||||
let actual = file_contents(dirs.test().join("empty_file"));
|
||||
assert_eq!(actual.trim_end(), "");
|
||||
})
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ fn complex_nested_columns() {
|
|||
r#"
|
||||
{sample}
|
||||
| select nu."0xATYKARNU" nu.committers.name nu.releases.version
|
||||
| get nu_releases_version
|
||||
| get "nu.releases.version"
|
||||
| where $it > "0.8"
|
||||
| get 0
|
||||
"#
|
||||
|
|
|
@ -2567,7 +2567,7 @@ fn theme_cmd(theme: &str, footer: bool, then: &str) -> String {
|
|||
with_footer = "$env.config.footer_mode = \"always\"".to_string();
|
||||
}
|
||||
|
||||
format!("$env.config.table.mode = {theme}; $env.config.table.header_on_separator = true; {with_footer}; {then}")
|
||||
format!("$env.config.table.mode = \"{theme}\"; $env.config.table.header_on_separator = true; {with_footer}; {then}")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2901,3 +2901,9 @@ fn table_general_header_on_separator_trim_algorithm() {
|
|||
let actual = nu!("$env.config.table.header_on_separator = true; [[a b]; ['11111111111111111111111111111111111111' 2] ] | table --width=20 --theme basic");
|
||||
assert_eq!(actual.out, "+-#-+----a-----+-b-+| 0 | 11111111 | 2 || | 11111111 | || | 11111111 | || | 11111111 | || | 111111 | |+---+----------+---+");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_general_header_on_separator_issue1() {
|
||||
let actual = nu!("$env.config.table.header_on_separator = true; [['Llll oo Bbbbbbbb' 'Bbbbbbbb Aaaa' Nnnnnn Ggggg 'Xxxxx Llllllll #' Bbb 'Pppp Ccccc' 'Rrrrrrrr Dddd' Rrrrrr 'Rrrrrr Ccccc II' 'Rrrrrr Ccccc Ppppppp II' 'Pppppp Dddddddd Tttt' 'Pppppp Dddddddd Dddd' 'Rrrrrrrrr Trrrrrr' 'Pppppp Ppppp Dddd' 'Ppppp Dddd' Hhhh]; [RRRRRRR FFFFFFFF UUUU VV 202407160001 BBB 1 '7/16/2024' '' AAA-1111 AAA-1111-11 '7 YEARS' 2555 'RRRRRRRR DDDD' '7/16/2031' '7/16/2031' NN]] | table --width=87 --theme basic");
|
||||
assert_eq!(actual.out, "+-#-+-Llll oo Bbbbbbbb-+-Bbbbbbbb Aaaa-+-Nnnnnn-+-Ggggg-+-Xxxxx Llllllll #-+-...-+| 0 | RRRRRRR | FFFFFFFF | UUUU | VV | 202407160001 | ... |+---+------------------+---------------+--------+-------+------------------+-----+");
|
||||
}
|
||||
|
|
103
crates/nu-command/tests/commands/window.rs
Normal file
103
crates/nu-command/tests/commands/window.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn window_size_negative() {
|
||||
let actual = nu!("[0 1 2] | window -1");
|
||||
assert!(actual.err.contains("positive"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size_zero() {
|
||||
let actual = nu!("[0 1 2] | window 0");
|
||||
assert!(actual.err.contains("zero"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size_not_int() {
|
||||
let actual = nu!("[0 1 2] | window (if true { 1sec })");
|
||||
assert!(actual.err.contains("can't convert"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_negative() {
|
||||
let actual = nu!("[0 1 2] | window 1 -s -1");
|
||||
assert!(actual.err.contains("positive"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_zero() {
|
||||
let actual = nu!("[0 1 2] | window 1 -s 0");
|
||||
assert!(actual.err.contains("zero"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_not_int() {
|
||||
let actual = nu!("[0 1 2] | window 1 -s (if true { 1sec })");
|
||||
assert!(actual.err.contains("can't convert"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let actual = nu!("[] | window 2 | is-empty");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_stream() {
|
||||
let actual = nu!("([0 1 2] | every 1 | window 2) == ([0 1 2] | window 2)");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_stream() {
|
||||
let actual = nu!("([[foo bar]; [0 1] [2 3] [4 5]] | every 1 | window 2) == ([[foo bar]; [0 1] [2 3] [4 5]] | window 2)");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_empty_chunks() {
|
||||
let actual = nu!("([0 1 2 3 4 5] | window 3 -s 3 -r | length) == 2");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_as_chunks() {
|
||||
let actual = nu!("([0 1 2 3 4] | window 2 -s 2 -r) == ([0 1 2 3 4 ] | chunks 2)");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_equal_to_window_size() {
|
||||
let actual = nu!("([0 1 2 3] | window 2 -s 2 | flatten) == [0 1 2 3]");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_greater_than_window_size() {
|
||||
let actual = nu!("([0 1 2 3 4] | window 2 -s 3 | flatten) == [0 1 3 4]");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_less_than_window_size() {
|
||||
let actual = nu!("([0 1 2 3 4 5] | window 3 -s 2 | length) == 2");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_equal_to_window_size_remainder() {
|
||||
let actual = nu!("([0 1 2 3 4] | window 2 -s 2 -r | flatten) == [0 1 2 3 4]");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_greater_than_window_size_remainder() {
|
||||
let actual = nu!("([0 1 2 3 4] | window 2 -s 3 -r | flatten) == [0 1 3 4]");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stride_less_than_window_size_remainder() {
|
||||
let actual = nu!("([0 1 2 3 4 5] | window 3 -s 2 -r | length) == 3");
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
license = "MIT"
|
||||
name = "nu-derive-value"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-derive-value"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
@ -5,17 +5,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-engine"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-engine"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", features = ["plugin"], version = "0.95.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.95.1" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.95.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.95.1" }
|
||||
nu-protocol = { path = "../nu-protocol", features = ["plugin"], version = "0.96.2" }
|
||||
nu-path = { path = "../nu-path", version = "0.96.2" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.96.2" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||
log = { workspace = true }
|
||||
|
||||
[features]
|
||||
plugin = []
|
||||
plugin = []
|
9
crates/nu-engine/README.md
Normal file
9
crates/nu-engine/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
This crate primarily drives the evaluation of expressions.
|
||||
|
||||
(Some overlap with nu-protocol)
|
||||
|
||||
- Provides `CallExt`
|
||||
|
||||
## Internal Nushell crate
|
||||
|
||||
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -204,6 +204,7 @@ impl BlockBuilder {
|
|||
Instruction::Drain { src } => allocate(&[*src], &[]),
|
||||
Instruction::LoadVariable { dst, var_id: _ } => allocate(&[], &[*dst]),
|
||||
Instruction::StoreVariable { var_id: _, src } => allocate(&[*src], &[]),
|
||||
Instruction::DropVariable { var_id: _ } => Ok(()),
|
||||
Instruction::LoadEnv { dst, key: _ } => allocate(&[], &[*dst]),
|
||||
Instruction::LoadEnvOpt { dst, key: _ } => allocate(&[], &[*dst]),
|
||||
Instruction::StoreEnv { key: _, src } => allocate(&[*src], &[]),
|
||||
|
@ -422,7 +423,7 @@ impl BlockBuilder {
|
|||
self.push(Instruction::Jump { index: label_id.0 }.into_spanned(span))
|
||||
}
|
||||
|
||||
/// The index that the next instruction [`.push()`]ed will have.
|
||||
/// The index that the next instruction [`.push()`](Self::push)ed will have.
|
||||
pub(crate) fn here(&self) -> usize {
|
||||
self.instructions.len()
|
||||
}
|
||||
|
|
|
@ -171,6 +171,27 @@ pub(crate) fn compile_expression(
|
|||
Err(CompileError::UnsupportedOperatorExpression { span: op.span })
|
||||
}
|
||||
}
|
||||
Expr::Collect(var_id, expr) => {
|
||||
let store_reg = if let Some(in_reg) = in_reg {
|
||||
// Collect, clone, store
|
||||
builder.push(Instruction::Collect { src_dst: in_reg }.into_spanned(expr.span))?;
|
||||
builder.clone_reg(in_reg, expr.span)?
|
||||
} else {
|
||||
// Just store nothing in the variable
|
||||
builder.literal(Literal::Nothing.into_spanned(Span::unknown()))?
|
||||
};
|
||||
builder.push(
|
||||
Instruction::StoreVariable {
|
||||
var_id: *var_id,
|
||||
src: store_reg,
|
||||
}
|
||||
.into_spanned(expr.span),
|
||||
)?;
|
||||
compile_expression(working_set, builder, expr, redirect_modes, in_reg, out_reg)?;
|
||||
// Clean it up afterward
|
||||
builder.push(Instruction::DropVariable { var_id: *var_id }.into_spanned(expr.span))?;
|
||||
Ok(())
|
||||
}
|
||||
Expr::Subexpression(block_id) => {
|
||||
let block = working_set.get_block(*block_id);
|
||||
compile_block(working_set, builder, block, redirect_modes, in_reg, out_reg)
|
||||
|
@ -423,7 +444,15 @@ pub(crate) fn compile_expression(
|
|||
working_set,
|
||||
builder,
|
||||
&full_cell_path.head,
|
||||
RedirectModes::capture_out(expr.span),
|
||||
// Only capture the output if there is a tail. This was a bit of a headscratcher
|
||||
// as the parser emits a FullCellPath with no tail for subexpressions in
|
||||
// general, which shouldn't be captured any differently than they otherwise
|
||||
// would be.
|
||||
if !full_cell_path.tail.is_empty() {
|
||||
RedirectModes::capture_out(expr.span)
|
||||
} else {
|
||||
redirect_modes
|
||||
},
|
||||
in_reg,
|
||||
out_reg,
|
||||
)?;
|
||||
|
|
|
@ -10,8 +10,8 @@ use nu_protocol::{
|
|||
debugger::DebugContext,
|
||||
engine::{Closure, EngineState, Redirection, Stack, StateWorkingSet},
|
||||
eval_base::Eval,
|
||||
ByteStreamSource, Config, FromValue, IntoPipelineData, OutDest, PipelineData, ShellError, Span,
|
||||
Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
ByteStreamSource, Config, DataSource, FromValue, IntoPipelineData, OutDest, PipelineData,
|
||||
PipelineMetadata, ShellError, Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use std::{fs::OpenOptions, path::PathBuf, sync::Arc};
|
||||
|
@ -198,7 +198,7 @@ pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee
|
|||
}
|
||||
|
||||
// set config to callee config, to capture any updates to that
|
||||
caller_stack.config = callee_stack.config.clone();
|
||||
caller_stack.config.clone_from(&callee_stack.config);
|
||||
}
|
||||
|
||||
fn eval_external(
|
||||
|
@ -259,6 +259,10 @@ pub fn eval_expression_with_input<D: DebugContext>(
|
|||
input = eval_external(engine_state, stack, head, args, input)?;
|
||||
}
|
||||
|
||||
Expr::Collect(var_id, expr) => {
|
||||
input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
|
||||
}
|
||||
|
||||
Expr::Subexpression(block_id) => {
|
||||
let block = engine_state.get_block(*block_id);
|
||||
// FIXME: protect this collect with ctrl-c
|
||||
|
@ -605,6 +609,44 @@ pub fn eval_block<D: DebugContext>(
|
|||
Ok(input)
|
||||
}
|
||||
|
||||
pub fn eval_collect<D: DebugContext>(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
var_id: VarId,
|
||||
expr: &Expression,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// Evaluate the expression with the variable set to the collected input
|
||||
let span = input.span().unwrap_or(Span::unknown());
|
||||
|
||||
let metadata = match input.metadata() {
|
||||
// Remove the `FilePath` metadata, because after `collect` it's no longer necessary to
|
||||
// check where some input came from.
|
||||
Some(PipelineMetadata {
|
||||
data_source: DataSource::FilePath(_),
|
||||
content_type: None,
|
||||
}) => None,
|
||||
other => other,
|
||||
};
|
||||
|
||||
let input = input.into_value(span)?;
|
||||
|
||||
stack.add_var(var_id, input.clone());
|
||||
|
||||
let result = eval_expression_with_input::<D>(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
// We still have to pass it as input
|
||||
input.into_pipeline_data_with_metadata(metadata),
|
||||
)
|
||||
.map(|(result, _failed)| result);
|
||||
|
||||
stack.remove_var(var_id);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn eval_subexpression<D: DebugContext>(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
|
@ -729,6 +771,18 @@ impl Eval for EvalRuntime {
|
|||
eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
|
||||
}
|
||||
|
||||
fn eval_collect<D: DebugContext>(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
var_id: VarId,
|
||||
expr: &Expression,
|
||||
) -> Result<Value, ShellError> {
|
||||
// It's a little bizarre, but the expression can still have some kind of result even with
|
||||
// nothing input
|
||||
eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
|
||||
.into_value(expr.span)
|
||||
}
|
||||
|
||||
fn eval_subexpression<D: DebugContext>(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
|
|
|
@ -6,9 +6,9 @@ use nu_protocol::{
|
|||
debugger::DebugContext,
|
||||
engine::{Argument, Closure, EngineState, ErrorHandler, Matcher, Redirection, Stack},
|
||||
ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
|
||||
record, ByteStreamSource, DeclId, ErrSpan, Flag, IntoPipelineData, IntoSpanned, ListStream,
|
||||
OutDest, PipelineData, PositionalArg, Range, Record, RegId, ShellError, Signals, Signature,
|
||||
Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
record, ByteStreamSource, DataSource, DeclId, ErrSpan, Flag, IntoPipelineData, IntoSpanned,
|
||||
ListStream, OutDest, PipelineData, PipelineMetadata, PositionalArg, Range, Record, RegId,
|
||||
ShellError, Signals, Signature, Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
|
||||
|
@ -184,11 +184,20 @@ fn eval_ir_block_impl<D: DebugContext>(
|
|||
let instruction = &ir_block.instructions[pc];
|
||||
let span = &ir_block.spans[pc];
|
||||
let ast = &ir_block.ast[pc];
|
||||
log::trace!(
|
||||
"{pc:-4}: {}",
|
||||
instruction.display(ctx.engine_state, ctx.data)
|
||||
|
||||
D::enter_instruction(ctx.engine_state, ir_block, pc, ctx.registers);
|
||||
|
||||
let result = eval_instruction::<D>(ctx, instruction, span, ast);
|
||||
|
||||
D::leave_instruction(
|
||||
ctx.engine_state,
|
||||
ir_block,
|
||||
pc,
|
||||
ctx.registers,
|
||||
result.as_ref().err(),
|
||||
);
|
||||
match eval_instruction::<D>(ctx, instruction, span, ast) {
|
||||
|
||||
match result {
|
||||
Ok(InstructionResult::Continue) => {
|
||||
pc += 1;
|
||||
}
|
||||
|
@ -336,6 +345,10 @@ fn eval_instruction<D: DebugContext>(
|
|||
ctx.stack.add_var(*var_id, value);
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::DropVariable { var_id } => {
|
||||
ctx.stack.remove_var(*var_id);
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::LoadEnv { dst, key } => {
|
||||
let key = ctx.get_str(*key, *span)?;
|
||||
if let Some(value) = get_env_var_case_insensitive(ctx, key) {
|
||||
|
@ -1332,9 +1345,19 @@ fn get_env_var_name_case_insensitive<'a>(ctx: &mut EvalContext<'_>, key: &'a str
|
|||
}
|
||||
|
||||
/// Helper to collect values into [`PipelineData`], preserving original span and metadata
|
||||
///
|
||||
/// The metadata is removed if it is the file data source, as that's just meant to mark streams.
|
||||
fn collect(data: PipelineData, fallback_span: Span) -> Result<PipelineData, ShellError> {
|
||||
let span = data.span().unwrap_or(fallback_span);
|
||||
let metadata = data.metadata();
|
||||
let metadata = match data.metadata() {
|
||||
// Remove the `FilePath` metadata, because after `collect` it's no longer necessary to
|
||||
// check where some input came from.
|
||||
Some(PipelineMetadata {
|
||||
data_source: DataSource::FilePath(_),
|
||||
content_type: None,
|
||||
}) => None,
|
||||
other => other,
|
||||
};
|
||||
let value = data.into_value(span)?;
|
||||
Ok(PipelineData::Value(value, metadata))
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod call_ext;
|
||||
mod closure_eval;
|
||||
pub mod column;
|
||||
|
|
|
@ -5,21 +5,21 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-explore"
|
|||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-explore"
|
||||
version = "0.95.1"
|
||||
version = "0.96.2"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.95.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.95.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.95.1" }
|
||||
nu-table = { path = "../nu-table", version = "0.95.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.95.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.95.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.96.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||
nu-table = { path = "../nu-table", version = "0.96.2" }
|
||||
nu-json = { path = "../nu-json", version = "0.96.2" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.95.1" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.96.2" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
|
5
crates/nu-explore/README.md
Normal file
5
crates/nu-explore/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
Implementation of the interactive `explore` command pager.
|
||||
|
||||
## Internal Nushell crate
|
||||
|
||||
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -1,3 +1,4 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
mod commands;
|
||||
mod default_context;
|
||||
mod explore;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user