Compare commits

..

1 Commits

Author SHA1 Message Date
Darren Schroeder
70859ffe70
Revert "Fix issue with head on separation lines (#13291)"
This reverts commit 32db5d3aa3.
2024-07-08 13:24:19 -05:00
408 changed files with 3762 additions and 15395 deletions

View File

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

View File

@ -161,7 +161,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release # REF: https://github.com/marketplace/actions/gh-release
# Create a release only in nushell/nightly repo # Create a release only in nushell/nightly repo
- name: Publish Archive - name: Publish Archive
uses: softprops/action-gh-release@v2.0.8 uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.repository, 'nushell/nightly') }} if: ${{ startsWith(github.repository, 'nushell/nightly') }}
with: with:
prerelease: true prerelease: true

View File

@ -161,12 +161,8 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
let releaseStem = $'($bin)-($version)-($target)' let releaseStem = $'($bin)-($version)-($target)'
print $'(char nl)Download less related stuffs...'; hr-line 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://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
# the below was renamed because it was failing to download for darren. it should work but it wasn't aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
# 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 # Create Windows msi release package
if (get-env _EXTRA_) == 'msi' { if (get-env _EXTRA_) == 'msi' {

View File

@ -91,7 +91,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release # REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive - name: Publish Archive
uses: softprops/action-gh-release@v2.0.8 uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
draft: true draft: true

View File

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

192
Cargo.lock generated
View File

@ -605,7 +605,7 @@ dependencies = [
"encoding_rs", "encoding_rs",
"log", "log",
"once_cell", "once_cell",
"quick-xml 0.31.0", "quick-xml",
"serde", "serde",
"zip", "zip",
] ]
@ -1219,24 +1219,24 @@ dependencies = [
] ]
[[package]] [[package]]
name = "dirs" name = "dirs-next"
version = "5.0.1" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [ dependencies = [
"dirs-sys", "cfg-if",
"dirs-sys-next",
] ]
[[package]] [[package]]
name = "dirs-sys" name = "dirs-sys-next"
version = "0.4.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [ dependencies = [
"libc", "libc",
"option-ext",
"redox_users", "redox_users",
"windows-sys 0.48.0", "winapi",
] ]
[[package]] [[package]]
@ -2342,9 +2342,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.155" version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]] [[package]]
name = "libflate" name = "libflate"
@ -2868,12 +2868,12 @@ dependencies = [
[[package]] [[package]]
name = "nu" name = "nu"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"crossterm", "crossterm",
"ctrlc", "ctrlc",
"dirs", "dirs-next",
"log", "log",
"miette", "miette",
"mimalloc", "mimalloc",
@ -2900,7 +2900,6 @@ dependencies = [
"openssl", "openssl",
"pretty_assertions", "pretty_assertions",
"reedline", "reedline",
"regex",
"rstest", "rstest",
"serde_json", "serde_json",
"serial_test", "serial_test",
@ -2913,16 +2912,16 @@ dependencies = [
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.50.1" version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" checksum = "dd2800e1520bdc966782168a627aa5d1ad92e33b984bf7c7615d31280c83ff14"
dependencies = [ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
name = "nu-cli" name = "nu-cli"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"crossterm", "crossterm",
@ -2957,7 +2956,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-base" name = "nu-cmd-base"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"miette", "miette",
@ -2969,7 +2968,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-extra" name = "nu-cmd-extra"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"heck 0.5.0", "heck 0.5.0",
@ -2994,7 +2993,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"nu-engine", "nu-engine",
@ -3006,7 +3005,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-plugin" name = "nu-cmd-plugin"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"nu-engine", "nu-engine",
@ -3017,7 +3016,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-color-config" name = "nu-color-config"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-engine", "nu-engine",
@ -3029,7 +3028,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-command" name = "nu-command"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"alphanumeric-sort", "alphanumeric-sort",
"base64 0.22.1", "base64 0.22.1",
@ -3047,7 +3046,7 @@ dependencies = [
"deunicode", "deunicode",
"dialoguer", "dialoguer",
"digest", "digest",
"dirs", "dirs-next",
"dtparse", "dtparse",
"encoding_rs", "encoding_rs",
"fancy-regex", "fancy-regex",
@ -3094,7 +3093,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"print-positions", "print-positions",
"procfs", "procfs",
"quick-xml 0.31.0", "quick-xml",
"quickcheck", "quickcheck",
"quickcheck_macros", "quickcheck_macros",
"rand", "rand",
@ -3139,7 +3138,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"proc-macro-error", "proc-macro-error",
@ -3150,19 +3149,17 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"log",
"nu-glob", "nu-glob",
"nu-path", "nu-path",
"nu-protocol", "nu-protocol",
"nu-utils", "nu-utils",
"terminal_size",
] ]
[[package]] [[package]]
name = "nu-explore" name = "nu-explore"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"ansi-str", "ansi-str",
"anyhow", "anyhow",
@ -3187,19 +3184,16 @@ dependencies = [
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"doc-comment", "doc-comment",
] ]
[[package]] [[package]]
name = "nu-json" name = "nu-json"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"fancy-regex",
"linked-hash-map", "linked-hash-map",
"nu-path",
"nu-test-support",
"num-traits", "num-traits",
"serde", "serde",
"serde_json", "serde_json",
@ -3207,7 +3201,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-lsp" name = "nu-lsp"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"assert-json-diff", "assert-json-diff",
"crossbeam-channel", "crossbeam-channel",
@ -3228,7 +3222,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-parser" name = "nu-parser"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"bytesize", "bytesize",
"chrono", "chrono",
@ -3244,16 +3238,16 @@ dependencies = [
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"dirs", "dirs-next",
"omnipath", "omnipath",
"pwd", "pwd",
] ]
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"log", "log",
"nix", "nix",
@ -3261,7 +3255,6 @@ dependencies = [
"nu-plugin-core", "nu-plugin-core",
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-utils",
"serde", "serde",
"thiserror", "thiserror",
"typetag", "typetag",
@ -3269,7 +3262,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -3283,7 +3276,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-engine" name = "nu-plugin-engine"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"log", "log",
"nu-engine", "nu-engine",
@ -3291,7 +3284,6 @@ dependencies = [
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-system", "nu-system",
"nu-utils",
"serde", "serde",
"typetag", "typetag",
"windows 0.54.0", "windows 0.54.0",
@ -3299,7 +3291,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"bincode", "bincode",
"nu-protocol", "nu-protocol",
@ -3311,7 +3303,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-test-support" name = "nu-plugin-test-support"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-cmd-lang", "nu-cmd-lang",
@ -3329,7 +3321,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-pretty-hex" name = "nu-pretty-hex"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"heapless", "heapless",
"nu-ansi-term", "nu-ansi-term",
@ -3338,18 +3330,15 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"brotli", "brotli",
"byte-unit", "byte-unit",
"chrono", "chrono",
"chrono-humanize", "chrono-humanize",
"convert_case", "convert_case",
"dirs",
"dirs-sys",
"fancy-regex", "fancy-regex",
"indexmap", "indexmap",
"log",
"lru", "lru",
"miette", "miette",
"nix", "nix",
@ -3370,12 +3359,11 @@ dependencies = [
"tempfile", "tempfile",
"thiserror", "thiserror",
"typetag", "typetag",
"windows-sys 0.48.0",
] ]
[[package]] [[package]]
name = "nu-std" name = "nu-std"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"log", "log",
"miette", "miette",
@ -3386,7 +3374,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.12.1", "itertools 0.12.1",
@ -3404,7 +3392,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-table" name = "nu-table"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"nu-ansi-term", "nu-ansi-term",
@ -3418,7 +3406,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-term-grid" name = "nu-term-grid"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-utils", "nu-utils",
"unicode-width", "unicode-width",
@ -3426,7 +3414,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-test-support" name = "nu-test-support"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-glob", "nu-glob",
"nu-path", "nu-path",
@ -3438,7 +3426,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"crossterm_winapi", "crossterm_winapi",
"log", "log",
@ -3464,7 +3452,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_example" name = "nu_plugin_example"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-cmd-lang", "nu-cmd-lang",
"nu-plugin", "nu-plugin",
@ -3474,22 +3462,20 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_formats" name = "nu_plugin_formats"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"chrono",
"eml-parser", "eml-parser",
"ical", "ical",
"indexmap", "indexmap",
"nu-plugin", "nu-plugin",
"nu-plugin-test-support", "nu-plugin-test-support",
"nu-protocol", "nu-protocol",
"plist",
"rust-ini", "rust-ini",
] ]
[[package]] [[package]]
name = "nu_plugin_gstat" name = "nu_plugin_gstat"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"git2", "git2",
"nu-plugin", "nu-plugin",
@ -3498,7 +3484,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_inc" name = "nu_plugin_inc"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"nu-plugin", "nu-plugin",
"nu-protocol", "nu-protocol",
@ -3507,7 +3493,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_polars" name = "nu_plugin_polars"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"chrono-tz 0.9.0", "chrono-tz 0.9.0",
@ -3541,7 +3527,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_query" name = "nu_plugin_query"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"gjson", "gjson",
"nu-plugin", "nu-plugin",
@ -3556,7 +3542,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_stress_internals" name = "nu_plugin_stress_internals"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"serde", "serde",
@ -3682,7 +3668,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "nuon" name = "nuon"
version = "0.96.2" version = "0.95.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"fancy-regex", "fancy-regex",
@ -3779,9 +3765,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "open" name = "open"
version = "5.3.0" version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37"
dependencies = [ dependencies = [
"is-wsl", "is-wsl",
"libc", "libc",
@ -3790,9 +3776,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.66" version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"cfg-if", "cfg-if",
@ -3831,9 +3817,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.103" version = "0.9.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -3842,12 +3828,6 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.7.3" version = "0.7.3"
@ -4150,19 +4130,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "plist"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
dependencies = [
"base64 0.22.1",
"indexmap",
"quick-xml 0.32.0",
"serde",
"time",
]
[[package]] [[package]]
name = "polars" name = "polars"
version = "0.41.2" version = "0.41.2"
@ -4831,15 +4798,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quick-xml"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quickcheck" name = "quickcheck"
version = "1.0.3" version = "1.0.3"
@ -5029,8 +4987,8 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.33.0" version = "0.32.0"
source = "git+https://github.com/nushell/reedline?branch=main#919292e40fd417e3da882692021961b444150c59" source = "git+https://github.com/nushell/reedline?branch=main#480059a3f52cf919341cda88e8c544edd846bc73"
dependencies = [ dependencies = [
"arboard", "arboard",
"chrono", "chrono",
@ -5244,9 +5202,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "8.5.0" version = "8.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
dependencies = [ dependencies = [
"rust-embed-impl", "rust-embed-impl",
"rust-embed-utils", "rust-embed-utils",
@ -5613,9 +5571,9 @@ dependencies = [
[[package]] [[package]]
name = "shadow-rs" name = "shadow-rs"
version = "0.30.0" version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d253e54681d4be0161e965db57974ae642a0b6aaeb18a999424c4dab062be8c5" checksum = "0a600f795d0894cda22235b44eea4b85c2a35b405f65523645ac8e35b306817a"
dependencies = [ dependencies = [
"const_format", "const_format",
"is_debug", "is_debug",
@ -5690,9 +5648,9 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.6.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
[[package]] [[package]]
name = "simplelog" name = "simplelog"
@ -6507,9 +6465,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "ureq" name = "ureq"
version = "2.10.0" version = "2.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"encoding_rs", "encoding_rs",
@ -6666,9 +6624,9 @@ checksum = "425a23c7b7145bc7620c9c445817c37b1f78b6790aee9f208133f3c028975b60"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.10.0" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde", "serde",
@ -6896,7 +6854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quick-xml 0.31.0", "quick-xml",
"quote", "quote",
] ]

View File

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

View File

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

View File

@ -45,10 +45,6 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
}; };
let mut stack = Stack::new(); let mut stack = Stack::new();
// Support running benchmarks with IR mode
stack.use_ir = std::env::var_os("NU_USE_IR").is_some();
evaluate_commands( evaluate_commands(
&commands, &commands,
&mut engine, &mut engine,

View File

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

View File

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

View File

@ -67,7 +67,7 @@ impl Command for History {
} else { } else {
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format { let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
HistoryFileFormat::Sqlite => { HistoryFileFormat::Sqlite => {
SqliteBackedHistory::with_file(history_path.clone().into(), None, None) SqliteBackedHistory::with_file(history_path.clone(), None, None)
.map(|inner| { .map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner); let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
boxed boxed
@ -77,7 +77,7 @@ impl Command for History {
HistoryFileFormat::PlainText => FileBackedHistory::with_file( HistoryFileFormat::PlainText => FileBackedHistory::with_file(
history.max_size as usize, history.max_size as usize,
history_path.clone().into(), history_path.clone(),
) )
.map(|inner| { .map(|inner| {
let boxed: Box<dyn ReedlineHistory> = Box::new(inner); let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
@ -156,34 +156,58 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
//2. Create a record of either short or long columns and values //2. Create a record of either short or long columns and values
let item_id_value = Value::int( let item_id_value = Value::int(
entry match entry.id {
.id Some(id) => {
.and_then(|id| id.to_string().parse::<i64>().ok()) let ids = id.to_string();
.unwrap_or_default(), match ids.parse::<i64>() {
Ok(i) => i,
_ => 0i64,
}
}
None => 0i64,
},
head, head,
); );
let start_timestamp_value = Value::string( let start_timestamp_value = Value::string(
entry match entry.start_timestamp {
.start_timestamp Some(time) => time.to_string(),
.map(|time| time.to_string()) None => "".into(),
.unwrap_or_default(), },
head, head,
); );
let command_value = Value::string(entry.command_line, head); let command_value = Value::string(entry.command_line, head);
let session_id_value = Value::int( let session_id_value = Value::int(
entry match entry.session_id {
.session_id Some(sid) => {
.and_then(|id| id.to_string().parse::<i64>().ok()) let sids = sid.to_string();
.unwrap_or_default(), 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(),
},
head, 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( let duration_value = Value::duration(
entry match entry.duration {
.duration Some(d) => d.as_nanos().try_into().unwrap_or(0),
.and_then(|d| d.as_nanos().try_into().ok()) None => 0,
.unwrap_or(0), },
head, head,
); );
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head); let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);

View File

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

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
completions::{Completer, CompletionOptions, MatchAlgorithm}, completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy},
SuggestionKind, SuggestionKind,
}; };
use nu_parser::FlatShape; use nu_parser::FlatShape;
@ -99,9 +99,10 @@ impl CommandCompletion {
suggestion: Suggestion { suggestion: Suggestion {
value: String::from_utf8_lossy(&x.0).to_string(), value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1, description: x.1,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset), span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true, append_whitespace: true,
..Suggestion::default()
}, },
kind: Some(SuggestionKind::Command(x.2)), kind: Some(SuggestionKind::Command(x.2)),
}) })
@ -117,9 +118,11 @@ impl CommandCompletion {
.map(move |x| SemanticSuggestion { .map(move |x| SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: x, value: x,
description: None,
style: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset), span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true, append_whitespace: true,
..Suggestion::default()
}, },
// TODO: is there a way to create a test? // TODO: is there a way to create a test?
kind: None, kind: None,
@ -133,9 +136,11 @@ impl CommandCompletion {
results.push(SemanticSuggestion { results.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: format!("^{}", external.suggestion.value), value: format!("^{}", external.suggestion.value),
description: None,
style: None,
extra: None,
span: external.suggestion.span, span: external.suggestion.span,
append_whitespace: true, append_whitespace: true,
..Suggestion::default()
}, },
kind: external.kind, kind: external.kind,
}) })
@ -193,7 +198,11 @@ impl Completer for CommandCompletion {
}; };
if !subcommands.is_empty() { if !subcommands.is_empty() {
return sort_suggestions(&String::from_utf8_lossy(&prefix), subcommands, options); return sort_suggestions(
&String::from_utf8_lossy(&prefix),
subcommands,
SortBy::LevenshteinDistance,
);
} }
let config = working_set.get_config(); let config = working_set.get_config();
@ -218,7 +227,11 @@ impl Completer for CommandCompletion {
vec![] vec![]
}; };
sort_suggestions(&String::from_utf8_lossy(&prefix), commands, options) sort_suggestions(
&String::from_utf8_lossy(&prefix),
commands,
SortBy::LevenshteinDistance,
)
} }
} }

View File

@ -48,7 +48,6 @@ impl NuCompleter {
let options = CompletionOptions { let options = CompletionOptions {
case_sensitive: config.case_sensitive_completions, case_sensitive: config.case_sensitive_completions,
match_algorithm: config.completion_algorithm.into(), match_algorithm: config.completion_algorithm.into(),
sort: config.completion_sort,
..Default::default() ..Default::default()
}; };
@ -444,11 +443,14 @@ pub fn map_value_completions<'a>(
return Some(SemanticSuggestion { return Some(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: s, value: s,
description: None,
style: None,
extra: None,
span: reedline::Span { span: reedline::Span {
start: span.start - offset, start: span.start - offset,
end: span.end - offset, end: span.end - offset,
}, },
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(SuggestionKind::Type(x.get_type())), kind: Some(SuggestionKind::Type(x.get_type())),
}); });
@ -458,11 +460,14 @@ pub fn map_value_completions<'a>(
if let Ok(record) = x.as_record() { if let Ok(record) = x.as_record() {
let mut suggestion = Suggestion { let mut suggestion = Suggestion {
value: String::from(""), // Initialize with empty string value: String::from(""), // Initialize with empty string
description: None,
style: None,
extra: None,
span: reedline::Span { span: reedline::Span {
start: span.start - offset, start: span.start - offset,
end: span.end - offset, end: span.end - offset,
}, },
..Suggestion::default() append_whitespace: false,
}; };
// Iterate the cols looking for `value` and `description` // Iterate the cols looking for `value` and `description`

View File

@ -2,18 +2,19 @@ use crate::{
completions::{matches, CompletionOptions}, completions::{matches, CompletionOptions},
SemanticSuggestion, SemanticSuggestion,
}; };
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_engine::env_to_string; use nu_engine::env_to_string;
use nu_path::{expand_to_real_path, home_dir}; use nu_path::{expand_to_real_path, home_dir};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
CompletionSort, Span, levenshtein_distance, Span,
}; };
use nu_utils::get_ls_colors; use nu_utils::get_ls_colors;
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP}; use std::path::{
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
};
use super::MatchAlgorithm; use super::SortBy;
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct PathBuiltFromString { pub struct PathBuiltFromString {
@ -21,21 +22,12 @@ pub struct PathBuiltFromString {
isdir: bool, isdir: bool,
} }
/// Recursively goes through paths that match a given `partial`. fn complete_rec(
/// built: State struct for a valid matching path built so far.
///
/// `isdir`: whether the current partial path has a trailing slash.
/// Parsing a path string into a pathbuf loses that bit of information.
///
/// want_directory: Whether we want only directories as completion matches.
/// Some commands like `cd` can only be run on directories whereas others
/// like `ls` can be run on regular files as well.
pub fn complete_rec(
partial: &[&str], partial: &[&str],
built: &PathBuiltFromString, built: &PathBuiltFromString,
cwd: &Path, cwd: &Path,
options: &CompletionOptions, options: &CompletionOptions,
want_directory: bool, dir: bool,
isdir: bool, isdir: bool,
) -> Vec<PathBuiltFromString> { ) -> Vec<PathBuiltFromString> {
let mut completions = vec![]; let mut completions = vec![];
@ -45,7 +37,7 @@ pub fn complete_rec(
let mut built = built.clone(); let mut built = built.clone();
built.parts.push(base.to_string()); built.parts.push(base.to_string());
built.isdir = true; built.isdir = true;
return complete_rec(rest, &built, cwd, options, want_directory, isdir); return complete_rec(rest, &built, cwd, options, dir, isdir);
} }
} }
@ -66,41 +58,24 @@ pub fn complete_rec(
built.parts.push(entry_name.clone()); built.parts.push(entry_name.clone());
built.isdir = entry_isdir; built.isdir = entry_isdir;
if !want_directory || entry_isdir { if !dir || entry_isdir {
entries.push((entry_name, built)); entries.push((entry_name, built));
} }
} }
let prefix = partial.first().unwrap_or(&""); let prefix = partial.first().unwrap_or(&"");
let sorted_entries = sort_completions(prefix, entries, options, |(entry, _)| entry); let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
for (entry_name, built) in sorted_entries { for (entry_name, built) in sorted_entries {
match partial.split_first() { match partial.split_first() {
Some((base, rest)) => { Some((base, rest)) => {
if matches(base, &entry_name, options) { if matches(base, &entry_name, options) {
// We use `isdir` to confirm that the current component has
// at least one next component or a slash.
// Serves as confirmation to ignore longer completions for
// components in between.
if !rest.is_empty() || isdir { if !rest.is_empty() || isdir {
completions.extend(complete_rec( completions.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
rest,
&built,
cwd,
options,
want_directory,
isdir,
));
} else { } else {
completions.push(built); completions.push(built);
} }
} }
if entry_name.eq(base)
&& matches!(options.match_algorithm, MatchAlgorithm::Prefix)
&& isdir
{
break;
}
} }
None => { None => {
completions.push(built); completions.push(built);
@ -118,16 +93,16 @@ enum OriginalCwd {
} }
impl OriginalCwd { impl OriginalCwd {
fn apply(&self, mut p: PathBuiltFromString, path_separator: char) -> String { fn apply(&self, mut p: PathBuiltFromString) -> String {
match self { match self {
Self::None => {} Self::None => {}
Self::Home => p.parts.insert(0, "~".to_string()), Self::Home => p.parts.insert(0, "~".to_string()),
Self::Prefix(s) => p.parts.insert(0, s.clone()), Self::Prefix(s) => p.parts.insert(0, s.clone()),
}; };
let mut ret = p.parts.join(&path_separator.to_string()); let mut ret = p.parts.join(MAIN_SEPARATOR_STR);
if p.isdir { if p.isdir {
ret.push(path_separator); ret.push(SEP);
} }
ret ret
} }
@ -158,14 +133,6 @@ pub fn complete_item(
) -> Vec<(nu_protocol::Span, String, Option<Style>)> { ) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
let partial = surround_remove(partial); let partial = surround_remove(partial);
let isdir = partial.ends_with(is_separator); 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 cwd_pathbuf = Path::new(cwd).to_path_buf();
let ls_colors = (engine_state.config.use_ls_colors_completions let ls_colors = (engine_state.config.use_ls_colors_completions
&& engine_state.config.use_ansi_coloring) && engine_state.config.use_ansi_coloring)
@ -203,7 +170,7 @@ pub fn complete_item(
} }
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => { Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
components.next(); components.next();
cwd = home_dir().map(Into::into).unwrap_or(cwd_pathbuf); cwd = home_dir().unwrap_or(cwd_pathbuf);
prefix_len = 1; prefix_len = 1;
original_cwd = OriginalCwd::Home; original_cwd = OriginalCwd::Home;
} }
@ -228,7 +195,7 @@ pub fn complete_item(
) )
.into_iter() .into_iter()
.map(|p| { .map(|p| {
let path = original_cwd.apply(p, path_separator); let path = original_cwd.apply(p);
let style = ls_colors.as_ref().map(|lsc| { let style = ls_colors.as_ref().map(|lsc| {
lsc.style_for_path_with_metadata( lsc.style_for_path_with_metadata(
&path, &path,
@ -306,37 +273,33 @@ pub fn adjust_if_intermediate(
pub fn sort_suggestions( pub fn sort_suggestions(
prefix: &str, prefix: &str,
items: Vec<SemanticSuggestion>, items: Vec<SemanticSuggestion>,
options: &CompletionOptions, sort_by: SortBy,
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
sort_completions(prefix, items, options, |it| &it.suggestion.value) sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
} }
/// # Arguments /// # Arguments
/// * `prefix` - What the user's typed, for sorting by fuzzy matcher score /// * `prefix` - What the user's typed, for sorting by Levenshtein distance
pub fn sort_completions<T>( pub fn sort_completions<T>(
prefix: &str, prefix: &str,
mut items: Vec<T>, mut items: Vec<T>,
options: &CompletionOptions, sort_by: SortBy,
get_value: fn(&T) -> &str, get_value: fn(&T) -> &str,
) -> Vec<T> { ) -> Vec<T> {
// Sort items // Sort items
if options.sort == CompletionSort::Smart && options.match_algorithm == MatchAlgorithm::Fuzzy { match sort_by {
let mut matcher = SkimMatcherV2::default(); SortBy::LevenshteinDistance => {
if options.case_sensitive { items.sort_by(|a, b| {
matcher = matcher.respect_case(); let a_distance = levenshtein_distance(prefix, get_value(a));
} else { let b_distance = levenshtein_distance(prefix, get_value(b));
matcher = matcher.ignore_case(); a_distance.cmp(&b_distance)
}; });
items.sort_by(|a, b| { }
let a_str = get_value(a); SortBy::Ascending => {
let b_str = get_value(b); items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
let a_score = matcher.fuzzy_match(a_str, prefix).unwrap_or_default(); }
let b_score = matcher.fuzzy_match(b_str, prefix).unwrap_or_default(); SortBy::None => {}
b_score.cmp(&a_score).then(a_str.cmp(b_str)) };
});
} else {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
items items
} }

View File

@ -1,10 +1,17 @@
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_parser::trim_quotes_str; use nu_parser::trim_quotes_str;
use nu_protocol::{CompletionAlgorithm, CompletionSort}; use nu_protocol::CompletionAlgorithm;
use std::fmt::Display; use std::fmt::Display;
#[derive(Copy, Clone)]
pub enum SortBy {
LevenshteinDistance,
Ascending,
None,
}
/// Describes how suggestions should be matched. /// Describes how suggestions should be matched.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug)]
pub enum MatchAlgorithm { pub enum MatchAlgorithm {
/// Only show suggestions which begin with the given input /// Only show suggestions which begin with the given input
/// ///
@ -89,7 +96,6 @@ pub struct CompletionOptions {
pub case_sensitive: bool, pub case_sensitive: bool,
pub positional: bool, pub positional: bool,
pub match_algorithm: MatchAlgorithm, pub match_algorithm: MatchAlgorithm,
pub sort: CompletionSort,
} }
impl Default for CompletionOptions { impl Default for CompletionOptions {
@ -98,7 +104,6 @@ impl Default for CompletionOptions {
case_sensitive: true, case_sensitive: true,
positional: true, positional: true,
match_algorithm: MatchAlgorithm::Prefix, match_algorithm: MatchAlgorithm::Prefix,
sort: Default::default(),
} }
} }
} }

View File

@ -1,13 +1,13 @@
use crate::completions::{ use crate::completions::{
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm, completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
SemanticSuggestion, SemanticSuggestion, SortBy,
}; };
use nu_engine::eval_call; use nu_engine::eval_call;
use nu_protocol::{ use nu_protocol::{
ast::{Argument, Call, Expr, Expression}, ast::{Argument, Call, Expr, Expression},
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
CompletionSort, PipelineData, Span, Type, Value, PipelineData, Span, Type, Value,
}; };
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use std::collections::HashMap; use std::collections::HashMap;
@ -18,6 +18,7 @@ pub struct CustomCompletion {
stack: Stack, stack: Stack,
decl_id: usize, decl_id: usize,
line: String, line: String,
sort_by: SortBy,
} }
impl CustomCompletion { impl CustomCompletion {
@ -26,6 +27,7 @@ impl CustomCompletion {
stack, stack,
decl_id, decl_id,
line, line,
sort_by: SortBy::None,
} }
} }
} }
@ -91,6 +93,10 @@ impl Completer for CustomCompletion {
.and_then(|val| val.as_bool().ok()) .and_then(|val| val.as_bool().ok())
.unwrap_or(false); .unwrap_or(false);
if should_sort {
self.sort_by = SortBy::Ascending;
}
custom_completion_options = Some(CompletionOptions { custom_completion_options = Some(CompletionOptions {
case_sensitive: options case_sensitive: options
.get("case_sensitive") .get("case_sensitive")
@ -108,11 +114,6 @@ impl Completer for CustomCompletion {
.unwrap_or(MatchAlgorithm::Prefix), .unwrap_or(MatchAlgorithm::Prefix),
None => completion_options.match_algorithm, None => completion_options.match_algorithm,
}, },
sort: if should_sort {
CompletionSort::Alphabetical
} else {
CompletionSort::Smart
},
}); });
} }
@ -123,11 +124,12 @@ impl Completer for CustomCompletion {
}) })
.unwrap_or_default(); .unwrap_or_default();
let options = custom_completion_options let suggestions = if let Some(custom_completion_options) = custom_completion_options {
.as_ref() filter(&prefix, suggestions, &custom_completion_options)
.unwrap_or(completion_options); } else {
let suggestions = filter(&prefix, suggestions, completion_options); filter(&prefix, suggestions, completion_options)
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, options) };
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
} }
} }

View File

@ -48,12 +48,14 @@ impl Completer for DirectoryCompletion {
.map(move |x| SemanticSuggestion { .map(move |x| SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: x.1, value: x.1,
description: None,
style: x.2, style: x.2,
extra: None,
span: reedline::Span { span: reedline::Span {
start: x.0.start - offset, start: x.0.start - offset,
end: x.0.end - offset, end: x.0.end - offset,
}, },
..Suggestion::default() append_whitespace: false,
}, },
// TODO???? // TODO????
kind: None, kind: None,

View File

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

View File

@ -53,12 +53,14 @@ impl Completer for FileCompletion {
.map(move |x| SemanticSuggestion { .map(move |x| SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: x.1, value: x.1,
description: None,
style: x.2, style: x.2,
extra: None,
span: reedline::Span { span: reedline::Span {
start: x.0.start - offset, start: x.0.start - offset,
end: x.0.end - offset, end: x.0.end - offset,
}, },
..Suggestion::default() append_whitespace: false,
}, },
// TODO???? // TODO????
kind: None, kind: None,

View File

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

View File

@ -13,7 +13,7 @@ mod variable_completions;
pub use base::{Completer, SemanticSuggestion, SuggestionKind}; pub use base::{Completer, SemanticSuggestion, SuggestionKind};
pub use command_completions::CommandCompletion; pub use command_completions::CommandCompletion;
pub use completer::NuCompleter; pub use completer::NuCompleter;
pub use completion_options::{CompletionOptions, MatchAlgorithm}; pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
pub use custom_completions::CustomCompletion; pub use custom_completions::CustomCompletion;
pub use directory_completions::DirectoryCompletion; pub use directory_completions::DirectoryCompletion;
pub use dotnu_completions::DotNuCompletion; pub use dotnu_completions::DotNuCompletion;

View File

@ -9,7 +9,7 @@ use nu_protocol::{
use reedline::Suggestion; use reedline::Suggestion;
use std::str; use std::str;
use super::completion_common::sort_suggestions; use super::{completion_common::sort_suggestions, SortBy};
#[derive(Clone)] #[derive(Clone)]
pub struct VariableCompletion { pub struct VariableCompletion {
@ -72,7 +72,7 @@ impl Completer for VariableCompletion {
} }
} }
return sort_suggestions(&prefix_str, output, options); return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} else { } else {
// No nesting provided, return all env vars // No nesting provided, return all env vars
@ -85,15 +85,18 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: env_var.0, value: env_var.0,
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(SuggestionKind::Type(env_var.1.get_type())), kind: Some(SuggestionKind::Type(env_var.1.get_type())),
}); });
} }
} }
return sort_suggestions(&prefix_str, output, options); return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
@ -117,7 +120,7 @@ impl Completer for VariableCompletion {
} }
} }
return sort_suggestions(&prefix_str, output, options); return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
@ -139,7 +142,7 @@ impl Completer for VariableCompletion {
} }
} }
return sort_suggestions(&prefix_str, output, options); return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
} }
@ -154,8 +157,11 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: builtin.to_string(), value: builtin.to_string(),
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
// TODO is there a way to get the VarId to get the type??? // TODO is there a way to get the VarId to get the type???
kind: None, kind: None,
@ -178,8 +184,11 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(), value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(SuggestionKind::Type( kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(), working_set.get_variable(*v.1).ty.clone(),
@ -206,8 +215,11 @@ impl Completer for VariableCompletion {
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: String::from_utf8_lossy(v.0).to_string(), value: String::from_utf8_lossy(v.0).to_string(),
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(SuggestionKind::Type( kind: Some(SuggestionKind::Type(
working_set.get_variable(*v.1).ty.clone(), working_set.get_variable(*v.1).ty.clone(),
@ -217,7 +229,7 @@ impl Completer for VariableCompletion {
} }
} }
output = sort_suggestions(&prefix_str, output, options); output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended? output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
@ -243,8 +255,11 @@ fn nested_suggestions(
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: col.clone(), value: col.clone(),
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(kind.clone()), kind: Some(kind.clone()),
}); });
@ -257,8 +272,11 @@ fn nested_suggestions(
output.push(SemanticSuggestion { output.push(SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value: column_name, value: column_name,
description: None,
style: None,
extra: None,
span: current_span, span: current_span,
..Suggestion::default() append_whitespace: false,
}, },
kind: Some(kind.clone()), kind: Some(kind.clone()),
}); });

View File

@ -192,8 +192,7 @@ pub fn add_plugin_file(
} else if let Some(mut plugin_path) = nu_path::config_dir() { } else if let Some(mut plugin_path) = nu_path::config_dir() {
// Path to store plugins signatures // Path to store plugins signatures
plugin_path.push(storage_path); plugin_path.push(storage_path);
let mut plugin_path = let mut plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path.into());
plugin_path.push(PLUGIN_FILE); plugin_path.push(PLUGIN_FILE);
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path); let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
engine_state.plugin_path = Some(plugin_path); engine_state.plugin_path = Some(plugin_path);
@ -248,7 +247,7 @@ pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> O
HistoryFileFormat::PlainText => HISTORY_FILE_TXT, HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE, HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
}); });
history_path.into() history_path
}) })
} }

View File

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

View File

@ -76,21 +76,12 @@ pub fn evaluate_file(
trace!("parsing file: {}", file_path_str); trace!("parsing file: {}", file_path_str);
let block = parse(&mut working_set, Some(file_path_str), &file, false); 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 any parse errors were found, report the first error and exit.
if let Some(err) = working_set.parse_errors.first() { if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err); report_error(&working_set, err);
std::process::exit(1); 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. // 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) { for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
if block.signature.name == "main" { if block.signature.name == "main" {

View File

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

View File

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

View File

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

View File

@ -1,5 +1,3 @@
use std::sync::Arc;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
@ -35,10 +33,13 @@ impl Command for NuHighlight {
let head = call.head; let head = call.head;
let signals = engine_state.signals(); let signals = engine_state.signals();
let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone();
let highlighter = crate::NuHighlighter { let highlighter = crate::NuHighlighter {
engine_state: Arc::new(engine_state.clone()), engine_state,
stack: Arc::new(stack.clone()), stack: std::sync::Arc::new(stack.clone()),
config,
}; };
input.map( input.map(

View File

@ -77,19 +77,13 @@ pub(crate) fn add_menus(
mut line_editor: Reedline, mut line_editor: Reedline,
engine_state_ref: Arc<EngineState>, engine_state_ref: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: Arc<Config>, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
//log::trace!("add_menus: config: {:#?}", &config); //log::trace!("add_menus: config: {:#?}", &config);
line_editor = line_editor.clear_menus(); line_editor = line_editor.clear_menus();
for menu in &config.menus { for menu in &config.menus {
line_editor = add_menu( line_editor = add_menu(line_editor, menu, engine_state_ref.clone(), stack, config)?
line_editor,
menu,
engine_state_ref.clone(),
stack,
config.clone(),
)?
} }
// Checking if the default menus have been added from the config file // Checking if the default menus have been added from the config file
@ -106,7 +100,7 @@ pub(crate) fn add_menus(
if !config if !config
.menus .menus
.iter() .iter()
.any(|menu| menu.name.to_expanded_string("", &config) == name) .any(|menu| menu.name.to_expanded_string("", config) == name)
{ {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state); let mut working_set = StateWorkingSet::new(&engine_state);
@ -143,7 +137,7 @@ pub(crate) fn add_menus(
&menu, &menu,
new_engine_state_ref.clone(), new_engine_state_ref.clone(),
stack, stack,
config.clone(), config,
)?; )?;
} }
} }
@ -157,27 +151,27 @@ fn add_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: Arc<Config>, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
let layout = extract_value("layout", val, span)?.to_expanded_string("", &config); let layout = extract_value("layout", val, span)?.to_expanded_string("", config);
match layout.as_str() { match layout.as_str() {
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, &config), "columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config),
"list" => add_list_menu(line_editor, menu, engine_state, stack, config), "list" => add_list_menu(line_editor, menu, engine_state, stack, config),
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config), "ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
"description" => add_description_menu(line_editor, menu, engine_state, stack, config), "description" => add_description_menu(line_editor, menu, engine_state, stack, config),
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "columnar, list, ide or description".to_string(), expected: "columnar, list, ide or description".to_string(),
value: menu.menu_type.to_abbreviated_string(&config), value: menu.menu_type.to_abbreviated_string(config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}), }),
} }
} else { } else {
Err(ShellError::UnsupportedConfigValue { Err(ShellError::UnsupportedConfigValue {
expected: "only record type".to_string(), expected: "only record type".to_string(),
value: menu.menu_type.to_abbreviated_string(&config), value: menu.menu_type.to_abbreviated_string(config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}) })
} }
@ -193,29 +187,6 @@ fn get_style(record: &Record, name: &str, span: Span) -> Option<Style> {
}) })
} }
fn set_menu_style<M: MenuBuilder>(mut menu: M, style: &Value) -> M {
let span = style.span();
let Value::Record { val, .. } = &style else {
return menu;
};
if let Some(style) = get_style(val, "text", span) {
menu = menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
menu = menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
menu = menu.with_description_text_style(style);
}
if let Some(style) = get_style(val, "match_text", span) {
menu = menu.with_match_text_style(style);
}
if let Some(style) = get_style(val, "selected_match_text", span) {
menu = menu.with_selected_match_text_style(style);
}
menu
}
// Adds a columnar menu to the editor engine // Adds a columnar menu to the editor engine
pub(crate) fn add_columnar_menu( pub(crate) fn add_columnar_menu(
line_editor: Reedline, line_editor: Reedline,
@ -254,7 +225,24 @@ pub(crate) fn add_columnar_menu(
}; };
} }
columnar_menu = set_menu_style(columnar_menu, &menu.style); let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
if let Some(style) = get_style(val, "text", span) {
columnar_menu = columnar_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
columnar_menu = columnar_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
columnar_menu = columnar_menu.with_description_text_style(style);
}
if let Some(style) = get_style(val, "match_text", span) {
columnar_menu = columnar_menu.with_match_text_style(style);
}
if let Some(style) = get_style(val, "selected_match_text", span) {
columnar_menu = columnar_menu.with_selected_match_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", config);
columnar_menu = columnar_menu.with_marker(&marker); columnar_menu = columnar_menu.with_marker(&marker);
@ -294,9 +282,9 @@ pub(crate) fn add_list_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: Arc<Config>, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", &config); let name = menu.name.to_expanded_string("", config);
let mut list_menu = ListMenu::default().with_name(&name); let mut list_menu = ListMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -310,9 +298,20 @@ pub(crate) fn add_list_menu(
}; };
} }
list_menu = set_menu_style(list_menu, &menu.style); let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
if let Some(style) = get_style(val, "text", span) {
list_menu = list_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
list_menu = list_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
list_menu = list_menu.with_description_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", &config); let marker = menu.marker.to_expanded_string("", config);
list_menu = list_menu.with_marker(&marker); list_menu = list_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -338,7 +337,7 @@ pub(crate) fn add_list_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(&config), value: menu.source.to_abbreviated_string(config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }
@ -350,10 +349,10 @@ pub(crate) fn add_ide_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: Arc<Config>, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
let name = menu.name.to_expanded_string("", &config); let name = menu.name.to_expanded_string("", config);
let mut ide_menu = IdeMenu::default().with_name(&name); let mut ide_menu = IdeMenu::default().with_name(&name);
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
@ -418,7 +417,7 @@ pub(crate) fn add_ide_menu(
} else { } else {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "bool or record".to_string(), expected: "bool or record".to_string(),
value: border.to_abbreviated_string(&config), value: border.to_abbreviated_string(config),
span: border.span(), span: border.span(),
}); });
} }
@ -442,7 +441,7 @@ pub(crate) fn add_ide_menu(
_ => { _ => {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(), expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
value: description_mode.to_abbreviated_string(&config), value: description_mode.to_abbreviated_string(config),
span: description_mode.span(), span: description_mode.span(),
}); });
} }
@ -491,9 +490,26 @@ pub(crate) fn add_ide_menu(
}; };
} }
ide_menu = set_menu_style(ide_menu, &menu.style); let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
if let Some(style) = get_style(val, "text", span) {
ide_menu = ide_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
ide_menu = ide_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
ide_menu = ide_menu.with_description_text_style(style);
}
if let Some(style) = get_style(val, "match_text", span) {
ide_menu = ide_menu.with_match_text_style(style);
}
if let Some(style) = get_style(val, "selected_match_text", span) {
ide_menu = ide_menu.with_selected_match_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", &config); let marker = menu.marker.to_expanded_string("", config);
ide_menu = ide_menu.with_marker(&marker); ide_menu = ide_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -519,7 +535,7 @@ pub(crate) fn add_ide_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(&config), value: menu.source.to_abbreviated_string(config),
span, span,
}), }),
} }
@ -531,9 +547,9 @@ pub(crate) fn add_description_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: Arc<Config>, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", &config); let name = menu.name.to_expanded_string("", config);
let mut description_menu = DescriptionMenu::default().with_name(&name); let mut description_menu = DescriptionMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -579,9 +595,20 @@ pub(crate) fn add_description_menu(
}; };
} }
description_menu = set_menu_style(description_menu, &menu.style); let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
if let Some(style) = get_style(val, "text", span) {
description_menu = description_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
description_menu = description_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
description_menu = description_menu.with_description_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", &config); let marker = menu.marker.to_expanded_string("", config);
description_menu = description_menu.with_marker(&marker); description_menu = description_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -590,7 +617,7 @@ pub(crate) fn add_description_menu(
let span = menu.source.span(); let span = menu.source.span();
match &menu.source { match &menu.source {
Value::Nothing { .. } => { Value::Nothing { .. } => {
let completer = Box::new(NuHelpCompleter::new(engine_state, config)); let completer = Box::new(NuHelpCompleter::new(engine_state));
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter { Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(description_menu), menu: Box::new(description_menu),
completer, completer,
@ -611,7 +638,7 @@ pub(crate) fn add_description_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "closure or omitted value".to_string(), expected: "closure or omitted value".to_string(),
value: menu.source.to_abbreviated_string(&config), value: menu.source.to_abbreviated_string(config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }

View File

@ -268,9 +268,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = engine_state.merge_env(&mut stack, cwd) { if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
// Check whether $env.NU_USE_IR is set, so that the user can change it in the REPL
// Temporary while IR eval is optional
stack.use_ir = stack.has_env_var(engine_state, "NU_USE_IR");
perf!("merge env", start_time, use_color); perf!("merge env", start_time, use_color);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
@ -297,7 +294,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("env-change hook", start_time, use_color); perf!("env-change hook", start_time, use_color);
let engine_reference = Arc::new(engine_state.clone()); let engine_reference = Arc::new(engine_state.clone());
let config = stack.get_config(engine_state); let config = engine_state.get_config();
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Find the configured cursor shapes for each mode // Find the configured cursor shapes for each mode
@ -323,6 +320,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
// STACK-REFERENCE 1 // STACK-REFERENCE 1
stack: stack_arc.clone(), stack: stack_arc.clone(),
config: config.clone(),
})) }))
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
@ -338,7 +336,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_cwd(Some( .with_cwd(Some(
engine_state engine_state
.cwd(None) .cwd(None)
.map(|cwd| cwd.into_std_path_buf())
.unwrap_or_default() .unwrap_or_default()
.to_string_lossy() .to_string_lossy()
.to_string(), .to_string(),
@ -1337,26 +1334,20 @@ fn are_session_ids_in_sync() {
#[cfg(test)] #[cfg(test)]
mod test_auto_cd { mod test_auto_cd {
use super::{do_auto_cd, parse_operation, ReplOperation}; use super::{do_auto_cd, parse_operation, ReplOperation};
use nu_path::AbsolutePath;
use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::engine::{EngineState, Stack};
use std::path::Path;
use tempfile::tempdir; use tempfile::tempdir;
/// Create a symlink. Works on both Unix and Windows. /// Create a symlink. Works on both Unix and Windows.
#[cfg(any(unix, windows))] #[cfg(any(unix, windows))]
fn symlink( fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> std::io::Result<()> {
original: impl AsRef<AbsolutePath>,
link: impl AsRef<AbsolutePath>,
) -> std::io::Result<()> {
let original = original.as_ref();
let link = link.as_ref();
#[cfg(unix)] #[cfg(unix)]
{ {
std::os::unix::fs::symlink(original, link) std::os::unix::fs::symlink(original, link)
} }
#[cfg(windows)] #[cfg(windows)]
{ {
if original.is_dir() { if original.as_ref().is_dir() {
std::os::windows::fs::symlink_dir(original, link) std::os::windows::fs::symlink_dir(original, link)
} else { } else {
std::os::windows::fs::symlink_file(original, link) std::os::windows::fs::symlink_file(original, link)
@ -1368,11 +1359,11 @@ mod test_auto_cd {
/// `before`, and after `input` is parsed and evaluated, PWD should be /// `before`, and after `input` is parsed and evaluated, PWD should be
/// changed to `after`. /// changed to `after`.
#[track_caller] #[track_caller]
fn check(before: impl AsRef<AbsolutePath>, input: &str, after: impl AsRef<AbsolutePath>) { fn check(before: impl AsRef<Path>, input: &str, after: impl AsRef<Path>) {
// Setup EngineState and Stack. // Setup EngineState and Stack.
let mut engine_state = EngineState::new(); let mut engine_state = EngineState::new();
let mut stack = Stack::new(); let mut stack = Stack::new();
stack.set_cwd(before.as_ref()).unwrap(); stack.set_cwd(before).unwrap();
// Parse the input. It must be an auto-cd operation. // Parse the input. It must be an auto-cd operation.
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap(); let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
@ -1388,66 +1379,54 @@ mod test_auto_cd {
// don't have to be byte-wise equal (on Windows, the 8.3 filename // don't have to be byte-wise equal (on Windows, the 8.3 filename
// conversion messes things up), // conversion messes things up),
let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap(); let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap();
let after = std::fs::canonicalize(after.as_ref()).unwrap(); let after = std::fs::canonicalize(after).unwrap();
assert_eq!(updated_cwd, after); assert_eq!(updated_cwd, after);
} }
#[test] #[test]
fn auto_cd_root() { fn auto_cd_root() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let root = if cfg!(windows) { r"C:\" } else { "/" };
check(&tempdir, root, root);
let input = if cfg!(windows) { r"C:\" } else { "/" };
let root = AbsolutePath::try_new(input).unwrap();
check(tempdir, input, root);
} }
#[test] #[test]
fn auto_cd_tilde() { fn auto_cd_tilde() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
let home = nu_path::home_dir().unwrap(); let home = nu_path::home_dir().unwrap();
check(tempdir, "~", home); check(&tempdir, "~", home);
} }
#[test] #[test]
fn auto_cd_dot() { fn auto_cd_dot() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); check(&tempdir, ".", &tempdir);
check(tempdir, ".", tempdir);
} }
#[test] #[test]
fn auto_cd_double_dot() { fn auto_cd_double_dot() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let dir = tempdir.path().join("foo");
let dir = tempdir.join("foo");
std::fs::create_dir_all(&dir).unwrap(); std::fs::create_dir_all(&dir).unwrap();
check(dir, "..", tempdir); check(dir, "..", &tempdir);
} }
#[test] #[test]
fn auto_cd_triple_dot() { fn auto_cd_triple_dot() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let dir = tempdir.path().join("foo").join("bar");
let dir = tempdir.join("foo").join("bar");
std::fs::create_dir_all(&dir).unwrap(); std::fs::create_dir_all(&dir).unwrap();
check(dir, "...", tempdir); check(dir, "...", &tempdir);
} }
#[test] #[test]
fn auto_cd_relative() { fn auto_cd_relative() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let foo = tempdir.path().join("foo");
let bar = tempdir.path().join("bar");
let foo = tempdir.join("foo");
let bar = tempdir.join("bar");
std::fs::create_dir_all(&foo).unwrap(); std::fs::create_dir_all(&foo).unwrap();
std::fs::create_dir_all(&bar).unwrap(); std::fs::create_dir_all(&bar).unwrap();
let input = if cfg!(windows) { r"..\bar" } else { "../bar" }; let input = if cfg!(windows) { r"..\bar" } else { "../bar" };
check(foo, input, bar); check(foo, input, bar);
} }
@ -1455,35 +1434,32 @@ mod test_auto_cd {
#[test] #[test]
fn auto_cd_trailing_slash() { fn auto_cd_trailing_slash() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let dir = tempdir.path().join("foo");
let dir = tempdir.join("foo");
std::fs::create_dir_all(&dir).unwrap(); std::fs::create_dir_all(&dir).unwrap();
let input = if cfg!(windows) { r"foo\" } else { "foo/" }; let input = if cfg!(windows) { r"foo\" } else { "foo/" };
check(tempdir, input, dir); check(&tempdir, input, dir);
} }
#[test] #[test]
fn auto_cd_symlink() { fn auto_cd_symlink() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let dir = tempdir.path().join("foo");
let dir = tempdir.join("foo");
std::fs::create_dir_all(&dir).unwrap(); std::fs::create_dir_all(&dir).unwrap();
let link = tempdir.join("link"); let link = tempdir.path().join("link");
symlink(&dir, &link).unwrap(); symlink(&dir, &link).unwrap();
let input = if cfg!(windows) { r".\link" } else { "./link" }; let input = if cfg!(windows) { r".\link" } else { "./link" };
check(tempdir, input, link); check(&tempdir, input, link);
} }
#[test] #[test]
#[should_panic(expected = "was not parsed into an auto-cd operation")] #[should_panic(expected = "was not parsed into an auto-cd operation")]
fn auto_cd_nonexistent_directory() { fn auto_cd_nonexistent_directory() {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap(); let dir = tempdir.path().join("foo");
let dir = tempdir.join("foo");
let input = if cfg!(windows) { r"foo\" } else { "foo/" }; let input = if cfg!(windows) { r"foo\" } else { "foo/" };
check(tempdir, input, dir); check(&tempdir, input, dir);
} }
} }

View File

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

View File

@ -262,11 +262,6 @@ fn evaluate_source(
return Ok(Some(1)); return Ok(Some(1));
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render()) (output, working_set.render())
}; };
@ -321,10 +316,16 @@ mod test {
let env = engine_state.render_env_vars(); let env = engine_state.render_env_vars();
assert!(matches!(env.get("FOO"), Some(&Value::String { val, .. }) if val == "foo")); assert!(
assert!(matches!(env.get("SYMBOLS"), Some(&Value::String { val, .. }) if val == symbols)); matches!(env.get(&"FOO".to_string()), Some(&Value::String { val, .. }) if val == "foo")
assert!(matches!(env.get(symbols), Some(&Value::String { val, .. }) if val == "symbols")); );
assert!(env.contains_key("PWD")); 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_eq!(env.len(), 4); assert_eq!(env.len(), 4);
} }
} }

View File

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

View File

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

View File

@ -32,6 +32,7 @@ fn completer() -> NuCompleter {
fn completer_strings() -> NuCompleter { fn completer_strings() -> NuCompleter {
// Create a new engine // Create a new engine
let (dir, _, mut engine, mut stack) = new_engine(); let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example // Add record value as example
let record = r#"def animals [] { ["cat", "dog", "eel" ] } let record = r#"def animals [] { ["cat", "dog", "eel" ] }
def my-command [animal: string@animals] { print $animal }"#; def my-command [animal: string@animals] { print $animal }"#;
@ -89,12 +90,14 @@ fn subcommand_completer() -> NuCompleter {
// Create a new engine // Create a new engine
let (dir, _, mut engine, mut stack) = new_engine(); let (dir, _, mut engine, mut stack) = new_engine();
// Use fuzzy matching, because subcommands are sorted by Levenshtein distance,
// and that's not very useful with prefix matching
let commands = r#" let commands = r#"
$env.config.completions.algorithm = "fuzzy" $env.config.completions.algorithm = "fuzzy"
def foo [] {} def foo [] {}
def "foo bar" [] {} def "foo bar" [] {}
def "foo abaz" [] {} def "foo abaz" [] {}
def "foo aabcrr" [] {} def "foo aabrr" [] {}
def food [] {} def food [] {}
"#; "#;
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok()); assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
@ -103,22 +106,6 @@ fn subcommand_completer() -> NuCompleter {
NuCompleter::new(Arc::new(engine), Arc::new(stack)) NuCompleter::new(Arc::new(engine), Arc::new(stack))
} }
/// Use fuzzy completions but sort in alphabetical order
#[fixture]
fn fuzzy_alpha_sort_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
let config = r#"
$env.config.completions.algorithm = "fuzzy"
$env.config.completions.sort = "alphabetical"
"#;
assert!(support::merge_input(config.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[test] #[test]
fn variables_dollar_sign_with_variablecompletion() { fn variables_dollar_sign_with_variablecompletion() {
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
@ -136,28 +123,28 @@ fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter
let suggestions = completer.complete("tst --", 6); let suggestions = completer.complete("tst --", 6);
let expected: Vec<String> = vec!["--help".into(), "--mod".into()]; let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
// dbg!(&expected, &suggestions); // dbg!(&expected, &suggestions);
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) { fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -", 5); let suggestions = completer.complete("tst -", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()]; let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) { fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-c ", 4); let suggestions = completer_strings.complete("my-c ", 4);
let expected: Vec<String> = vec!["my-command".into()]; let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) { fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11); let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
@ -166,7 +153,7 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
) { ) {
let suggestions = completer_strings.complete("my-command ", 11); let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[test] #[test]
@ -195,19 +182,19 @@ fn dotnu_completions() {
let completion_str = "source-env ".to_string(); let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
match_suggestions(&expected, &suggestions); match_suggestions(expected.clone(), suggestions);
// Test use completion // Test use completion
let completion_str = "use ".to_string(); let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
match_suggestions(&expected, &suggestions); match_suggestions(expected.clone(), suggestions);
// Test overlay use completion // Test overlay use completion
let completion_str = "overlay use ".to_string(); let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[test] #[test]
@ -271,22 +258,8 @@ fn file_completions() {
folder(dir.join(".hidden_folder")), 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 the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
// Test completions for a file // Test completions for a file
let target_dir = format!("cp {}", folder(dir.join("another"))); let target_dir = format!("cp {}", folder(dir.join("another")));
@ -296,91 +269,17 @@ fn file_completions() {
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))]; let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
// Match the results // Match the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
// Test completions for hidden files // Test completions for hidden files
let target_dir = format!("ls {}{MAIN_SEPARATOR}.", folder(dir.join(".hidden_folder"))); let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
let suggestions = completer.complete(&target_dir, target_dir.len()); let suggestions = completer.complete(&target_dir, target_dir.len());
let expected_paths: Vec<String> = let expected_paths: Vec<String> =
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))]; 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 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] #[test]
@ -404,7 +303,7 @@ fn partial_completions() {
]; ];
// Match the results // Match the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
// Test completions for the files whose name begin with "h" // Test completions for the files whose name begin with "h"
// and are present under directories whose names begin with "pa" // and are present under directories whose names begin with "pa"
@ -425,7 +324,7 @@ fn partial_completions() {
]; ];
// Match the results // 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" // Test completion for all files under directories whose names begin with "pa"
let dir_str = folder(dir.join("pa")); let dir_str = folder(dir.join("pa"));
@ -446,7 +345,7 @@ fn partial_completions() {
]; ];
// Match the results // Match the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
// Test completion for a single file // Test completion for a single file
let dir_str = file(dir.join("fi").join("so")); let dir_str = file(dir.join("fi").join("so"));
@ -457,7 +356,7 @@ fn partial_completions() {
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))]; let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
// Match the results // Match the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
// Test completion where there is a sneaky `..` in the path // Test completion where there is a sneaky `..` in the path
let dir_str = file(dir.join("par").join("..").join("fi").join("so")); let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
@ -493,7 +392,7 @@ fn partial_completions() {
]; ];
// Match the results // 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" // Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial-a").join("have")); let file_str = file(dir.join("partial-a").join("have"));
@ -507,7 +406,7 @@ fn partial_completions() {
]; ];
// Match the results // 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" // Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial-a").join("have_ext.")); let file_str = file(dir.join("partial-a").join("have_ext."));
@ -521,7 +420,7 @@ fn partial_completions() {
]; ];
// Match the results // Match the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
} }
#[test] #[test]
@ -556,16 +455,15 @@ fn command_ls_with_filecompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
let target_dir = "ls custom_completion."; let target_dir = "ls custom_completion.";
let suggestions = completer.complete(target_dir, target_dir.len()); let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()]; let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
fn command_open_with_filecompletion() { fn command_open_with_filecompletion() {
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
@ -598,14 +496,14 @@ fn command_open_with_filecompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
let target_dir = "open custom_completion."; let target_dir = "open custom_completion.";
let suggestions = completer.complete(target_dir, target_dir.len()); let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()]; let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -640,7 +538,7 @@ fn command_rm_with_globcompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -675,7 +573,7 @@ fn command_cp_with_globcompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -710,7 +608,7 @@ fn command_save_with_filecompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -745,7 +643,7 @@ fn command_touch_with_filecompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -780,7 +678,7 @@ fn command_watch_with_filecompletion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[rstest] #[rstest]
@ -788,19 +686,19 @@ fn subcommand_completions(mut subcommand_completer: NuCompleter) {
let prefix = "foo br"; let prefix = "foo br";
let suggestions = subcommand_completer.complete(prefix, prefix.len()); let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions( match_suggestions(
&vec!["foo bar".to_string(), "foo aabcrr".to_string()], vec!["foo bar".to_string(), "foo aabrr".to_string()],
&suggestions, suggestions,
); );
let prefix = "foo b"; let prefix = "foo b";
let suggestions = subcommand_completer.complete(prefix, prefix.len()); let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions( match_suggestions(
&vec![ vec![
"foo bar".to_string(), "foo bar".to_string(),
"foo aabcrr".to_string(),
"foo abaz".to_string(), "foo abaz".to_string(),
"foo aabrr".to_string(),
], ],
&suggestions, suggestions,
); );
} }
@ -823,10 +721,10 @@ fn file_completion_quoted() {
"`te#st.txt`".to_string(), "`te#st.txt`".to_string(),
"`te'st.txt`".to_string(), "`te'st.txt`".to_string(),
"`te(st).txt`".to_string(), "`te(st).txt`".to_string(),
format!("`{}`", folder("test dir")), format!("`{}`", folder("test dir".into())),
]; ];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
let dir: PathBuf = "test dir".into(); let dir: PathBuf = "test dir".into();
let target_dir = format!("open '{}'", folder(dir.clone())); let target_dir = format!("open '{}'", folder(dir.clone()));
@ -837,7 +735,7 @@ fn file_completion_quoted() {
format!("`{}`", file(dir.join("single quote"))), format!("`{}`", file(dir.join("single quote"))),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test] #[test]
@ -872,7 +770,7 @@ fn flag_completions() {
]; ];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[test] #[test]
@ -896,21 +794,8 @@ fn folder_with_directorycompletions() {
folder(dir.join(".hidden_folder")), 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 the results
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
} }
#[test] #[test]
@ -948,11 +833,11 @@ fn variables_completions() {
"plugin-path".into(), "plugin-path".into(),
"startup-time".into(), "startup-time".into(),
"temp-path".into(), "temp-path".into(),
"vendor-autoload-dirs".into(), "vendor-autoload-dir".into(),
]; ];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for $nu.h (filter) // Test completions for $nu.h (filter)
let suggestions = completer.complete("$nu.h", 5); let suggestions = completer.complete("$nu.h", 5);
@ -966,7 +851,7 @@ fn variables_completions() {
]; ];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for $nu.os-info // Test completions for $nu.os-info
let suggestions = completer.complete("$nu.os-info.", 12); let suggestions = completer.complete("$nu.os-info.", 12);
@ -978,7 +863,7 @@ fn variables_completions() {
"name".into(), "name".into(),
]; ];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for custom var // Test completions for custom var
let suggestions = completer.complete("$actor.", 7); let suggestions = completer.complete("$actor.", 7);
@ -988,7 +873,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["age".into(), "name".into()]; let expected: Vec<String> = vec!["age".into(), "name".into()];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for custom var (filtering) // Test completions for custom var (filtering)
let suggestions = completer.complete("$actor.n", 8); let suggestions = completer.complete("$actor.n", 8);
@ -998,7 +883,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["name".into()]; let expected: Vec<String> = vec!["name".into()];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for $env // Test completions for $env
let suggestions = completer.complete("$env.", 5); let suggestions = completer.complete("$env.", 5);
@ -1011,7 +896,7 @@ fn variables_completions() {
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()]; let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
// Test completions for $env // Test completions for $env
let suggestions = completer.complete("$env.T", 6); let suggestions = completer.complete("$env.T", 6);
@ -1021,12 +906,12 @@ fn variables_completions() {
let expected: Vec<String> = vec!["TEST".into()]; let expected: Vec<String> = vec!["TEST".into()];
// Match results // Match results
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
let suggestions = completer.complete("$", 1); let suggestions = completer.complete("$", 1);
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()]; let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[test] #[test]
@ -1045,7 +930,7 @@ fn alias_of_command_and_flags() {
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()]; 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] #[test]
@ -1064,7 +949,7 @@ fn alias_of_basic_command() {
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()]; 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] #[test]
@ -1086,7 +971,7 @@ fn alias_of_another_alias() {
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()]; 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> { fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
@ -1149,35 +1034,35 @@ fn unknown_command_completion() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions) match_suggestions(expected_paths, suggestions)
} }
#[rstest] #[rstest]
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) { fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h", 5); let suggestions = completer.complete("tst -h", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()]; let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) { fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c", 11); let suggestions = completer_strings.complete("my-command c", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) { fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c | ls", 11); let suggestions = completer_strings.complete("my-command c | ls", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) { fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h | ls", 5); let suggestions = completer.complete("tst -h | ls", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()]; let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[test] #[test]
@ -1211,88 +1096,77 @@ fn filecompletions_triggers_after_cursor() {
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
match_suggestions(&expected_paths, &suggestions); match_suggestions(expected_paths, suggestions);
} }
#[rstest] #[rstest]
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) { fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam ", 5); let suggestions = extern_completer.complete("spam ", 5);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) { fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo=", 11); let suggestions = extern_completer.complete("spam --foo=", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) { fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo ", 11); let suggestions = extern_completer.complete("spam --foo ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) { fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -f ", 8); let suggestions = extern_completer.complete("spam -f ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) { fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -b ", 8); let suggestions = extern_completer.complete("spam -b ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()]; let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn extern_complete_flags(mut extern_completer: NuCompleter) { fn extern_complete_flags(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -", 6); let suggestions = extern_completer.complete("spam -", 6);
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()]; let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) { fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 8); let suggestions = custom_completer.complete("cmd foo bar", 8);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()]; let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) { fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 8); let suggestions = custom_completer.complete("cmd foo bar", 8);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()]; let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) { fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar", 11); let suggestions = custom_completer.complete("cmd foo bar", 11);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()]; let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
} }
#[rstest] #[rstest]
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) { fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
let suggestions = custom_completer.complete("cmd foo bar ", 12); let suggestions = custom_completer.complete("cmd foo bar ", 12);
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()]; let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
match_suggestions(&expected, &suggestions); match_suggestions(expected, suggestions);
}
#[rstest]
fn sort_fuzzy_completions_in_alphabetical_order(mut fuzzy_alpha_sort_completer: NuCompleter) {
let suggestions = fuzzy_alpha_sort_completer.complete("ls nu", 5);
// Even though "nushell" is a better match, it should come second because
// the completions should be sorted in alphabetical order
match_suggestions(
&vec!["custom_completion.nu".into(), "nushell".into()],
&suggestions,
);
} }
#[ignore = "was reverted, still needs fixing"] #[ignore = "was reverted, still needs fixing"]

View File

@ -1,6 +1,5 @@
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_path::{AbsolutePathBuf, PathBuf};
use nu_protocol::{ use nu_protocol::{
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
@ -8,14 +7,14 @@ use nu_protocol::{
}; };
use nu_test_support::fs; use nu_test_support::fs;
use reedline::Suggestion; use reedline::Suggestion;
use std::path::MAIN_SEPARATOR; use std::path::{PathBuf, MAIN_SEPARATOR};
fn create_default_context() -> EngineState { fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()) nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
} }
// creates a new engine with the current path into the completions fixtures folder // creates a new engine with the current path into the completions fixtures folder
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("completions"); let dir = fs::fixtures().join("completions");
let dir_str = dir let dir_str = dir
@ -70,7 +69,7 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
} }
// creates a new engine with the current path into the completions fixtures folder // creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_dotnu_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions"); let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir let dir_str = dir
@ -115,7 +114,7 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("quoted_completions"); let dir = fs::fixtures().join("quoted_completions");
let dir_str = dir let dir_str = dir
@ -150,7 +149,7 @@ pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) { pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("partial_completions"); let dir = fs::fixtures().join("partial_completions");
let dir_str = dir let dir_str = dir
@ -186,7 +185,7 @@ pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
} }
// match a list of suggestions with the expected values // 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 expected_len = expected.len();
let suggestions_len = suggestions.len(); let suggestions_len = suggestions.len();
if expected_len != suggestions_len { if expected_len != suggestions_len {
@ -196,25 +195,26 @@ pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>)
Expected: {expected:#?}\n" Expected: {expected:#?}\n"
) )
} }
assert_eq!(
let suggestoins_str = suggestions expected,
.iter() suggestions
.map(|it| it.value.clone()) .into_iter()
.collect::<Vec<_>>(); .map(|it| it.value)
.collect::<Vec<_>>()
assert_eq!(expected, &suggestoins_str); );
} }
// append the separator to the converted path // append the separator to the converted path
pub fn folder(path: impl Into<PathBuf>) -> String { pub fn folder(path: PathBuf) -> String {
let mut converted_path = file(path); let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR); converted_path.push(MAIN_SEPARATOR);
converted_path converted_path
} }
// convert a given path to string // convert a given path to string
pub fn file(path: impl Into<PathBuf>) -> String { pub fn file(path: PathBuf) -> String {
path.into().into_os_string().into_string().unwrap() path.into_os_string().into_string().unwrap_or_default()
} }
// merge_input executes the given input into the engine // merge_input executes the given input into the engine
@ -223,7 +223,7 @@ pub fn merge_input(
input: &[u8], input: &[u8],
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
dir: AbsolutePathBuf, dir: PathBuf,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);

View File

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

View File

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

View File

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

View File

@ -1,28 +1,21 @@
use nu_path::AbsolutePathBuf;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Range, ShellError, Span, Value, Range, ShellError, Span, Value,
}; };
use std::ops::Bound; use std::{ops::Bound, path::PathBuf};
pub fn get_init_cwd() -> AbsolutePathBuf { pub fn get_init_cwd() -> PathBuf {
std::env::current_dir() std::env::current_dir().unwrap_or_else(|_| {
.ok() std::env::var("PWD")
.and_then(|path| AbsolutePathBuf::try_from(path).ok()) .map(Into::into)
.or_else(|| { .unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
std::env::var("PWD") })
.ok()
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
})
.or_else(nu_path::home_dir)
.expect("Failed to get current working directory")
} }
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> AbsolutePathBuf { pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
engine_state engine_state
.cwd(Some(stack)) .cwd(Some(stack))
.ok() .unwrap_or(crate::util::get_init_cwd())
.unwrap_or_else(get_init_cwd)
} }
type MakeRangeError = fn(&str, Span) -> ShellError; type MakeRangeError = fn(&str, Span) -> ShellError;

View File

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

View File

@ -239,7 +239,7 @@ fn to_html(
let partial = call.has_flag(engine_state, stack, "partial")?; let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?; let list = call.has_flag(engine_state, stack, "list")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?; let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = &stack.get_config(engine_state); let config = engine_state.get_config();
let vec_of_values = input.into_iter().collect::<Vec<Value>>(); let vec_of_values = input.into_iter().collect::<Vec<Value>>();
let headers = merge_descriptors(&vec_of_values); let headers = merge_descriptors(&vec_of_values);

View File

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

View File

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

View File

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

View File

@ -21,9 +21,7 @@ impl Command for Break {
fn extra_usage(&self) -> &str { fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check: 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 { fn command_type(&self) -> CommandType {

View File

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

View File

@ -21,9 +21,7 @@ impl Command for Continue {
fn extra_usage(&self) -> &str { fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check: 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 { fn command_type(&self) -> CommandType {

View File

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

View File

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

View File

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

View File

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

View File

@ -37,9 +37,6 @@ impl Command for Loop {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head; let head = call.head;
let block_id = call let block_id = call
.positional_nth(0) .positional_nth(0)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-color-config" name = "nu-color-config"
version = "0.96.2" version = "0.95.1"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.96.2" } nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.96.2" } nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-json = { path = "../nu-json", version = "0.96.2" } nu-json = { path = "../nu-json", version = "0.95.1" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.96.2" } nu-test-support = { path = "../nu-test-support", version = "0.95.1" }

View File

@ -1,5 +0,0 @@
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.

View File

@ -1,4 +1,3 @@
#![doc = include_str!("../README.md")]
mod color_config; mod color_config;
mod matching_brackets_style; mod matching_brackets_style;
mod nu_style; mod nu_style;

View File

@ -1,6 +1,6 @@
use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle}; use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle};
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_engine::ClosureEvalOnce; use nu_engine::{env::get_config, ClosureEvalOnce};
use nu_protocol::{ use nu_protocol::{
cli_error::CliError, cli_error::CliError,
engine::{Closure, EngineState, Stack, StateWorkingSet}, engine::{Closure, EngineState, Stack, StateWorkingSet},
@ -114,7 +114,7 @@ impl<'a> StyleComputer<'a> {
// The main constructor. // The main constructor.
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> { pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
let config = stack.get_config(engine_state); let config = get_config(engine_state, stack);
// Create the hashmap // Create the hashmap
#[rustfmt::skip] #[rustfmt::skip]

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-command" name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.96.2" version = "0.95.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,21 +13,21 @@ version = "0.96.2"
bench = false bench = false
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.2" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.95.1" }
nu-color-config = { path = "../nu-color-config", version = "0.96.2" } nu-color-config = { path = "../nu-color-config", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.96.2" } nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-glob = { path = "../nu-glob", version = "0.96.2" } nu-glob = { path = "../nu-glob", version = "0.95.1" }
nu-json = { path = "../nu-json", version = "0.96.2" } nu-json = { path = "../nu-json", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.96.2" } nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.96.2" } nu-path = { path = "../nu-path", version = "0.95.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.96.2" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.96.2" } nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-system = { path = "../nu-system", version = "0.96.2" } nu-system = { path = "../nu-system", version = "0.95.1" }
nu-table = { path = "../nu-table", version = "0.96.2" } nu-table = { path = "../nu-table", version = "0.95.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.96.2" } nu-term-grid = { path = "../nu-term-grid", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.96.2" } nu-utils = { path = "../nu-utils", version = "0.95.1" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
nuon = { path = "../nuon", version = "0.96.2" } nuon = { path = "../nuon", version = "0.95.1" }
alphanumeric-sort = { workspace = true } alphanumeric-sort = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
@ -137,10 +137,10 @@ sqlite = ["rusqlite"]
trash-support = ["trash"] trash-support = ["trash"]
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" }
nu-test-support = { path = "../nu-test-support", version = "0.96.2" } nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
dirs = { workspace = true } dirs-next = { workspace = true }
mockito = { workspace = true, default-features = false } mockito = { workspace = true, default-features = false }
quickcheck = { workspace = true } quickcheck = { workspace = true }
quickcheck_macros = { workspace = true } quickcheck_macros = { workspace = true }

View File

@ -1,7 +0,0 @@
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.

View File

@ -49,8 +49,10 @@ impl Command for BytesBuild {
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let mut output = vec![]; let mut output = vec![];
let eval_expression = get_eval_expression(engine_state); for val in call.rest_iter_flattened(0, |expr| {
for val in call.rest_iter_flattened(engine_state, stack, eval_expression, 0)? { let eval_expression = get_eval_expression(engine_state);
eval_expression(engine_state, stack, expr)
})? {
let val_span = val.span(); let val_span = val.span();
match val { match val {
Value::Binary { mut val, .. } => output.append(&mut val), Value::Binary { mut val, .. } => output.append(&mut val),

View File

@ -177,9 +177,11 @@ fn run_histogram(
match v { match v {
// parse record, and fill valid value to actual input. // parse record, and fill valid value to actual input.
Value::Record { val, .. } => { Value::Record { val, .. } => {
if let Some(v) = val.get(col_name) { for (c, v) in val.iter() {
if let Ok(v) = HashableValue::from_value(v.clone(), head_span) { if c == col_name {
inputs.push(v); if let Ok(v) = HashableValue::from_value(v.clone(), head_span) {
inputs.push(v);
}
} }
} }
} }

View File

@ -150,9 +150,13 @@ fn fill(
FillAlignment::Left FillAlignment::Left
}; };
let width = width_arg.unwrap_or(1); let width = if let Some(arg) = width_arg { arg } else { 1 };
let character = character_arg.unwrap_or_else(|| " ".to_string()); let character = if let Some(arg) = character_arg {
arg
} else {
" ".to_string()
};
let arg = Arguments { let arg = Arguments {
width, width,

View File

@ -379,47 +379,42 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
// If input is not a timestamp, try parsing it as a string // If input is not a timestamp, try parsing it as a string
let span = input.span(); let span = input.span();
match input {
let parse_as_string = |val: &str| { Value::String { val, .. } => {
match dateformat { match dateformat {
Some(dt) => match DateTime::parse_from_str(val, &dt.0) { Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
Ok(d) => Value::date ( d, head ), Ok(d) => Value::date ( d, head ),
Err(reason) => { Err(reason) => {
match NaiveDateTime::parse_from_str(val, &dt.0) { match NaiveDateTime::parse_from_str(val, &dt.0) {
Ok(d) => Value::date ( Ok(d) => Value::date (
DateTime::from_naive_utc_and_offset( DateTime::from_naive_utc_and_offset(
d, d,
*Local::now().offset(), *Local::now().offset(),
), ),
head,
),
Err(_) => {
Value::error (
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
head, head,
) ),
Err(_) => {
Value::error (
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
head,
)
}
} }
} }
} },
},
// Tries to automatically parse the date // Tries to automatically parse the date
// (i.e. without a format string) // (i.e. without a format string)
// and assumes the system's local timezone if none is specified // and assumes the system's local timezone if none is specified
None => match parse_date_from_string(val, span) { None => match parse_date_from_string(val, span) {
Ok(date) => Value::date ( Ok(date) => Value::date (
date, date,
span, span,
), ),
Err(err) => err, Err(err) => err,
}, },
}
} }
};
match input {
Value::String { val, .. } => parse_as_string(val),
Value::Int { val, .. } => parse_as_string(&val.to_string()),
// Propagate errors by explicitly matching them before the final case. // Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(), Value::Error { .. } => input.clone(),
other => Value::error( other => Value::error(
@ -580,24 +575,6 @@ mod tests {
assert_eq!(actual, expected) assert_eq!(actual, expected)
} }
#[test]
fn takes_int_with_formatstring() {
let date_int = Value::test_int(1_614_434_140);
let fmt_options = Some(DatetimeFormat("%s".to_string()));
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_int, &args, Span::test_data());
let expected = Value::date(
DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z").unwrap(),
Span::test_data(),
);
assert_eq!(actual, expected)
}
#[test] #[test]
fn takes_timestamp() { fn takes_timestamp() {
let date_str = Value::test_string("1614434140000000000"); let date_str = Value::test_string("1614434140000000000");

View File

@ -1,5 +1,3 @@
use std::sync::Arc;
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::{into_code, Config}; use nu_protocol::{into_code, Config};
@ -9,7 +7,7 @@ use num_format::ToFormattedString;
struct Arguments { struct Arguments {
decimals_value: Option<i64>, decimals_value: Option<i64>,
cell_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
config: Arc<Config>, config: Config,
} }
impl CmdArgument for Arguments { impl CmdArgument for Arguments {
@ -176,7 +174,7 @@ fn string_helper(
}) })
} }
} else { } else {
let config = stack.get_config(engine_state); let config = engine_state.get_config().clone();
let args = Arguments { let args = Arguments {
decimals_value, decimals_value,
cell_paths, cell_paths,

View File

@ -424,7 +424,11 @@ pub fn value_to_sql(value: Value) -> Result<Box<dyn rusqlite::ToSql>, ShellError
Value::Filesize { val, .. } => Box::new(val), Value::Filesize { val, .. } => Box::new(val),
Value::Duration { val, .. } => Box::new(val), Value::Duration { val, .. } => Box::new(val),
Value::Date { val, .. } => Box::new(val), Value::Date { val, .. } => Box::new(val),
Value::String { 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::Binary { val, .. } => Box::new(val), Value::Binary { val, .. } => Box::new(val),
Value::Nothing { .. } => Box::new(rusqlite::types::Null), Value::Nothing { .. } => Box::new(rusqlite::types::Null),
val => { val => {

View File

@ -33,7 +33,7 @@ impl Command for Debug {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let config = stack.get_config(engine_state); let config = engine_state.get_config().clone();
let raw = call.has_flag(engine_state, stack, "raw")?; let raw = call.has_flag(engine_state, stack, "raw")?;
// Should PipelineData::Empty result in an error here? // Should PipelineData::Empty result in an error here?

View File

@ -1,6 +1,6 @@
use nu_engine::{command_prelude::*, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_expression};
use nu_protocol::{ use nu_protocol::{
ast::{self, Argument, Block, Expr, Expression}, ast::{Argument, Block, Expr, Expression},
engine::Closure, engine::Closure,
}; };
@ -106,7 +106,7 @@ pub fn get_pipeline_elements(
fn get_arguments( fn get_arguments(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &ast::Call, call: &Call,
eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result<Value, ShellError>, eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result<Value, ShellError>,
) -> Vec<Value> { ) -> Vec<Value> {
let mut arg_value = vec![]; let mut arg_value = vec![];

View File

@ -28,10 +28,6 @@ impl Command for Metadata {
.category(Category::Debug) .category(Category::Debug)
} }
fn requires_ast_for_arguments(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -39,7 +35,7 @@ impl Command for Metadata {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let arg = call.positional_nth(stack, 0); let arg = call.positional_nth(0);
let head = call.head; let head = call.head;
match arg { match arg {

View File

@ -42,32 +42,32 @@ impl Command for MetadataSet {
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
mut input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let ds_fp: Option<String> = call.get_flag(engine_state, stack, "datasource-filepath")?; 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 ds_ls = call.has_flag(engine_state, stack, "datasource-ls")?;
let content_type: Option<String> = call.get_flag(engine_state, stack, "content-type")?; let content_type: Option<String> = call.get_flag(engine_state, stack, "content-type")?;
let signals = engine_state.signals().clone();
let mut metadata = match &mut input { let metadata = input
PipelineData::Value(_, metadata) .metadata()
| PipelineData::ListStream(_, metadata) .clone()
| PipelineData::ByteStream(_, metadata) => metadata.take().unwrap_or_default(), .unwrap_or_default()
PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: head }), .with_content_type(content_type);
};
if let Some(content_type) = content_type {
metadata.content_type = Some(content_type);
}
match (ds_fp, ds_ls) { match (ds_fp, ds_ls) {
(Some(path), false) => metadata.data_source = DataSource::FilePath(path.into()), (Some(path), false) => Ok(input.into_pipeline_data_with_metadata(
(None, true) => metadata.data_source = DataSource::Ls, head,
(Some(_), true) => (), // TODO: error here signals,
(None, false) => (), 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)),
} }
Ok(input.set_metadata(Some(metadata)))
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -85,9 +85,7 @@ impl Command for MetadataSet {
Example { Example {
description: "Set the metadata of a file path", description: "Set the metadata of a file path",
example: "'crates' | metadata set --content-type text/plain | metadata", example: "'crates' | metadata set --content-type text/plain | metadata",
result: Some(Value::test_record(record! { result: Some(Value::record(record!("content_type" => Value::string("text/plain", Span::test_data())), Span::test_data())),
"content_type" => Value::test_string("text/plain"),
})),
}, },
] ]
} }

View File

@ -10,7 +10,6 @@ mod profile;
mod timeit; mod timeit;
mod view; mod view;
mod view_files; mod view_files;
mod view_ir;
mod view_source; mod view_source;
mod view_span; mod view_span;
@ -26,6 +25,5 @@ pub use profile::DebugProfile;
pub use timeit::TimeIt; pub use timeit::TimeIt;
pub use view::View; pub use view::View;
pub use view_files::ViewFiles; pub use view_files::ViewFiles;
pub use view_ir::ViewIr;
pub use view_source::ViewSource; pub use view_source::ViewSource;
pub use view_span::ViewSpan; pub use view_span::ViewSpan;

View File

@ -1,8 +1,5 @@
use nu_engine::{command_prelude::*, ClosureEvalOnce}; use nu_engine::{command_prelude::*, ClosureEvalOnce};
use nu_protocol::{ use nu_protocol::{debugger::Profiler, engine::Closure};
debugger::{Profiler, ProfilerOptions},
engine::Closure,
};
#[derive(Clone)] #[derive(Clone)]
pub struct DebugProfile; pub struct DebugProfile;
@ -31,7 +28,6 @@ impl Command for DebugProfile {
Some('v'), Some('v'),
) )
.switch("expr", "Collect expression types", Some('x')) .switch("expr", "Collect expression types", Some('x'))
.switch("instructions", "Collect IR instructions", Some('i'))
.switch("lines", "Collect line numbers", Some('l')) .switch("lines", "Collect line numbers", Some('l'))
.named( .named(
"max-depth", "max-depth",
@ -95,23 +91,19 @@ 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_expanded_source = call.has_flag(engine_state, stack, "expanded-source")?;
let collect_values = call.has_flag(engine_state, stack, "values")?; let collect_values = call.has_flag(engine_state, stack, "values")?;
let collect_exprs = call.has_flag(engine_state, stack, "expr")?; 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 collect_lines = call.has_flag(engine_state, stack, "lines")?;
let max_depth = call let max_depth = call
.get_flag(engine_state, stack, "max-depth")? .get_flag(engine_state, stack, "max-depth")?
.unwrap_or(2); .unwrap_or(2);
let profiler = Profiler::new( let profiler = Profiler::new(
ProfilerOptions { max_depth,
max_depth, collect_spans,
collect_spans, true,
collect_source: true, collect_expanded_source,
collect_expanded_source, collect_values,
collect_values, collect_exprs,
collect_exprs, collect_lines,
collect_instructions,
collect_lines,
},
call.span(), call.span(),
); );

View File

@ -32,10 +32,6 @@ impl Command for TimeIt {
vec!["timing", "timer", "benchmark", "measure"] vec!["timing", "timer", "benchmark", "measure"]
} }
fn requires_ast_for_arguments(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -43,14 +39,13 @@ impl Command for TimeIt {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// reset outdest, so the command can write to stdout and stderr. let command_to_run = call.positional_nth(0);
let stack = &mut stack.push_redirection(None, None);
let command_to_run = call.positional_nth(stack, 0);
// Get the start time after all other computation has been done. // Get the start time after all other computation has been done.
let start_time = Instant::now(); let start_time = Instant::now();
// reset outdest, so the command can write to stdout and stderr.
let stack = &mut stack.push_redirection(None, None);
if let Some(command_to_run) = command_to_run { if let Some(command_to_run) = command_to_run {
if let Some(block_id) = command_to_run.as_block() { if let Some(block_id) = command_to_run.as_block() {
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);
@ -58,8 +53,7 @@ impl Command for TimeIt {
eval_block(engine_state, stack, block, input)? eval_block(engine_state, stack, block, input)?
} else { } else {
let eval_expression_with_input = get_eval_expression_with_input(engine_state); let eval_expression_with_input = get_eval_expression_with_input(engine_state);
let expression = &command_to_run.clone(); eval_expression_with_input(engine_state, stack, command_to_run, input)?.0
eval_expression_with_input(engine_state, stack, expression, input)?.0
} }
} else { } else {
PipelineData::empty() PipelineData::empty()

View File

@ -1,171 +0,0 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct ViewIr;
impl Command for ViewIr {
fn name(&self) -> &str {
"view ir"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"target",
SyntaxShape::Any,
"The name or block to view compiled code for.",
)
.switch(
"json",
"Dump the raw block data as JSON (unstable).",
Some('j'),
)
.switch(
"decl-id",
"Integer is a declaration ID rather than a block ID.",
Some('d'),
)
.input_output_type(Type::Nothing, Type::String)
.category(Category::Debug)
}
fn usage(&self) -> &str {
"View the compiled IR code for a block of code."
}
fn extra_usage(&self) -> &str {
"
The target can be a closure, the name of a custom command, or an internal block
ID. Closure literals within IR dumps often reference the block by ID (e.g.
`closure(3231)`), so this provides an easy way to read the IR of any embedded
closures.
The --decl-id option is provided to use a declaration ID instead, which can be
found on `call` instructions. This is sometimes better than using the name, as
the declaration may not be in scope.
"
.trim()
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let target: Value = call.req(engine_state, stack, 0)?;
let json = call.has_flag(engine_state, stack, "json")?;
let is_decl_id = call.has_flag(engine_state, stack, "decl-id")?;
let block_id = match target {
Value::Closure { ref val, .. } => val.block_id,
// Decl by name
Value::String { ref val, .. } => {
if let Some(decl_id) = engine_state.find_decl(val.as_bytes(), &[]) {
let decl = engine_state.get_decl(decl_id);
decl.block_id().ok_or_else(|| ShellError::GenericError {
error: format!("Can't view IR for `{val}`"),
msg: "not a custom command".into(),
span: Some(target.span()),
help: Some("internal commands don't have Nushell source code".into()),
inner: vec![],
})?
} else {
return Err(ShellError::GenericError {
error: format!("Can't view IR for `{val}`"),
msg: "can't find a command with this name".into(),
span: Some(target.span()),
help: None,
inner: vec![],
});
}
}
// Decl by ID - IR dump always shows name of decl, but sometimes it isn't in scope
Value::Int { val, .. } if is_decl_id => {
let decl_id = val
.try_into()
.ok()
.filter(|id| *id < engine_state.num_decls())
.ok_or_else(|| ShellError::IncorrectValue {
msg: "not a valid decl id".into(),
val_span: target.span(),
call_span: call.head,
})?;
let decl = engine_state.get_decl(decl_id);
decl.block_id().ok_or_else(|| ShellError::GenericError {
error: format!("Can't view IR for `{}`", decl.name()),
msg: "not a custom command".into(),
span: Some(target.span()),
help: Some("internal commands don't have Nushell source code".into()),
inner: vec![],
})?
}
// Block by ID - often shows up in IR
Value::Int { val, .. } => val.try_into().map_err(|_| ShellError::IncorrectValue {
msg: "not a valid block id".into(),
val_span: target.span(),
call_span: call.head,
})?,
// Pass through errors
Value::Error { error, .. } => return Err(*error),
_ => {
return Err(ShellError::TypeMismatch {
err_message: "expected closure, string, or int".into(),
span: call.head,
})
}
};
let Some(block) = engine_state.try_get_block(block_id) else {
return Err(ShellError::GenericError {
error: format!("Unknown block ID: {block_id}"),
msg: "ensure the block ID is correct and try again".into(),
span: Some(target.span()),
help: None,
inner: vec![],
});
};
let ir_block = block
.ir_block
.as_ref()
.ok_or_else(|| ShellError::GenericError {
error: "Can't view IR for this block".into(),
msg: "block is missing compiled representation".into(),
span: block.span,
help: Some("the IrBlock is probably missing due to a compilation error".into()),
inner: vec![],
})?;
let formatted = if json {
let formatted_instructions = ir_block
.instructions
.iter()
.map(|instruction| {
instruction
.display(engine_state, &ir_block.data)
.to_string()
})
.collect::<Vec<_>>();
serde_json::to_string_pretty(&serde_json::json!({
"block_id": block_id,
"span": block.span,
"ir_block": ir_block,
"formatted_instructions": formatted_instructions,
}))
.map_err(|err| ShellError::GenericError {
error: "JSON serialization failed".into(),
msg: err.to_string(),
span: Some(call.head),
help: None,
inner: vec![],
})?
} else {
format!("{}", ir_block.display(engine_state))
};
Ok(Value::string(formatted, call.head).into_pipeline_data())
}
}

View File

@ -31,7 +31,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
All, All,
Any, Any,
Append, Append,
Chunks,
Columns, Columns,
Compact, Compact,
Default, Default,
@ -155,7 +154,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
TimeIt, TimeIt,
View, View,
ViewFiles, ViewFiles,
ViewIr,
ViewSource, ViewSource,
ViewSpan, ViewSpan,
}; };
@ -291,6 +289,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
ToText, ToText,
ToToml, ToToml,
ToTsv, ToTsv,
Touch,
Upsert, Upsert,
Where, Where,
ToXml, ToXml,
@ -397,7 +396,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
RandomFloat, RandomFloat,
RandomInt, RandomInt,
RandomUuid, RandomUuid,
RandomBinary
}; };
// Generators // Generators

View File

@ -60,13 +60,12 @@ impl Command for ConfigEnv {
let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?; let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?; let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
let cwd = engine_state.cwd(Some(stack))?; let cwd = engine_state.cwd(Some(stack))?;
let editor_executable = crate::which(&editor_name, &paths, cwd.as_ref()).ok_or( let editor_executable =
ShellError::ExternalCommand { crate::which(&editor_name, &paths, &cwd).ok_or(ShellError::ExternalCommand {
label: format!("`{editor_name}` not found"), label: format!("`{editor_name}` not found"),
help: "Failed to find the editor executable".into(), help: "Failed to find the editor executable".into(),
span: call.head, span: call.head,
}, })?;
)?;
let Some(env_path) = engine_state.get_config_path("env-path") else { let Some(env_path) = engine_state.get_config_path("env-path") else {
return Err(ShellError::GenericError { return Err(ShellError::GenericError {

View File

@ -64,13 +64,12 @@ impl Command for ConfigNu {
let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?; let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?; let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
let cwd = engine_state.cwd(Some(stack))?; let cwd = engine_state.cwd(Some(stack))?;
let editor_executable = crate::which(&editor_name, &paths, cwd.as_ref()).ok_or( let editor_executable =
ShellError::ExternalCommand { crate::which(&editor_name, &paths, &cwd).ok_or(ShellError::ExternalCommand {
label: format!("`{editor_name}` not found"), label: format!("`{editor_name}` not found"),
help: "Failed to find the editor executable".into(), help: "Failed to find the editor executable".into(),
span: call.head, span: call.head,
}, })?;
)?;
let Some(config_path) = engine_state.get_config_path("config-path") else { let Some(config_path) = engine_state.get_config_path("config-path") else {
return Err(ShellError::GenericError { return Err(ShellError::GenericError {

View File

@ -33,10 +33,6 @@ impl Command for ExportEnv {
CommandType::Keyword CommandType::Keyword
} }
fn requires_ast_for_arguments(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -45,7 +41,7 @@ impl Command for ExportEnv {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let block_id = call let block_id = call
.positional_nth(caller_stack, 0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")
.as_block() .as_block()
.expect("internal error: missing block"); .expect("internal error: missing block");

View File

@ -56,7 +56,7 @@ impl Command for SourceEnv {
&source_filename.item, &source_filename.item,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(caller_stack, call), get_dirs_var_from_call(call),
)? { )? {
PathBuf::from(&path) PathBuf::from(&path)
} else { } else {

View File

@ -31,7 +31,7 @@ mod test_examples {
check_example_evaluates_to_expected_output, check_example_evaluates_to_expected_output,
check_example_input_and_output_types_match_command_signature, check_example_input_and_output_types_match_command_signature,
}; };
use nu_cmd_lang::{Break, Describe, Echo, If, Let, Mut}; use nu_cmd_lang::{Break, Echo, If, Let, Mut};
use nu_protocol::{ use nu_protocol::{
engine::{Command, EngineState, StateWorkingSet}, engine::{Command, EngineState, StateWorkingSet},
Type, Type,
@ -81,7 +81,6 @@ mod test_examples {
working_set.add_decl(Box::new(Break)); working_set.add_decl(Box::new(Break));
working_set.add_decl(Box::new(Date)); working_set.add_decl(Box::new(Date));
working_set.add_decl(Box::new(Default)); 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(Each));
working_set.add_decl(Box::new(Echo)); working_set.add_decl(Box::new(Echo));
working_set.add_decl(Box::new(Enumerate)); working_set.add_decl(Box::new(Enumerate));

View File

@ -43,10 +43,7 @@ impl Command for Cd {
// If getting PWD failed, default to the initial directory. This way, the // If getting PWD failed, default to the initial directory. This way, the
// user can use `cd` to recover PWD to a good state. // user can use `cd` to recover PWD to a good state.
let cwd = engine_state let cwd = engine_state.cwd(Some(stack)).unwrap_or(get_init_cwd());
.cwd(Some(stack))
.ok()
.unwrap_or_else(get_init_cwd);
let path_val = { let path_val = {
if let Some(path) = path_val { if let Some(path) = path_val {
@ -65,7 +62,7 @@ impl Command for Cd {
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") { if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
oldpwd.to_path()? oldpwd.to_path()?
} else { } else {
cwd.into() cwd
} }
} else { } else {
// Trim whitespace from the end of path. // Trim whitespace from the end of path.
@ -134,7 +131,7 @@ impl Command for Cd {
result: None, result: None,
}, },
Example { Example {
description: r#"Change to the previous working directory (same as "cd $env.OLDPWD")"#, description: "Change to the previous working directory ($OLDPWD)",
example: r#"cd -"#, example: r#"cd -"#,
result: None, result: None,
}, },
@ -143,16 +140,6 @@ impl Command for Cd {
example: r#"def --env gohome [] { cd ~ }"#, example: r#"def --env gohome [] { cd ~ }"#,
result: None, result: None,
}, },
Example {
description: "Move two directories up in the tree (the parent directory's parent). Additional dots can be added for additional levels.",
example: r#"cd ..."#,
result: None,
},
Example {
description: "The cd command itself is often optional. Simply entering a path to a directory will cd to it.",
example: r#"/home"#,
result: None,
},
] ]
} }
} }

View File

@ -102,7 +102,7 @@ impl Command for Du {
let current_dir = current_dir(engine_state, stack)?; let current_dir = current_dir(engine_state, stack)?;
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let paths = if !call.has_positional_args(stack, 0) { let paths = if call.rest_iter(0).count() == 0 {
None None
} else { } else {
Some(paths) Some(paths)

View File

@ -108,7 +108,7 @@ impl Command for Ls {
}; };
let pattern_arg = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let pattern_arg = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let input_pattern_arg = if !call.has_positional_args(stack, 0) { let input_pattern_arg = if call.rest_iter(0).count() == 0 {
None None
} else { } else {
Some(pattern_arg) Some(pattern_arg)

View File

@ -1,7 +1,7 @@
use super::util::get_rest_for_glob_pattern; use super::util::get_rest_for_glob_pattern;
#[allow(deprecated)] #[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir, get_eval_block}; use nu_engine::{command_prelude::*, current_dir, get_eval_block};
use nu_protocol::{ast, ByteStream, DataSource, NuGlob, PipelineMetadata}; use nu_protocol::{ByteStream, DataSource, NuGlob, PipelineMetadata};
use std::path::Path; use std::path::Path;
#[cfg(feature = "sqlite")] #[cfg(feature = "sqlite")]
@ -56,7 +56,7 @@ impl Command for Open {
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);
if paths.is_empty() && !call.has_positional_args(stack, 0) { if paths.is_empty() && call.rest_iter(0).next().is_none() {
// try to use path from pipeline input if there were no positional or spread args // try to use path from pipeline input if there were no positional or spread args
let (filename, span) = match input { let (filename, span) = match input {
PipelineData::Value(val, ..) => { PipelineData::Value(val, ..) => {
@ -180,8 +180,7 @@ impl Command for Open {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
eval_block(engine_state, stack, block, stream) eval_block(engine_state, stack, block, stream)
} else { } else {
let call = ast::Call::new(call_span); decl.run(engine_state, stack, &Call::new(call_span), stream)
decl.run(engine_state, stack, &(&call).into(), stream)
}; };
output.push(command_output.map_err(|inner| { output.push(command_output.map_err(|inner| {
ShellError::GenericError{ ShellError::GenericError{

View File

@ -135,9 +135,12 @@ fn rm(
let home: Option<String> = nu_path::home_dir().map(|path| { let home: Option<String> = nu_path::home_dir().map(|path| {
{ {
if path.exists() { if path.exists() {
nu_path::canonicalize_with(&path, &currentdir_path).unwrap_or(path.into()) match nu_path::canonicalize_with(&path, &currentdir_path) {
Ok(canon_path) => canon_path,
Err(_) => path,
}
} else { } else {
path.into() path
} }
} }
.to_string_lossy() .to_string_lossy()
@ -167,7 +170,7 @@ fn rm(
} }
let span = call.head; let span = call.head;
let rm_always_trash = stack.get_config(engine_state).rm_always_trash; let rm_always_trash = engine_state.get_config().rm_always_trash;
if !TRASH_SUPPORTED { if !TRASH_SUPPORTED {
if rm_always_trash { if rm_always_trash {

View File

@ -4,8 +4,10 @@ use nu_engine::get_eval_block;
use nu_engine::{command_prelude::*, current_dir}; use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with; use nu_path::expand_path_with;
use nu_protocol::{ use nu_protocol::{
ast, byte_stream::copy_with_signals, process::ChildPipe, ByteStreamSource, DataSource, OutDest, ast::{Expr, Expression},
PipelineMetadata, Signals, byte_stream::copy_with_signals,
process::ChildPipe,
ByteStreamSource, DataSource, OutDest, PipelineMetadata, Signals,
}; };
use std::{ use std::{
fs::File, fs::File,
@ -67,6 +69,24 @@ impl Command for Save {
let append = call.has_flag(engine_state, stack, "append")?; let append = call.has_flag(engine_state, stack, "append")?;
let force = call.has_flag(engine_state, stack, "force")?; let force = call.has_flag(engine_state, stack, "force")?;
let progress = call.has_flag(engine_state, stack, "progress")?; let progress = call.has_flag(engine_state, stack, "progress")?;
let out_append = if let Some(Expression {
expr: Expr::Bool(out_append),
..
}) = call.get_parser_info("out-append")
{
*out_append
} else {
false
};
let err_append = if let Some(Expression {
expr: Expr::Bool(err_append),
..
}) = call.get_parser_info("err-append")
{
*err_append
} else {
false
};
let span = call.head; let span = call.head;
#[allow(deprecated)] #[allow(deprecated)]
@ -89,7 +109,14 @@ impl Command for Save {
PipelineData::ByteStream(stream, metadata) => { PipelineData::ByteStream(stream, metadata) => {
check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?; check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?;
let (file, stderr_file) = get_files(&path, stderr_path.as_ref(), append, force)?; let (file, stderr_file) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
let size = stream.known_size(); let size = stream.known_size();
let signals = engine_state.signals(); let signals = engine_state.signals();
@ -121,11 +148,9 @@ impl Command for Save {
} else { } else {
match stderr { match stderr {
ChildPipe::Pipe(mut pipe) => { ChildPipe::Pipe(mut pipe) => {
io::copy(&mut pipe, &mut io::stderr()) io::copy(&mut pipe, &mut io::sink())
}
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)?; .err_span(span)?;
} }
@ -196,7 +221,14 @@ impl Command for Save {
stderr_path.as_ref(), stderr_path.as_ref(),
)?; )?;
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?; let (mut file, _) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
for val in ls { for val in ls {
file.write_all(&value_to_bytes(val)?) file.write_all(&value_to_bytes(val)?)
.map_err(|err| ShellError::IOError { .map_err(|err| ShellError::IOError {
@ -226,7 +258,14 @@ impl Command for Save {
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?; input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
// Only open file after successful conversion // Only open file after successful conversion
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?; let (mut file, _) = get_files(
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
file.write_all(&bytes).map_err(|err| ShellError::IOError { file.write_all(&bytes).map_err(|err| ShellError::IOError {
msg: err.to_string(), msg: err.to_string(),
@ -358,8 +397,7 @@ fn convert_to_extension(
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);
eval_block(engine_state, stack, block, input) eval_block(engine_state, stack, block, input)
} else { } else {
let call = ast::Call::new(span); decl.run(engine_state, stack, &Call::new(span), input)
decl.run(engine_state, stack, &(&call).into(), input)
} }
} else { } else {
Ok(input) Ok(input)
@ -435,17 +473,19 @@ fn get_files(
path: &Spanned<PathBuf>, path: &Spanned<PathBuf>,
stderr_path: Option<&Spanned<PathBuf>>, stderr_path: Option<&Spanned<PathBuf>>,
append: bool, append: bool,
out_append: bool,
err_append: bool,
force: bool, force: bool,
) -> Result<(File, Option<File>), ShellError> { ) -> Result<(File, Option<File>), ShellError> {
// First check both paths // First check both paths
let (path, path_span) = prepare_path(path, append, force)?; let (path, path_span) = prepare_path(path, append || out_append, force)?;
let stderr_path_and_span = stderr_path let stderr_path_and_span = stderr_path
.as_ref() .as_ref()
.map(|stderr_path| prepare_path(stderr_path, append, force)) .map(|stderr_path| prepare_path(stderr_path, append || err_append, force))
.transpose()?; .transpose()?;
// Only if both files can be used open and possibly truncate them // Only if both files can be used open and possibly truncate them
let file = open_file(path, path_span, append)?; let file = open_file(path, path_span, append || out_append)?;
let stderr_file = stderr_path_and_span let stderr_file = stderr_path_and_span
.map(|(stderr_path, stderr_path_span)| { .map(|(stderr_path, stderr_path_span)| {
@ -458,7 +498,7 @@ fn get_files(
inner: vec![], inner: vec![],
}) })
} else { } else {
open_file(stderr_path, stderr_path_span, append) open_file(stderr_path, stderr_path_span, append || err_append)
} }
}) })
.transpose()?; .transpose()?;

View File

@ -86,22 +86,17 @@ impl Command for UCp {
}, },
Example { Example {
description: "Copy only if source file is newer than target file", description: "Copy only if source file is newer than target file",
example: "cp -u myfile newfile", example: "cp -u a b",
result: None, result: None,
}, },
Example { Example {
description: "Copy file preserving mode and timestamps attributes", description: "Copy file preserving mode and timestamps attributes",
example: "cp --preserve [ mode timestamps ] myfile newfile", example: "cp --preserve [ mode timestamps ] a b",
result: None, result: None,
}, },
Example { Example {
description: "Copy file erasing all attributes", description: "Copy file erasing all attributes",
example: "cp --preserve [] myfile newfile", example: "cp --preserve [] a b",
result: None,
},
Example {
description: "Copy file to a directory three levels above its current location",
example: "cp myfile ....",
result: None, result: None,
}, },
] ]
@ -240,7 +235,7 @@ impl Command for UCp {
for (sources, need_expand_tilde) in sources.iter_mut() { for (sources, need_expand_tilde) in sources.iter_mut() {
for src in sources.iter_mut() { for src in sources.iter_mut() {
if !src.is_absolute() { if !src.is_absolute() {
*src = nu_path::expand_path_with(&*src, &cwd, *need_expand_tilde); *src = nu_path::expand_path_with(&src, &cwd, *need_expand_tilde);
} }
} }
} }

View File

@ -30,21 +30,11 @@ impl Command for UMv {
example: "mv test.txt my/subdirectory", example: "mv test.txt my/subdirectory",
result: None, result: None,
}, },
Example {
description: "Move only if source file is newer than target file",
example: "mv -u new/test.txt old/",
result: None,
},
Example { Example {
description: "Move many files into a directory", description: "Move many files into a directory",
example: "mv *.txt my/subdirectory", example: "mv *.txt my/subdirectory",
result: None, result: None,
}, },
Example {
description: r#"Move a file into the "my" directory two levels up in the directory tree"#,
example: "mv test.txt .../my/",
result: None,
},
] ]
} }
@ -59,11 +49,6 @@ impl Command for UMv {
.switch("verbose", "explain what is being done.", Some('v')) .switch("verbose", "explain what is being done.", Some('v'))
.switch("progress", "display a progress bar", Some('p')) .switch("progress", "display a progress bar", Some('p'))
.switch("interactive", "prompt before overwriting", Some('i')) .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')) .switch("no-clobber", "do not overwrite an existing file", Some('n'))
.rest( .rest(
"paths", "paths",
@ -92,11 +77,6 @@ impl Command for UMv {
} else { } else {
uu_mv::OverwriteMode::Force uu_mv::OverwriteMode::Force
}; };
let update = if call.has_flag(engine_state, stack, "update")? {
UpdateMode::ReplaceIfOlder
} else {
UpdateMode::ReplaceAll
};
#[allow(deprecated)] #[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?; let cwd = current_dir(engine_state, stack)?;
@ -161,7 +141,7 @@ impl Command for UMv {
for (files, need_expand_tilde) in files.iter_mut() { for (files, need_expand_tilde) in files.iter_mut() {
for src in files.iter_mut() { for src in files.iter_mut() {
if !src.is_absolute() { if !src.is_absolute() {
*src = nu_path::expand_path_with(&*src, &cwd, *need_expand_tilde); *src = nu_path::expand_path_with(&src, &cwd, *need_expand_tilde);
} }
} }
} }
@ -184,7 +164,7 @@ impl Command for UMv {
verbose, verbose,
suffix: String::from("~"), suffix: String::from("~"),
backup: BackupMode::NoBackup, backup: BackupMode::NoBackup,
update, update: UpdateMode::ReplaceAll,
target_dir: None, target_dir: None,
no_target_dir: false, no_target_dir: false,
strip_slashes: false, strip_slashes: false,

View File

@ -1,6 +1,6 @@
use dialoguer::Input; use dialoguer::Input;
use nu_engine::{command_prelude::*, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_expression};
use nu_protocol::{FromValue, NuGlob}; use nu_protocol::{ast::Expr, FromValue, NuGlob};
use std::{ use std::{
error::Error, error::Error,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -92,19 +92,42 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
/// Get rest arguments from given `call`, starts with `starting_pos`. /// Get rest arguments from given `call`, starts with `starting_pos`.
/// ///
/// It's similar to `call.rest`, except that it always returns NuGlob. /// It's similar to `call.rest`, except that it always returns NuGlob. And if input argument has
/// Type::Glob, the NuGlob is unquoted, which means it's required to expand.
pub fn get_rest_for_glob_pattern( pub fn get_rest_for_glob_pattern(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
starting_pos: usize, starting_pos: usize,
) -> Result<Vec<Spanned<NuGlob>>, ShellError> { ) -> Result<Vec<Spanned<NuGlob>>, ShellError> {
let mut output = vec![];
let eval_expression = get_eval_expression(engine_state); let eval_expression = get_eval_expression(engine_state);
call.rest_iter_flattened(engine_state, stack, eval_expression, starting_pos)? for result in call.rest_iter_flattened(starting_pos, |expr| {
.into_iter() let result = eval_expression(engine_state, stack, expr);
// This used to be much more complex, but I think `FromValue` should be able to handle the match result {
// nuance here. Err(e) => Err(e),
.map(FromValue::from_value) Ok(result) => {
.collect() let span = result.span();
// convert from string to quoted string if expr is a variable
// or string interpolation
match result {
Value::String { val, .. }
if matches!(
&expr.expr,
Expr::FullCellPath(_) | Expr::StringInterpolation(_)
) =>
{
// should not expand if given input type is not glob.
Ok(Value::glob(val, expr.ty != Type::Glob, span))
}
other => Ok(other),
}
}
}
})? {
output.push(FromValue::from_value(result)?);
}
Ok(output)
} }

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