mirror of
https://github.com/nushell/nushell.git
synced 2025-08-16 00:58:14 +02:00
Compare commits
1 Commits
0.106.1
...
revert-159
Author | SHA1 | Date | |
---|---|---|---|
4f67310681 |
25
.github/workflows/friendly-config-reminder.yml
vendored
25
.github/workflows/friendly-config-reminder.yml
vendored
@ -1,25 +0,0 @@
|
|||||||
name: Comment on changes to the config
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
paths:
|
|
||||||
- 'crates/nu-protocol/src/config/**'
|
|
||||||
jobs:
|
|
||||||
comment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check if there is already a bot comment
|
|
||||||
uses: peter-evans/find-comment@v3
|
|
||||||
id: fc
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
comment-author: 'github-actions[bot]'
|
|
||||||
body-includes: Hey, just a bot checking in!
|
|
||||||
- name: Create comment if there is not
|
|
||||||
if: steps.fc.outputs.comment-id == ''
|
|
||||||
uses: peter-evans/create-or-update-comment@v4
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
body: |
|
|
||||||
Hey, just a bot checking in! You edited files related to the configuration.
|
|
||||||
If you changed any of the default values or added a new config option, don't forget to update the [`doc_config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/doc_config.nu) which documents the options for our users including the defaults provided by the Rust implementation.
|
|
||||||
If you didn't make a change here, you can just ignore me.
|
|
27
.github/workflows/nightly-build.yml
vendored
27
.github/workflows/nightly-build.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.105.1
|
version: 0.103.0
|
||||||
|
|
||||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
||||||
- name: Prepare for Nightly Release
|
- name: Prepare for Nightly Release
|
||||||
@ -127,7 +127,6 @@ jobs:
|
|||||||
- armv7-unknown-linux-musleabihf
|
- armv7-unknown-linux-musleabihf
|
||||||
- riscv64gc-unknown-linux-gnu
|
- riscv64gc-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-gnu
|
- loongarch64-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-musl
|
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
@ -153,8 +152,6 @@ jobs:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-gnu
|
- target: loongarch64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-musl
|
|
||||||
os: ubuntu-22.04
|
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
steps:
|
steps:
|
||||||
@ -182,22 +179,36 @@ jobs:
|
|||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||||
with:
|
with:
|
||||||
cache: false
|
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
|
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||||
with:
|
with:
|
||||||
version: 0.105.1
|
version: 0.103.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
|
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||||
run: nu .github/workflows/release-pkg.nu
|
run: nu .github/workflows/release-pkg.nu
|
||||||
env:
|
env:
|
||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build Nu for Windows ARM64
|
||||||
|
id: nu0
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ matrix.os == 'windows-11-arm' }}
|
||||||
|
run: |
|
||||||
|
$env:OS = 'windows'
|
||||||
|
$env:REF = '${{ github.ref }}'
|
||||||
|
$env:TARGET = '${{ matrix.target }}'
|
||||||
|
cargo build --release --all --target aarch64-pc-windows-msvc
|
||||||
|
cp ./target/${{ matrix.target }}/release/nu.exe .
|
||||||
|
./nu.exe -c 'version'
|
||||||
|
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
|
||||||
|
|
||||||
- name: Create an Issue for Release Failure
|
- name: Create an Issue for Release Failure
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
uses: JasonEtco/create-an-issue@v2
|
uses: JasonEtco/create-an-issue@v2
|
||||||
@ -217,7 +228,9 @@ jobs:
|
|||||||
prerelease: true
|
prerelease: true
|
||||||
files: |
|
files: |
|
||||||
${{ steps.nu.outputs.msi }}
|
${{ steps.nu.outputs.msi }}
|
||||||
|
${{ steps.nu0.outputs.msi }}
|
||||||
${{ steps.nu.outputs.archive }}
|
${{ steps.nu.outputs.archive }}
|
||||||
|
${{ steps.nu0.outputs.archive }}
|
||||||
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
|
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
|
||||||
name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
|
name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
|
||||||
env:
|
env:
|
||||||
@ -263,7 +276,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.105.1
|
version: 0.103.0
|
||||||
|
|
||||||
# Keep the last a few releases
|
# Keep the last a few releases
|
||||||
- name: Delete Older Releases
|
- name: Delete Older Releases
|
||||||
|
2
.github/workflows/release-msi.yml
vendored
2
.github/workflows/release-msi.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.105.1
|
version: nightly
|
||||||
|
|
||||||
- name: Release MSI Packages
|
- name: Release MSI Packages
|
||||||
id: nu
|
id: nu
|
||||||
|
8
.github/workflows/release-pkg.nu
vendored
8
.github/workflows/release-pkg.nu
vendored
@ -99,14 +99,6 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
|||||||
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
|
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
|
||||||
cargo-build-nu
|
cargo-build-nu
|
||||||
}
|
}
|
||||||
'loongarch64-unknown-linux-musl' => {
|
|
||||||
print $"(ansi g)Downloading LoongArch64 musl cross-compilation toolchain...(ansi reset)"
|
|
||||||
aria2c -q https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-linux-musl-cross-gcc-13.2.0/loongarch64-linux-musl-cross.tgz
|
|
||||||
tar -xf loongarch64-linux-musl-cross.tgz
|
|
||||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/loongarch64-linux-musl-cross/bin')
|
|
||||||
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER = "loongarch64-linux-musl-gcc"
|
|
||||||
cargo-build-nu
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
|
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@ -35,7 +35,6 @@ jobs:
|
|||||||
- armv7-unknown-linux-musleabihf
|
- armv7-unknown-linux-musleabihf
|
||||||
- riscv64gc-unknown-linux-gnu
|
- riscv64gc-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-gnu
|
- loongarch64-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-musl
|
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
@ -61,8 +60,6 @@ jobs:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-gnu
|
- target: loongarch64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-musl
|
|
||||||
os: ubuntu-22.04
|
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
@ -93,17 +90,32 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
|
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||||
with:
|
with:
|
||||||
version: 0.105.1
|
version: 0.103.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
|
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||||
run: nu .github/workflows/release-pkg.nu
|
run: nu .github/workflows/release-pkg.nu
|
||||||
env:
|
env:
|
||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build Nu for Windows ARM64
|
||||||
|
id: nu0
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ matrix.os == 'windows-11-arm' }}
|
||||||
|
run: |
|
||||||
|
$env:OS = 'windows'
|
||||||
|
$env:REF = '${{ github.ref }}'
|
||||||
|
$env:TARGET = '${{ matrix.target }}'
|
||||||
|
cargo build --release --all --target aarch64-pc-windows-msvc
|
||||||
|
cp ./target/${{ matrix.target }}/release/nu.exe .
|
||||||
|
./nu.exe -c 'version'
|
||||||
|
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
|
||||||
|
|
||||||
# WARN: Don't upgrade this action due to the release per asset issue.
|
# WARN: Don't upgrade this action due to the release per asset issue.
|
||||||
# See: https://github.com/softprops/action-gh-release/issues/445
|
# See: https://github.com/softprops/action-gh-release/issues/445
|
||||||
- name: Publish Archive
|
- name: Publish Archive
|
||||||
@ -113,7 +125,9 @@ jobs:
|
|||||||
draft: true
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
${{ steps.nu.outputs.msi }}
|
${{ steps.nu.outputs.msi }}
|
||||||
|
${{ steps.nu0.outputs.msi }}
|
||||||
${{ steps.nu.outputs.archive }}
|
${{ steps.nu.outputs.archive }}
|
||||||
|
${{ steps.nu0.outputs.archive }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -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.34.0
|
uses: crate-ci/typos@v1.33.1
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -32,17 +32,11 @@ unstable_cargo_features.txt
|
|||||||
# Helix configuration folder
|
# Helix configuration folder
|
||||||
.helix/*
|
.helix/*
|
||||||
.helix
|
.helix
|
||||||
wix/bin/
|
|
||||||
wix/obj/
|
|
||||||
wix/nu/
|
|
||||||
|
|
||||||
# Coverage tools
|
# Coverage tools
|
||||||
lcov.info
|
lcov.info
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
|
|
||||||
# benchmarking
|
|
||||||
/tango
|
|
||||||
|
|
||||||
# Visual Studio
|
# Visual Studio
|
||||||
.vs/*
|
.vs/*
|
||||||
*.rsproj
|
*.rsproj
|
||||||
|
606
Cargo.lock
generated
606
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
109
Cargo.toml
109
Cargo.toml
@ -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.86.0"
|
rust-version = "1.85.1"
|
||||||
version = "0.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -24,37 +24,36 @@ pkg-fmt = "zip"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/nu_plugin_custom_values",
|
|
||||||
"crates/nu_plugin_example",
|
|
||||||
"crates/nu_plugin_formats",
|
|
||||||
"crates/nu_plugin_gstat",
|
|
||||||
"crates/nu_plugin_inc",
|
|
||||||
"crates/nu_plugin_polars",
|
|
||||||
"crates/nu_plugin_query",
|
|
||||||
"crates/nu_plugin_stress_internals",
|
|
||||||
"crates/nu-cli",
|
"crates/nu-cli",
|
||||||
|
"crates/nu-engine",
|
||||||
|
"crates/nu-parser",
|
||||||
|
"crates/nu-system",
|
||||||
"crates/nu-cmd-base",
|
"crates/nu-cmd-base",
|
||||||
"crates/nu-cmd-extra",
|
"crates/nu-cmd-extra",
|
||||||
"crates/nu-cmd-lang",
|
"crates/nu-cmd-lang",
|
||||||
"crates/nu-cmd-plugin",
|
"crates/nu-cmd-plugin",
|
||||||
"crates/nu-color-config",
|
|
||||||
"crates/nu-command",
|
"crates/nu-command",
|
||||||
"crates/nu-derive-value",
|
"crates/nu-color-config",
|
||||||
"crates/nu-engine",
|
|
||||||
"crates/nu-experimental",
|
|
||||||
"crates/nu-explore",
|
"crates/nu-explore",
|
||||||
"crates/nu-json",
|
"crates/nu-json",
|
||||||
"crates/nu-lsp",
|
"crates/nu-lsp",
|
||||||
"crates/nu-parser",
|
"crates/nu-pretty-hex",
|
||||||
|
"crates/nu-protocol",
|
||||||
|
"crates/nu-derive-value",
|
||||||
|
"crates/nu-plugin",
|
||||||
"crates/nu-plugin-core",
|
"crates/nu-plugin-core",
|
||||||
"crates/nu-plugin-engine",
|
"crates/nu-plugin-engine",
|
||||||
"crates/nu-plugin-protocol",
|
"crates/nu-plugin-protocol",
|
||||||
"crates/nu-plugin-test-support",
|
"crates/nu-plugin-test-support",
|
||||||
"crates/nu-plugin",
|
"crates/nu_plugin_inc",
|
||||||
"crates/nu-pretty-hex",
|
"crates/nu_plugin_gstat",
|
||||||
"crates/nu-protocol",
|
"crates/nu_plugin_example",
|
||||||
|
"crates/nu_plugin_query",
|
||||||
|
"crates/nu_plugin_custom_values",
|
||||||
|
"crates/nu_plugin_formats",
|
||||||
|
"crates/nu_plugin_polars",
|
||||||
|
"crates/nu_plugin_stress_internals",
|
||||||
"crates/nu-std",
|
"crates/nu-std",
|
||||||
"crates/nu-system",
|
|
||||||
"crates/nu-table",
|
"crates/nu-table",
|
||||||
"crates/nu-term-grid",
|
"crates/nu-term-grid",
|
||||||
"crates/nu-test-support",
|
"crates/nu-test-support",
|
||||||
@ -72,7 +71,7 @@ brotli = "7.0"
|
|||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytesize = "1.3.3"
|
bytesize = "1.3.3"
|
||||||
calamine = "0.28"
|
calamine = "0.26"
|
||||||
chardetng = "0.1.17"
|
chardetng = "0.1.17"
|
||||||
chrono = { default-features = false, version = "0.4.34" }
|
chrono = { default-features = false, version = "0.4.34" }
|
||||||
chrono-humanize = "0.2.3"
|
chrono-humanize = "0.2.3"
|
||||||
@ -93,7 +92,7 @@ filesize = "0.2"
|
|||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
human-date-parser = "0.3.0"
|
human-date-parser = "0.3.0"
|
||||||
indexmap = "2.10"
|
indexmap = "2.9"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
interprocess = "2.2.0"
|
interprocess = "2.2.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
@ -132,7 +131,7 @@ proc-macro-error2 = "2.0"
|
|||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
procfs = "0.17.0"
|
procfs = "0.17.0"
|
||||||
pwd = "1.3"
|
pwd = "1.3"
|
||||||
quick-xml = "0.37.5"
|
quick-xml = "0.37.0"
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
quickcheck_macros = "1.1"
|
quickcheck_macros = "1.1"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
@ -141,7 +140,7 @@ getrandom = "0.2" # pick same version that rand requires
|
|||||||
rand_chacha = "0.9"
|
rand_chacha = "0.9"
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
reedline = "0.41.0"
|
reedline = "0.40.0"
|
||||||
rmp = "0.8"
|
rmp = "0.8"
|
||||||
rmp-serde = "1.3"
|
rmp-serde = "1.3"
|
||||||
roxmltree = "0.20"
|
roxmltree = "0.20"
|
||||||
@ -157,18 +156,17 @@ serde_json = "1.0.97"
|
|||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
serde_yaml = "0.9.33"
|
serde_yaml = "0.9.33"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
strip-ansi-escapes = "0.2.1"
|
strip-ansi-escapes = "0.2.0"
|
||||||
strum = "0.26"
|
strum = "0.26"
|
||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
syn = "2.0"
|
syn = "2.0"
|
||||||
sysinfo = "0.36"
|
sysinfo = "0.33"
|
||||||
tabled = { version = "0.20", default-features = false }
|
tabled = { version = "0.20", default-features = false }
|
||||||
tempfile = "3.20"
|
tempfile = "3.20"
|
||||||
thiserror = "2.0.12"
|
|
||||||
titlecase = "3.6"
|
titlecase = "3.6"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
trash = "5.2"
|
trash = "5.2"
|
||||||
update-informer = { version = "1.3.0", default-features = false, features = ["github", "ureq"] }
|
update-informer = { version = "1.2.0", default-features = false, features = ["github", "ureq"] }
|
||||||
umask = "2.1"
|
umask = "2.1"
|
||||||
unicode-segmentation = "1.12"
|
unicode-segmentation = "1.12"
|
||||||
unicode-width = "0.2"
|
unicode-width = "0.2"
|
||||||
@ -186,7 +184,7 @@ uuid = "1.16.0"
|
|||||||
v_htmlescape = "0.15.0"
|
v_htmlescape = "0.15.0"
|
||||||
wax = "0.6"
|
wax = "0.6"
|
||||||
web-time = "1.1.0"
|
web-time = "1.1.0"
|
||||||
which = "8.0.0"
|
which = "7.0.3"
|
||||||
windows = "0.56"
|
windows = "0.56"
|
||||||
windows-sys = "0.48"
|
windows-sys = "0.48"
|
||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
@ -197,29 +195,27 @@ webpki-roots = "1.0"
|
|||||||
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
|
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
|
||||||
# todo = "warn"
|
# todo = "warn"
|
||||||
unchecked_duration_subtraction = "warn"
|
unchecked_duration_subtraction = "warn"
|
||||||
used_underscore_binding = "warn"
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.106.1" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.105.2" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.106.1" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.105.2" }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.106.1" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.106.1" }
|
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.105.2", optional = true }
|
||||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.106.1", optional = true }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.105.2" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.106.1", default-features = false, features = ["os"] }
|
nu-command = { path = "./crates/nu-command", version = "0.105.2", default-features = false, features = ["os"] }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.106.1" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.105.2" }
|
||||||
nu-experimental = { path = "./crates/nu-experimental", version = "0.106.1" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.105.2" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.106.1" }
|
nu-lsp = { path = "./crates/nu-lsp/", version = "0.105.2" }
|
||||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.106.1" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.105.2" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.106.1" }
|
nu-path = { path = "./crates/nu-path", version = "0.105.2" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.106.1" }
|
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.105.2" }
|
||||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.106.1" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.105.2" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.106.1" }
|
nu-std = { path = "./crates/nu-std", version = "0.105.2" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.106.1" }
|
nu-system = { path = "./crates/nu-system", version = "0.105.2" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.106.1" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.105.2" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.106.1" }
|
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
@ -248,9 +244,9 @@ nix = { workspace = true, default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.106.1" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.105.2" }
|
||||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.106.1" }
|
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.105.2" }
|
||||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.106.1" }
|
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.105.2" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
tango-bench = "0.6"
|
tango-bench = "0.6"
|
||||||
@ -261,14 +257,10 @@ serial_test = "3.2"
|
|||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Enable all features while still avoiding mutually exclusive features.
|
|
||||||
# Use this if `--all-features` fails.
|
|
||||||
full = ["plugin", "rustls-tls", "system-clipboard", "trash-support", "sqlite"]
|
|
||||||
|
|
||||||
plugin = [
|
plugin = [
|
||||||
# crates
|
# crates
|
||||||
"dep:nu-cmd-plugin",
|
"nu-cmd-plugin",
|
||||||
"dep:nu-plugin-engine",
|
"nu-plugin-engine",
|
||||||
|
|
||||||
# features
|
# features
|
||||||
"nu-cli/plugin",
|
"nu-cli/plugin",
|
||||||
@ -294,20 +286,21 @@ stable = ["default"]
|
|||||||
|
|
||||||
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
||||||
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||||
static-link-openssl = ["dep:openssl"]
|
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
||||||
|
|
||||||
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
||||||
# Missing X server/ Wayland can cause issues
|
# Missing X server/ Wayland can cause issues
|
||||||
system-clipboard = [
|
system-clipboard = [
|
||||||
"reedline/system_clipboard",
|
"reedline/system_clipboard",
|
||||||
"nu-cli/system-clipboard",
|
"nu-cli/system-clipboard",
|
||||||
|
"nu-cmd-lang/system-clipboard",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Stable (Default)
|
# Stable (Default)
|
||||||
trash-support = ["nu-command/trash-support"]
|
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
||||||
|
|
||||||
# SQLite commands for nushell
|
# SQLite commands for nushell
|
||||||
sqlite = ["nu-command/sqlite", "nu-std/sqlite"]
|
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # Optimize for size
|
opt-level = "s" # Optimize for size
|
||||||
|
@ -199,7 +199,7 @@ fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
|
|||||||
let nested_access = ".col".repeat(n);
|
let nested_access = ".col".repeat(n);
|
||||||
bench_command(
|
bench_command(
|
||||||
format!("record_nested_access_{n}"),
|
format!("record_nested_access_{n}"),
|
||||||
format!("$record{nested_access} | ignore"),
|
format!("$record{} | ignore", nested_access),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -319,7 +319,7 @@ fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
|
|||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
format!("eval_par_each_{n}"),
|
format!("eval_par_each_{n}"),
|
||||||
format!("(1..{n}) | par-each -t 2 {{|_| 1 }} | ignore"),
|
format!("(1..{}) | par-each -t 2 {{|_| 1 }} | ignore", n),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -357,7 +357,7 @@ fn encode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
|
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("encode_json_{row_cnt}_{col_cnt}"),
|
format!("encode_json_{}_{}", row_cnt, col_cnt),
|
||||||
move |b| {
|
move |b| {
|
||||||
let encoder = encoder.clone();
|
let encoder = encoder.clone();
|
||||||
let test_data = test_data.clone();
|
let test_data = test_data.clone();
|
||||||
@ -377,7 +377,7 @@ fn encode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
|
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("encode_msgpack_{row_cnt}_{col_cnt}"),
|
format!("encode_msgpack_{}_{}", row_cnt, col_cnt),
|
||||||
move |b| {
|
move |b| {
|
||||||
let encoder = encoder.clone();
|
let encoder = encoder.clone();
|
||||||
let test_data = test_data.clone();
|
let test_data = test_data.clone();
|
||||||
@ -399,7 +399,7 @@ fn decode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
encoder.encode(&test_data, &mut res).unwrap();
|
encoder.encode(&test_data, &mut res).unwrap();
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("decode_json_{row_cnt}_{col_cnt}"),
|
format!("decode_json_{}_{}", row_cnt, col_cnt),
|
||||||
move |b| {
|
move |b| {
|
||||||
let res = res.clone();
|
let res = res.clone();
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
@ -422,7 +422,7 @@ fn decode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
encoder.encode(&test_data, &mut res).unwrap();
|
encoder.encode(&test_data, &mut res).unwrap();
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("decode_msgpack_{row_cnt}_{col_cnt}"),
|
format!("decode_msgpack_{}_{}", row_cnt, col_cnt),
|
||||||
move |b| {
|
move |b| {
|
||||||
let res = res.clone();
|
let res = res.clone();
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
|
@ -5,29 +5,29 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.106.1" }
|
nu-command = { path = "../nu-command", version = "0.105.2" }
|
||||||
nu-std = { path = "../nu-std", version = "0.106.1" }
|
nu-std = { path = "../nu-std", version = "0.105.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
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.106.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", features = ["os"] }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", features = ["os"] }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.106.1" }
|
nu-glob = { path = "../nu-glob", version = "0.105.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.106.1" }
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.106.1" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1", optional = true }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.105.2", optional = true }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["os"] }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", features = ["os"] }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.106.1" }
|
nu-utils = { path = "../nu-utils", version = "0.105.2" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.105.2" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ Note that history item IDs are ignored when importing from file."#
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
example: "[[ command cwd ]; [ foo /home ]] | history import",
|
example: "[[ command_line cwd ]; [ foo /home ]] | history import",
|
||||||
description: "Append `foo` ran from `/home` to the current history",
|
description: "Append `foo` ran from `/home` to the current history",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -118,7 +118,7 @@ fn get_suggestions_by_value(
|
|||||||
|| s.chars()
|
|| s.chars()
|
||||||
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
|
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
|
||||||
{
|
{
|
||||||
format!("{s:?}")
|
format!("{:?}", s)
|
||||||
} else {
|
} else {
|
||||||
s
|
s
|
||||||
};
|
};
|
||||||
|
@ -52,7 +52,7 @@ impl CommandCompletion {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let value = if matched_internal(&name) {
|
let value = if matched_internal(&name) {
|
||||||
format!("^{name}")
|
format!("^{}", name)
|
||||||
} else {
|
} else {
|
||||||
name.clone()
|
name.clone()
|
||||||
};
|
};
|
||||||
|
@ -176,7 +176,7 @@ impl NuCompleter {
|
|||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some("completer"),
|
Some("completer"),
|
||||||
// Add a placeholder `a` to the end
|
// Add a placeholder `a` to the end
|
||||||
format!("{line}a").as_bytes(),
|
format!("{}a", line).as_bytes(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
|
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
|
||||||
@ -370,8 +370,7 @@ impl NuCompleter {
|
|||||||
FileCompletion,
|
FileCompletion,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prioritize argument completions over (sub)commands
|
suggestions.extend(self.process_completion(&mut completer, &ctx));
|
||||||
suggestions.splice(0..0, self.process_completion(&mut completer, &ctx));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,39 +384,33 @@ impl NuCompleter {
|
|||||||
};
|
};
|
||||||
self.process_completion(&mut flag_completions, &ctx)
|
self.process_completion(&mut flag_completions, &ctx)
|
||||||
};
|
};
|
||||||
// Prioritize argument completions over (sub)commands
|
suggestions.extend(match arg {
|
||||||
suggestions.splice(
|
// flags
|
||||||
0..0,
|
Argument::Named(_) | Argument::Unknown(_)
|
||||||
match arg {
|
if prefix.starts_with(b"-") =>
|
||||||
// flags
|
{
|
||||||
Argument::Named(_) | Argument::Unknown(_)
|
flag_completion_helper()
|
||||||
if prefix.starts_with(b"-") =>
|
}
|
||||||
{
|
// only when `strip` == false
|
||||||
flag_completion_helper()
|
Argument::Positional(_) if prefix == b"-" => flag_completion_helper(),
|
||||||
}
|
// complete according to expression type and command head
|
||||||
// only when `strip` == false
|
Argument::Positional(expr) => {
|
||||||
Argument::Positional(_) if prefix == b"-" => {
|
let command_head = working_set.get_decl(call.decl_id).name();
|
||||||
flag_completion_helper()
|
positional_arg_indices.push(arg_idx);
|
||||||
}
|
self.argument_completion_helper(
|
||||||
// complete according to expression type and command head
|
PositionalArguments {
|
||||||
Argument::Positional(expr) => {
|
command_head,
|
||||||
let command_head = working_set.get_decl(call.decl_id).name();
|
positional_arg_indices,
|
||||||
positional_arg_indices.push(arg_idx);
|
arguments: &call.arguments,
|
||||||
self.argument_completion_helper(
|
expr,
|
||||||
PositionalArguments {
|
},
|
||||||
command_head,
|
pos,
|
||||||
positional_arg_indices,
|
&ctx,
|
||||||
arguments: &call.arguments,
|
suggestions.is_empty(),
|
||||||
expr,
|
)
|
||||||
},
|
}
|
||||||
pos,
|
_ => vec![],
|
||||||
&ctx,
|
});
|
||||||
suggestions.is_empty(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
} else if !matches!(arg, Argument::Named(_)) {
|
} else if !matches!(arg, Argument::Named(_)) {
|
||||||
positional_arg_indices.push(arg_idx);
|
positional_arg_indices.push(arg_idx);
|
||||||
@ -469,18 +462,10 @@ impl NuCompleter {
|
|||||||
if let Some(external_result) =
|
if let Some(external_result) =
|
||||||
self.external_completion(closure, &text_spans, offset, new_span)
|
self.external_completion(closure, &text_spans, offset, new_span)
|
||||||
{
|
{
|
||||||
// Prioritize external results over (sub)commands
|
suggestions.extend(external_result);
|
||||||
suggestions.splice(0..0, external_result);
|
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for external path arguments with spaces, please check issue #15790
|
|
||||||
if suggestions.is_empty() {
|
|
||||||
let (new_span, prefix) =
|
|
||||||
strip_placeholder_if_any(working_set, &span, strip);
|
|
||||||
let ctx = Context::new(working_set, new_span, prefix, offset);
|
|
||||||
return self.process_completion(&mut FileCompletion, &ctx);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -857,7 +842,7 @@ mod completer_tests {
|
|||||||
for (line, has_result, begins_with, expected_values) in dataset {
|
for (line, has_result, begins_with, expected_values) in dataset {
|
||||||
let result = completer.fetch_completions_at(line, line.len());
|
let result = completer.fetch_completions_at(line, line.len());
|
||||||
// Test whether the result is empty or not
|
// Test whether the result is empty or not
|
||||||
assert_eq!(!result.is_empty(), has_result, "line: {line}");
|
assert_eq!(!result.is_empty(), has_result, "line: {}", line);
|
||||||
|
|
||||||
// Test whether the result begins with the expected value
|
// Test whether the result begins with the expected value
|
||||||
result
|
result
|
||||||
@ -872,7 +857,8 @@ mod completer_tests {
|
|||||||
.filter(|x| *x)
|
.filter(|x| *x)
|
||||||
.count(),
|
.count(),
|
||||||
expected_values.len(),
|
expected_values.len(),
|
||||||
"line: {line}"
|
"line: {}",
|
||||||
|
line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ pub fn escape_path(path: String) -> String {
|
|||||||
if path.contains('\'') {
|
if path.contains('\'') {
|
||||||
// decide to use double quotes
|
// decide to use double quotes
|
||||||
// Path as Debug will do the escaping for `"`, `\`
|
// Path as Debug will do the escaping for `"`, `\`
|
||||||
format!("{path:?}")
|
format!("{:?}", path)
|
||||||
} else {
|
} else {
|
||||||
format!("'{path}'")
|
format!("'{path}'")
|
||||||
}
|
}
|
||||||
|
@ -102,11 +102,7 @@ impl<T> NuMatcher<'_, T> {
|
|||||||
options,
|
options,
|
||||||
needle: needle.to_owned(),
|
needle: needle.to_owned(),
|
||||||
state: State::Fuzzy {
|
state: State::Fuzzy {
|
||||||
matcher: Matcher::new({
|
matcher: Matcher::new(Config::DEFAULT),
|
||||||
let mut cfg = Config::DEFAULT;
|
|
||||||
cfg.prefer_prefix = true;
|
|
||||||
cfg
|
|
||||||
}),
|
|
||||||
atom,
|
atom,
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
},
|
},
|
||||||
|
@ -129,7 +129,7 @@ impl Completer for DotNuCompletion {
|
|||||||
.take_while(|c| "`'\"".contains(*c))
|
.take_while(|c| "`'\"".contains(*c))
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
for path in ["std", "std-rfc"] {
|
for path in ["std", "std-rfc"] {
|
||||||
let path = format!("{surround_prefix}{path}");
|
let path = format!("{}{}", surround_prefix, path);
|
||||||
matcher.add(
|
matcher.add(
|
||||||
path.clone(),
|
path.clone(),
|
||||||
FileSuggestion {
|
FileSuggestion {
|
||||||
@ -146,7 +146,7 @@ impl Completer for DotNuCompletion {
|
|||||||
for sub_vp_id in sub_paths {
|
for sub_vp_id in sub_paths {
|
||||||
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
||||||
let path = path
|
let path = path
|
||||||
.strip_prefix(&format!("{base_dir}/"))
|
.strip_prefix(&format!("{}/", base_dir))
|
||||||
.unwrap_or(path)
|
.unwrap_or(path)
|
||||||
.to_string();
|
.to_string();
|
||||||
matcher.add(
|
matcher.add(
|
||||||
|
@ -3,9 +3,9 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
PipelineData, ShellError, Spanned, Value,
|
PipelineData, ShellError, Spanned, Value,
|
||||||
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error::report_compile_error,
|
|
||||||
report_parse_error, report_parse_warning,
|
report_parse_error, report_parse_warning,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -5,9 +5,9 @@ use nu_parser::parse;
|
|||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
PipelineData, ShellError, Span, Value,
|
PipelineData, ShellError, Span, Value,
|
||||||
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error::report_compile_error,
|
|
||||||
report_parse_error, report_parse_warning,
|
report_parse_error, report_parse_warning,
|
||||||
shell_error::io::*,
|
shell_error::io::*,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::documentation::{FormatterValue, HelpStyle, get_flags_section};
|
use nu_engine::documentation::{HelpStyle, get_flags_section};
|
||||||
use nu_protocol::{Config, engine::EngineState, levenshtein_distance};
|
use nu_protocol::{Config, engine::EngineState, levenshtein_distance};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
@ -66,11 +66,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| match v {
|
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| {
|
||||||
FormatterValue::DefaultValue(value) => {
|
v.to_parsable_string(", ", &self.config)
|
||||||
value.to_parsable_string(", ", &self.config)
|
|
||||||
}
|
|
||||||
FormatterValue::CodeString(text) => text.to_string(),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ use std::sync::Arc;
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
|
||||||
use crate::syntax_highlight::highlight_syntax;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NuHighlight;
|
pub struct NuHighlight;
|
||||||
|
|
||||||
@ -16,11 +14,6 @@ impl Command for NuHighlight {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("nu-highlight")
|
Signature::build("nu-highlight")
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
.switch(
|
|
||||||
"reject-garbage",
|
|
||||||
"Return an error if invalid syntax (garbage) was encountered",
|
|
||||||
Some('r'),
|
|
||||||
)
|
|
||||||
.input_output_types(vec![(Type::String, Type::String)])
|
.input_output_types(vec![(Type::String, Type::String)])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,33 +32,19 @@ impl Command for NuHighlight {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let reject_garbage = call.has_flag(engine_state, stack, "reject-garbage")?;
|
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let signals = engine_state.signals();
|
let signals = engine_state.signals();
|
||||||
|
|
||||||
let engine_state = Arc::new(engine_state.clone());
|
let highlighter = crate::NuHighlighter {
|
||||||
let stack = Arc::new(stack.clone());
|
engine_state: Arc::new(engine_state.clone()),
|
||||||
|
stack: Arc::new(stack.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |x| match x.coerce_into_string() {
|
move |x| match x.coerce_into_string() {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let result = highlight_syntax(&engine_state, &stack, &line, line.len());
|
let highlights = highlighter.highlight(&line, line.len());
|
||||||
|
|
||||||
let highlights = match (reject_garbage, result.found_garbage) {
|
|
||||||
(false, _) => result.text,
|
|
||||||
(true, None) => result.text,
|
|
||||||
(true, Some(span)) => {
|
|
||||||
let error = ShellError::OutsideSpannedLabeledError {
|
|
||||||
src: line,
|
|
||||||
error: "encountered invalid syntax while highlighting".into(),
|
|
||||||
msg: "invalid syntax".into(),
|
|
||||||
span,
|
|
||||||
};
|
|
||||||
return Value::error(error, head);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::string(highlights.render_simple(), head)
|
Value::string(highlights.render_simple(), head)
|
||||||
}
|
}
|
||||||
Err(err) => Value::error(err, head),
|
Err(err) => Value::error(err, head),
|
||||||
|
@ -1172,7 +1172,6 @@ fn edit_from_record(
|
|||||||
"cutfromlinestart" => EditCommand::CutFromLineStart,
|
"cutfromlinestart" => EditCommand::CutFromLineStart,
|
||||||
"cuttoend" => EditCommand::CutToEnd,
|
"cuttoend" => EditCommand::CutToEnd,
|
||||||
"cuttolineend" => EditCommand::CutToLineEnd,
|
"cuttolineend" => EditCommand::CutToLineEnd,
|
||||||
"killline" => EditCommand::KillLine,
|
|
||||||
"cutwordleft" => EditCommand::CutWordLeft,
|
"cutwordleft" => EditCommand::CutWordLeft,
|
||||||
"cutbigwordleft" => EditCommand::CutBigWordLeft,
|
"cutbigwordleft" => EditCommand::CutBigWordLeft,
|
||||||
"cutwordright" => EditCommand::CutWordRight,
|
"cutwordright" => EditCommand::CutWordRight,
|
||||||
|
@ -22,8 +22,8 @@ use nu_color_config::StyleComputer;
|
|||||||
use nu_engine::env_to_strings;
|
use nu_engine::env_to_strings;
|
||||||
use nu_engine::exit::cleanup_exit;
|
use nu_engine::exit::cleanup_exit;
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
|
use nu_protocol::shell_error;
|
||||||
use nu_protocol::shell_error::io::IoError;
|
use nu_protocol::shell_error::io::IoError;
|
||||||
use nu_protocol::{BannerKind, shell_error};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
||||||
config::NuCursorShape,
|
config::NuCursorShape,
|
||||||
@ -145,8 +145,8 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
if load_std_lib.is_none() {
|
if load_std_lib.is_none() {
|
||||||
match engine_state.get_config().show_banner {
|
match engine_state.get_config().show_banner {
|
||||||
BannerKind::None => {}
|
Value::Bool { val: false, .. } => {}
|
||||||
BannerKind::Short => {
|
Value::String { ref val, .. } if val == "short" => {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut unique_stack,
|
&mut unique_stack,
|
||||||
@ -156,7 +156,7 @@ pub fn evaluate_repl(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
BannerKind::Full => {
|
_ => {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut unique_stack,
|
&mut unique_stack,
|
||||||
@ -239,7 +239,7 @@ fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
|
|||||||
|
|
||||||
match byte {
|
match byte {
|
||||||
// Escape bytes below 0x20
|
// Escape bytes below 0x20
|
||||||
b if b < 0x20 => format!("\\x{byte:02X}").into_bytes(),
|
b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
|
||||||
// Escape semicolon as \x3B
|
// Escape semicolon as \x3B
|
||||||
b';' => "\\x3B".to_string().into_bytes(),
|
b';' => "\\x3B".to_string().into_bytes(),
|
||||||
// Escape backslash as \\
|
// Escape backslash as \\
|
||||||
@ -1097,7 +1097,8 @@ fn run_shell_integration_osc633(
|
|||||||
// If we're in vscode, run their specific ansi escape sequence.
|
// If we're in vscode, run their specific ansi escape sequence.
|
||||||
// This is helpful for ctrl+g to change directories in the terminal.
|
// This is helpful for ctrl+g to change directories in the terminal.
|
||||||
run_ansi_sequence(&format!(
|
run_ansi_sequence(&format!(
|
||||||
"{VSCODE_CWD_PROPERTY_MARKER_PREFIX}{path}{VSCODE_CWD_PROPERTY_MARKER_SUFFIX}"
|
"{}{}{}",
|
||||||
|
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
||||||
));
|
));
|
||||||
|
|
||||||
perf!(
|
perf!(
|
||||||
@ -1113,7 +1114,10 @@ fn run_shell_integration_osc633(
|
|||||||
|
|
||||||
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
||||||
run_ansi_sequence(&format!(
|
run_ansi_sequence(&format!(
|
||||||
"{VSCODE_COMMANDLINE_MARKER_PREFIX}{replaced_cmd_text}{VSCODE_COMMANDLINE_MARKER_SUFFIX}"
|
"{}{}{}",
|
||||||
|
VSCODE_COMMANDLINE_MARKER_PREFIX,
|
||||||
|
replaced_cmd_text,
|
||||||
|
VSCODE_COMMANDLINE_MARKER_SUFFIX
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1489,7 +1493,7 @@ mod test_auto_cd {
|
|||||||
// 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();
|
||||||
let ReplOperation::AutoCd { cwd, target, span } = op else {
|
let ReplOperation::AutoCd { cwd, target, span } = op else {
|
||||||
panic!("'{input}' was not parsed into an auto-cd operation")
|
panic!("'{}' was not parsed into an auto-cd operation", input)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Perform the auto-cd operation.
|
// Perform the auto-cd operation.
|
||||||
|
@ -17,173 +17,147 @@ pub struct NuHighlighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Highlighter for NuHighlighter {
|
impl Highlighter for NuHighlighter {
|
||||||
fn highlight(&self, line: &str, cursor: usize) -> StyledText {
|
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||||
let result = highlight_syntax(&self.engine_state, &self.stack, line, cursor);
|
trace!("highlighting: {}", line);
|
||||||
result.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a syntax highlight operation
|
let config = self.stack.get_config(&self.engine_state);
|
||||||
#[derive(Default)]
|
let highlight_resolved_externals = config.highlight_resolved_externals;
|
||||||
pub(crate) struct HighlightResult {
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
/// The highlighted text
|
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
pub(crate) text: StyledText,
|
let (shapes, global_span_offset) = {
|
||||||
/// The span of any garbage that was highlighted
|
let mut shapes = flatten_block(&working_set, &block);
|
||||||
pub(crate) found_garbage: Option<Span>,
|
// Highlighting externals has a config point because of concerns that using which to resolve
|
||||||
}
|
// externals may slow down things too much.
|
||||||
|
if highlight_resolved_externals {
|
||||||
|
for (span, shape) in shapes.iter_mut() {
|
||||||
|
if *shape == FlatShape::External {
|
||||||
|
let str_contents =
|
||||||
|
working_set.get_span_contents(Span::new(span.start, span.end));
|
||||||
|
|
||||||
pub(crate) fn highlight_syntax(
|
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
||||||
engine_state: &EngineState,
|
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
|
||||||
stack: &Stack,
|
#[allow(deprecated)]
|
||||||
line: &str,
|
let res = if let Ok(cwd) =
|
||||||
cursor: usize,
|
env::current_dir_str(&self.engine_state, &self.stack)
|
||||||
) -> HighlightResult {
|
{
|
||||||
trace!("highlighting: {}", line);
|
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
||||||
|
} else {
|
||||||
let config = stack.get_config(engine_state);
|
which::which_in_global(str_word, paths.as_ref())
|
||||||
let highlight_resolved_externals = config.highlight_resolved_externals;
|
.ok()
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
.and_then(|mut i| i.next())
|
||||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
};
|
||||||
let (shapes, global_span_offset) = {
|
if res.is_some() {
|
||||||
let mut shapes = flatten_block(&working_set, &block);
|
*shape = FlatShape::ExternalResolved;
|
||||||
// Highlighting externals has a config point because of concerns that using which to resolve
|
}
|
||||||
// externals may slow down things too much.
|
|
||||||
if highlight_resolved_externals {
|
|
||||||
for (span, shape) in shapes.iter_mut() {
|
|
||||||
if *shape == FlatShape::External {
|
|
||||||
let str_contents =
|
|
||||||
working_set.get_span_contents(Span::new(span.start, span.end));
|
|
||||||
|
|
||||||
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
|
||||||
let paths = env::path_str(engine_state, stack, *span).ok();
|
|
||||||
let res = if let Ok(cwd) = engine_state.cwd(Some(stack)) {
|
|
||||||
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
|
||||||
} else {
|
|
||||||
which::which_in_global(str_word, paths.as_ref())
|
|
||||||
.ok()
|
|
||||||
.and_then(|mut i| i.next())
|
|
||||||
};
|
|
||||||
if res.is_some() {
|
|
||||||
*shape = FlatShape::ExternalResolved;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
(shapes, self.engine_state.next_span_start())
|
||||||
(shapes, engine_state.next_span_start())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut result = HighlightResult::default();
|
|
||||||
let mut last_seen_span = global_span_offset;
|
|
||||||
|
|
||||||
let global_cursor_offset = cursor + global_span_offset;
|
|
||||||
let matching_brackets_pos = find_matching_brackets(
|
|
||||||
line,
|
|
||||||
&working_set,
|
|
||||||
&block,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
for shape in &shapes {
|
|
||||||
if shape.0.end <= last_seen_span
|
|
||||||
|| last_seen_span < global_span_offset
|
|
||||||
|| shape.0.start < global_span_offset
|
|
||||||
{
|
|
||||||
// We've already output something for this span
|
|
||||||
// so just skip this one
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if shape.0.start > last_seen_span {
|
|
||||||
let gap = line
|
|
||||||
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
|
||||||
.to_string();
|
|
||||||
result.text.push((Style::new(), gap));
|
|
||||||
}
|
|
||||||
let next_token = line
|
|
||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
|
||||||
result
|
|
||||||
.text
|
|
||||||
.push((get_shape_color(shape.as_str(), &config), text));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match shape.1 {
|
let mut output = StyledText::default();
|
||||||
FlatShape::Garbage => {
|
let mut last_seen_span = global_span_offset;
|
||||||
result.found_garbage.get_or_insert_with(|| {
|
|
||||||
Span::new(
|
let global_cursor_offset = _cursor + global_span_offset;
|
||||||
shape.0.start - global_span_offset,
|
let matching_brackets_pos = find_matching_brackets(
|
||||||
shape.0.end - global_span_offset,
|
line,
|
||||||
)
|
&working_set,
|
||||||
});
|
&block,
|
||||||
add_colored_token(&shape.1, next_token)
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
for shape in &shapes {
|
||||||
|
if shape.0.end <= last_seen_span
|
||||||
|
|| last_seen_span < global_span_offset
|
||||||
|
|| shape.0.start < global_span_offset
|
||||||
|
{
|
||||||
|
// We've already output something for this span
|
||||||
|
// so just skip this one
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
if shape.0.start > last_seen_span {
|
||||||
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
let gap = line
|
||||||
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
||||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
.to_string();
|
||||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
output.push((Style::new(), gap));
|
||||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
}
|
||||||
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
let next_token = line
|
||||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
.to_string();
|
||||||
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
output.push((get_shape_color(shape.as_str(), &config), text));
|
||||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
};
|
||||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
match shape.1 {
|
||||||
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::List
|
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
||||||
| FlatShape::Table
|
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||||
| FlatShape::Record
|
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||||
| FlatShape::Block
|
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||||
| FlatShape::Closure => {
|
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
||||||
let span = shape.0;
|
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||||
let shape = &shape.1;
|
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||||
let spans = split_span_by_highlight_positions(
|
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
||||||
line,
|
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
||||||
span,
|
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||||
&matching_brackets_pos,
|
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||||
global_span_offset,
|
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||||
);
|
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||||
for (part, highlight) in spans {
|
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
||||||
let start = part.start - span.start;
|
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
let end = part.end - span.start;
|
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||||
let text = next_token[start..end].to_string();
|
FlatShape::List
|
||||||
let mut style = get_shape_color(shape.as_str(), &config);
|
| FlatShape::Table
|
||||||
if highlight {
|
| FlatShape::Record
|
||||||
style = get_matching_brackets_style(style, &config);
|
| FlatShape::Block
|
||||||
|
| FlatShape::Closure => {
|
||||||
|
let span = shape.0;
|
||||||
|
let shape = &shape.1;
|
||||||
|
let spans = split_span_by_highlight_positions(
|
||||||
|
line,
|
||||||
|
span,
|
||||||
|
&matching_brackets_pos,
|
||||||
|
global_span_offset,
|
||||||
|
);
|
||||||
|
for (part, highlight) in spans {
|
||||||
|
let start = part.start - span.start;
|
||||||
|
let end = part.end - span.start;
|
||||||
|
let text = next_token[start..end].to_string();
|
||||||
|
let mut style = get_shape_color(shape.as_str(), &config);
|
||||||
|
if highlight {
|
||||||
|
style = get_matching_brackets_style(style, &config);
|
||||||
|
}
|
||||||
|
output.push((style, text));
|
||||||
}
|
}
|
||||||
result.text.push((style, text));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||||
add_colored_token(&shape.1, next_token)
|
add_colored_token(&shape.1, next_token)
|
||||||
|
}
|
||||||
|
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
||||||
}
|
}
|
||||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
last_seen_span = shape.0.end;
|
||||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
||||||
if !remainder.is_empty() {
|
if !remainder.is_empty() {
|
||||||
result.text.push((Style::new(), remainder));
|
output.push((Style::new(), remainder));
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_span_by_highlight_positions(
|
fn split_span_by_highlight_positions(
|
||||||
|
@ -5,9 +5,9 @@ use nu_engine::{eval_block, eval_block_with_early_return};
|
|||||||
use nu_parser::{Token, TokenContents, lex, parse, unescape_unquote_string};
|
use nu_parser::{Token, TokenContents, lex, parse, unescape_unquote_string};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
PipelineData, ShellError, Span, Value,
|
PipelineData, ShellError, Span, Value,
|
||||||
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error::report_compile_error,
|
|
||||||
report_parse_error, report_parse_warning, report_shell_error,
|
report_parse_error, report_parse_warning, report_shell_error,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -5,9 +5,3 @@ fn nu_highlight_not_expr() {
|
|||||||
let actual = nu!("'not false' | nu-highlight | ansi strip");
|
let actual = nu!("'not false' | nu-highlight | ansi strip");
|
||||||
assert_eq!(actual.out, "not false");
|
assert_eq!(actual.out, "not false");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nu_highlight_where_row_condition() {
|
|
||||||
let actual = nu!("'ls | where a b 12345(' | nu-highlight | ansi strip");
|
|
||||||
assert_eq!(actual.out, "ls | where a b 12345(");
|
|
||||||
}
|
|
||||||
|
@ -9,16 +9,14 @@ use std::{
|
|||||||
use nu_cli::NuCompleter;
|
use nu_cli::NuCompleter;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::{AbsolutePathBuf, expand_tilde};
|
use nu_path::expand_tilde;
|
||||||
use nu_protocol::{Config, PipelineData, debugger::WithoutDebug, engine::StateWorkingSet};
|
use nu_protocol::{Config, PipelineData, debugger::WithoutDebug, engine::StateWorkingSet};
|
||||||
use nu_std::load_standard_library;
|
use nu_std::load_standard_library;
|
||||||
use nu_test_support::fs;
|
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use support::{
|
use support::{
|
||||||
completions_helpers::{
|
completions_helpers::{
|
||||||
new_dotnu_engine, new_engine_helper, new_external_engine, new_partial_engine,
|
new_dotnu_engine, new_external_engine, new_partial_engine, new_quote_engine,
|
||||||
new_quote_engine,
|
|
||||||
},
|
},
|
||||||
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
||||||
};
|
};
|
||||||
@ -125,7 +123,7 @@ fn custom_completer_with_options(
|
|||||||
global_opts,
|
global_opts,
|
||||||
completions
|
completions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|comp| format!("'{comp}'"))
|
.map(|comp| format!("'{}'", comp))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "),
|
.join(", "),
|
||||||
completer_opts,
|
completer_opts,
|
||||||
@ -309,10 +307,10 @@ fn custom_arguments_and_subcommands() {
|
|||||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
// including both subcommand and directory completions
|
// including both subcommand and directory completions
|
||||||
let expected = [
|
let expected = [
|
||||||
|
"foo test bar".into(),
|
||||||
folder("test_a"),
|
folder("test_a"),
|
||||||
file("test_a_symlink"),
|
file("test_a_symlink"),
|
||||||
folder("test_b"),
|
folder("test_b"),
|
||||||
"foo test bar".into(),
|
|
||||||
];
|
];
|
||||||
match_suggestions_by_string(&expected, &suggestions);
|
match_suggestions_by_string(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
@ -330,7 +328,7 @@ fn custom_flags_and_subcommands() {
|
|||||||
let completion_str = "foo --test";
|
let completion_str = "foo --test";
|
||||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
// including both flag and directory completions
|
// including both flag and directory completions
|
||||||
let expected: Vec<_> = vec!["--test", "foo --test bar"];
|
let expected: Vec<_> = vec!["foo --test bar", "--test"];
|
||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,16 +716,6 @@ fn external_completer_fallback() {
|
|||||||
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
||||||
let suggestions = run_external_completion(block, input);
|
let suggestions = run_external_completion(block, input);
|
||||||
match_suggestions_by_string(&expected, &suggestions);
|
match_suggestions_by_string(&expected, &suggestions);
|
||||||
|
|
||||||
// issue #15790
|
|
||||||
let input = "foo `dir with space/`";
|
|
||||||
let expected = vec!["`dir with space/bar baz`", "`dir with space/foo`"];
|
|
||||||
let suggestions = run_external_completion_within_pwd(
|
|
||||||
block,
|
|
||||||
input,
|
|
||||||
fs::fixtures().join("external_completions"),
|
|
||||||
);
|
|
||||||
match_suggestions(&expected, &suggestions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallback to external completions for flags of `sudo`
|
/// Fallback to external completions for flags of `sudo`
|
||||||
@ -1480,12 +1468,11 @@ fn command_watch_with_filecompletion() {
|
|||||||
match_suggestions(&expected_paths, &suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[rstest]
|
||||||
fn subcommand_vs_external_completer() {
|
fn subcommand_completions() {
|
||||||
let (_, _, mut engine, mut stack) = new_engine();
|
let (_, _, mut engine, mut stack) = new_engine();
|
||||||
let commands = r#"
|
let commands = r#"
|
||||||
$env.config.completions.algorithm = "fuzzy"
|
$env.config.completions.algorithm = "fuzzy"
|
||||||
$env.config.completions.external.completer = {|spans| ["external"]}
|
|
||||||
def foo-test-command [] {}
|
def foo-test-command [] {}
|
||||||
def "foo-test-command bar" [] {}
|
def "foo-test-command bar" [] {}
|
||||||
def "foo-test-command aagap bcr" [] {}
|
def "foo-test-command aagap bcr" [] {}
|
||||||
@ -1498,7 +1485,6 @@ fn subcommand_vs_external_completer() {
|
|||||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
match_suggestions(
|
match_suggestions(
|
||||||
&vec![
|
&vec![
|
||||||
"external",
|
|
||||||
"food bar",
|
"food bar",
|
||||||
"foo-test-command bar",
|
"foo-test-command bar",
|
||||||
"foo-test-command aagap bcr",
|
"foo-test-command aagap bcr",
|
||||||
@ -1508,7 +1494,7 @@ fn subcommand_vs_external_completer() {
|
|||||||
|
|
||||||
let prefix = "foot bar";
|
let prefix = "foot bar";
|
||||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
match_suggestions(&vec!["external", "foo-test-command bar"], &suggestions);
|
match_suggestions(&vec!["foo-test-command bar"], &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2117,15 +2103,11 @@ fn alias_of_another_alias() {
|
|||||||
match_suggestions(&expected_paths, &suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion_within_pwd(
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||||
completer: &str,
|
|
||||||
input: &str,
|
|
||||||
pwd: AbsolutePathBuf,
|
|
||||||
) -> Vec<Suggestion> {
|
|
||||||
let completer = format!("$env.config.completions.external.completer = {completer}");
|
let completer = format!("$env.config.completions.external.completer = {completer}");
|
||||||
|
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, mut engine_state, mut stack) = new_engine_helper(pwd);
|
let (_, _, mut engine_state, mut stack) = new_engine();
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
||||||
@ -2149,10 +2131,6 @@ fn run_external_completion_within_pwd(
|
|||||||
completer.complete(input, input.len())
|
completer.complete(input, input.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
|
||||||
run_external_completion_within_pwd(completer, input, fs::fixtures().join("completions"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_command_completion() {
|
fn unknown_command_completion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
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.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
# 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,10 +13,10 @@ version = "0.106.1"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.106.1" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.106.1" }
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use miette::Result;
|
use miette::Result;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return, redirect_env};
|
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
||||||
|
cli_error::{report_parse_error, report_shell_error},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
report_error::{report_parse_error, report_shell_error},
|
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
@ -325,7 +325,19 @@ fn run_hook(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If all went fine, preserve the environment of the called block
|
// If all went fine, preserve the environment of the called block
|
||||||
redirect_env(engine_state, stack, &callee_stack);
|
let caller_env_vars = stack.get_env_var_names(engine_state);
|
||||||
|
|
||||||
|
// remove env vars that are present in the caller but not in the callee
|
||||||
|
// (the callee hid them)
|
||||||
|
for var in caller_env_vars.iter() {
|
||||||
|
if !callee_stack.has_env_var(engine_state, var) {
|
||||||
|
stack.remove_env_var(engine_state, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new env vars from callee to caller
|
||||||
|
for (var, value) in callee_stack.get_stack_env_vars() {
|
||||||
|
stack.add_env_var(var, value);
|
||||||
|
}
|
||||||
Ok(pipeline_data)
|
Ok(pipeline_data)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
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.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -16,13 +16,13 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-json = { version = "0.106.1", path = "../nu-json" }
|
nu-json = { version = "0.105.2", path = "../nu-json" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.106.1" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-pretty-hex = { version = "0.106.1", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.105.2", path = "../nu-pretty-hex" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
|
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
@ -37,6 +37,6 @@ itertools = { workspace = true }
|
|||||||
mime = { workspace = true }
|
mime = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.106.1" }
|
nu-command = { path = "../nu-command", version = "0.105.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
|
@ -12,10 +12,7 @@ impl Command for UpdateCells {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("update cells")
|
Signature::build("update cells")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![(Type::table(), Type::table())])
|
||||||
(Type::table(), Type::table()),
|
|
||||||
(Type::record(), Type::record()),
|
|
||||||
])
|
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"closure",
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||||
@ -80,15 +77,6 @@ impl Command for UpdateCells {
|
|||||||
"2021-11-18" => Value::test_string(""),
|
"2021-11-18" => Value::test_string(""),
|
||||||
})])),
|
})])),
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
example: r#"{a: 1, b: 2, c: 3} | update cells { $in + 10 }"#,
|
|
||||||
description: "Update each value in a record.",
|
|
||||||
result: Some(Value::test_record(record! {
|
|
||||||
"a" => Value::test_int(11),
|
|
||||||
"b" => Value::test_int(12),
|
|
||||||
"c" => Value::test_int(13),
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +85,7 @@ impl Command for UpdateCells {
|
|||||||
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 closure: Closure = call.req(engine_state, stack, 0)?;
|
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||||
@ -114,51 +102,14 @@ impl Command for UpdateCells {
|
|||||||
|
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
|
||||||
match input {
|
Ok(UpdateCellIterator {
|
||||||
PipelineData::Value(
|
iter: input.into_iter(),
|
||||||
Value::Record {
|
closure: ClosureEval::new(engine_state, stack, closure),
|
||||||
ref mut val,
|
columns,
|
||||||
internal_span,
|
span: head,
|
||||||
},
|
|
||||||
..,
|
|
||||||
) => {
|
|
||||||
let val = val.to_mut();
|
|
||||||
update_record(
|
|
||||||
val,
|
|
||||||
&mut ClosureEval::new(engine_state, stack, closure),
|
|
||||||
internal_span,
|
|
||||||
columns.as_ref(),
|
|
||||||
);
|
|
||||||
Ok(input)
|
|
||||||
}
|
|
||||||
_ => Ok(UpdateCellIterator {
|
|
||||||
iter: input.into_iter(),
|
|
||||||
closure: ClosureEval::new(engine_state, stack, closure),
|
|
||||||
columns,
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data(head, engine_state.signals().clone())
|
|
||||||
.set_metadata(metadata)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_record(
|
|
||||||
record: &mut Record,
|
|
||||||
closure: &mut ClosureEval,
|
|
||||||
span: Span,
|
|
||||||
cols: Option<&HashSet<String>>,
|
|
||||||
) {
|
|
||||||
if let Some(columns) = cols {
|
|
||||||
for (col, val) in record.iter_mut() {
|
|
||||||
if columns.contains(col) {
|
|
||||||
*val = eval_value(closure, span, std::mem::take(val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (_, val) in record.iter_mut() {
|
|
||||||
*val = eval_value(closure, span, std::mem::take(val))
|
|
||||||
}
|
}
|
||||||
|
.into_pipeline_data(head, engine_state.signals().clone())
|
||||||
|
.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +128,18 @@ impl Iterator for UpdateCellIterator {
|
|||||||
|
|
||||||
let value = if let Value::Record { val, .. } = &mut value {
|
let value = if let Value::Record { val, .. } = &mut value {
|
||||||
let val = val.to_mut();
|
let val = val.to_mut();
|
||||||
update_record(val, &mut self.closure, self.span, self.columns.as_ref());
|
if let Some(columns) = &self.columns {
|
||||||
|
for (col, val) in val.iter_mut() {
|
||||||
|
if columns.contains(col) {
|
||||||
|
*val = eval_value(&mut self.closure, self.span, std::mem::take(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (_, val) in val.iter_mut() {
|
||||||
|
*val = eval_value(&mut self.closure, self.span, std::mem::take(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
eval_value(&mut self.closure, self.span, value)
|
eval_value(&mut self.closure, self.span, value)
|
||||||
|
@ -188,7 +188,7 @@ fn get_theme_from_asset_file(
|
|||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message: format!("Unknown HTML theme '{theme_name}'"),
|
err_message: format!("Unknown HTML theme '{}'", theme_name),
|
||||||
span: theme_span,
|
span: theme_span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -774,7 +774,8 @@ mod tests {
|
|||||||
for key in required_keys {
|
for key in required_keys {
|
||||||
assert!(
|
assert!(
|
||||||
theme_map.contains_key(key),
|
theme_map.contains_key(key),
|
||||||
"Expected theme to contain key '{key}'"
|
"Expected theme to contain key '{}'",
|
||||||
|
key
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -791,13 +792,15 @@ mod tests {
|
|||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
assert!(
|
assert!(
|
||||||
matches!(err, ShellError::TypeMismatch { .. }),
|
matches!(err, ShellError::TypeMismatch { .. }),
|
||||||
"Expected TypeMismatch error, got: {err:?}"
|
"Expected TypeMismatch error, got: {:?}",
|
||||||
|
err
|
||||||
);
|
);
|
||||||
|
|
||||||
if let ShellError::TypeMismatch { err_message, span } = err {
|
if let ShellError::TypeMismatch { err_message, span } = err {
|
||||||
assert!(
|
assert!(
|
||||||
err_message.contains("doesnt-exist"),
|
err_message.contains("doesnt-exist"),
|
||||||
"Error message should mention theme name, got: {err_message}"
|
"Error message should mention theme name, got: {}",
|
||||||
|
err_message
|
||||||
);
|
);
|
||||||
assert_eq!(span.start, 0);
|
assert_eq!(span.start, 0);
|
||||||
assert_eq!(span.end, 13);
|
assert_eq!(span.end, 13);
|
||||||
|
@ -161,28 +161,28 @@ fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
|
|||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else if let Some(v) = num.to_i16() {
|
} else if let Some(v) = num.to_i16() {
|
||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else if let Some(v) = num.to_i32() {
|
} else if let Some(v) = num.to_i32() {
|
||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else {
|
} else {
|
||||||
let bytes = num.to_ne_bytes();
|
let bytes = num.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
Value::Binary { val, .. } => {
|
Value::Binary { val, .. } => {
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in val {
|
for ch in val {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
let raw_bytes = val.as_bytes();
|
let raw_bytes = val.as_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in raw_bytes {
|
for ch in raw_bytes {
|
||||||
raw_string.push_str(&format!("{ch:08b} "));
|
raw_string.push_str(&format!("{:08b} ", ch));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,6 @@ impl Command for FormatNumber {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("format number")
|
Signature::build("format number")
|
||||||
.input_output_types(vec![(Type::Number, Type::record())])
|
.input_output_types(vec![(Type::Number, Type::record())])
|
||||||
.switch(
|
|
||||||
"no-prefix",
|
|
||||||
"don't include the binary, hex or octal prefixes",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,36 +24,20 @@ impl Command for FormatNumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![Example {
|
||||||
Example {
|
description: "Get a record containing multiple formats for the number 42",
|
||||||
description: "Get a record containing multiple formats for the number 42",
|
example: "42 | format number",
|
||||||
example: "42 | format number",
|
result: Some(Value::test_record(record! {
|
||||||
result: Some(Value::test_record(record! {
|
"binary" => Value::test_string("0b101010"),
|
||||||
"debug" => Value::test_string("42"),
|
"debug" => Value::test_string("42"),
|
||||||
"display" => Value::test_string("42"),
|
"display" => Value::test_string("42"),
|
||||||
"binary" => Value::test_string("0b101010"),
|
"lowerexp" => Value::test_string("4.2e1"),
|
||||||
"lowerexp" => Value::test_string("4.2e1"),
|
"lowerhex" => Value::test_string("0x2a"),
|
||||||
"upperexp" => Value::test_string("4.2E1"),
|
"octal" => Value::test_string("0o52"),
|
||||||
"lowerhex" => Value::test_string("0x2a"),
|
"upperexp" => Value::test_string("4.2E1"),
|
||||||
"upperhex" => Value::test_string("0x2A"),
|
"upperhex" => Value::test_string("0x2A"),
|
||||||
"octal" => Value::test_string("0o52"),
|
})),
|
||||||
})),
|
}]
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Format float without prefixes",
|
|
||||||
example: "3.14 | format number --no-prefix",
|
|
||||||
result: Some(Value::test_record(record! {
|
|
||||||
"debug" => Value::test_string("3.14"),
|
|
||||||
"display" => Value::test_string("3.14"),
|
|
||||||
"binary" => Value::test_string("100000000001001000111101011100001010001111010111000010100011111"),
|
|
||||||
"lowerexp" => Value::test_string("3.14e0"),
|
|
||||||
"upperexp" => Value::test_string("3.14E0"),
|
|
||||||
"lowerhex" => Value::test_string("40091eb851eb851f"),
|
|
||||||
"upperhex" => Value::test_string("40091EB851EB851F"),
|
|
||||||
"octal" => Value::test_string("400110753412172702437"),
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -80,24 +59,14 @@ pub(crate) fn format_number(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let args = CellPathOnlyArgs::from(cell_paths);
|
let args = CellPathOnlyArgs::from(cell_paths);
|
||||||
if call.has_flag(engine_state, stack, "no-prefix")? {
|
operate(action, args, input, call.head, engine_state.signals())
|
||||||
operate(
|
|
||||||
action_no_prefix,
|
|
||||||
args,
|
|
||||||
input,
|
|
||||||
call.head,
|
|
||||||
engine_state.signals(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
operate(action, args, input, call.head, engine_state.signals())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Float { val, .. } => format_f64(*val, false, span),
|
Value::Float { val, .. } => format_f64(*val, span),
|
||||||
Value::Int { val, .. } => format_i64(*val, false, span),
|
Value::Int { val, .. } => format_i64(*val, span),
|
||||||
Value::Filesize { val, .. } => format_i64(val.get(), false, span),
|
Value::Filesize { val, .. } => format_i64(val.get(), span),
|
||||||
// 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(
|
||||||
@ -112,80 +81,33 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_no_prefix(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn format_i64(num: i64, span: Span) -> Value {
|
||||||
match input {
|
|
||||||
Value::Float { val, .. } => format_f64(*val, true, span),
|
|
||||||
Value::Int { val, .. } => format_i64(*val, true, span),
|
|
||||||
Value::Filesize { val, .. } => format_i64(val.get(), true, span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => input.clone(),
|
|
||||||
other => Value::error(
|
|
||||||
ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "float, int, or filesize".into(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: span,
|
|
||||||
src_span: other.span(),
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_i64(num: i64, no_prefix: bool, span: Span) -> Value {
|
|
||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
|
"binary" => Value::string(format!("{num:#b}"), span),
|
||||||
"debug" => Value::string(format!("{num:#?}"), span),
|
"debug" => Value::string(format!("{num:#?}"), span),
|
||||||
"display" => Value::string(format!("{num}"), span),
|
"display" => Value::string(format!("{num}"), span),
|
||||||
"binary" => Value::string(
|
|
||||||
if no_prefix { format!("{num:b}") } else { format!("{num:#b}") },
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||||
|
"lowerhex" => Value::string(format!("{num:#x}"), span),
|
||||||
|
"octal" => Value::string(format!("{num:#o}"), span),
|
||||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||||
"lowerhex" => Value::string(
|
"upperhex" => Value::string(format!("{num:#X}"), span),
|
||||||
if no_prefix { format!("{num:x}") } else { format!("{num:#x}") },
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"upperhex" => Value::string(
|
|
||||||
if no_prefix { format!("{num:X}") } else { format!("{num:#X}") },
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"octal" => Value::string(
|
|
||||||
if no_prefix { format!("{num:o}") } else { format!("{num:#o}") },
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_f64(num: f64, no_prefix: bool, span: Span) -> Value {
|
fn format_f64(num: f64, span: Span) -> Value {
|
||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
|
"binary" => Value::string(format!("{:b}", num.to_bits()), span),
|
||||||
"debug" => Value::string(format!("{num:#?}"), span),
|
"debug" => Value::string(format!("{num:#?}"), span),
|
||||||
"display" => Value::string(format!("{num}"), span),
|
"display" => Value::string(format!("{num}"), span),
|
||||||
"binary" => Value::string(
|
|
||||||
if no_prefix {
|
|
||||||
format!("{:b}", num.to_bits())
|
|
||||||
} else {
|
|
||||||
format!("{:#b}", num.to_bits())
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||||
|
"lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span),
|
||||||
|
"octal" => Value::string(format!("{:0o}", num.to_bits()), span),
|
||||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||||
"lowerhex" => Value::string(
|
"upperhex" => Value::string(format!("{:0X}", num.to_bits()), span),
|
||||||
if no_prefix { format!("{:x}", num.to_bits()) } else { format!("{:#x}", num.to_bits()) },
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"upperhex" => Value::string(
|
|
||||||
if no_prefix { format!("{:X}", num.to_bits()) } else { format!("{:#X}", num.to_bits()) },
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
"octal" => Value::string(
|
|
||||||
if no_prefix { format!("{:o}", num.to_bits()) } else { format!("{:#o}", num.to_bits()) },
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
@ -15,18 +15,17 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.106.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
|
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
|
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
shadow-rs = { version = "1.2", default-features = false }
|
shadow-rs = { version = "1.1", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "1.2", default-features = false, features = ["build"] }
|
shadow-rs = { version = "1.1", default-features = false, features = ["build"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = { workspace = true }
|
quickcheck = { workspace = true }
|
||||||
@ -44,3 +43,8 @@ plugin = [
|
|||||||
"nu-protocol/plugin",
|
"nu-protocol/plugin",
|
||||||
"os",
|
"os",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
trash-support = []
|
||||||
|
sqlite = []
|
||||||
|
static-link-openssl = []
|
||||||
|
system-clipboard = []
|
||||||
|
@ -296,7 +296,7 @@ fn run(
|
|||||||
} else {
|
} else {
|
||||||
let value = stream.into_value();
|
let value = stream.into_value();
|
||||||
let base_description = value.get_type().to_string();
|
let base_description = value.get_type().to_string();
|
||||||
Value::string(format!("{base_description} (stream)"), head)
|
Value::string(format!("{} (stream)", base_description), head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::Value(value, ..) => {
|
PipelineData::Value(value, ..) => {
|
||||||
|
@ -264,7 +264,7 @@ fn bind_args_to(
|
|||||||
.expect("internal error: all custom parameters must have var_ids");
|
.expect("internal error: all custom parameters must have var_ids");
|
||||||
if let Some(result) = val_iter.next() {
|
if let Some(result) = val_iter.next() {
|
||||||
let param_type = param.shape.to_type();
|
let param_type = param.shape.to_type();
|
||||||
if !result.is_subtype_of(¶m_type) {
|
if required && !result.is_subtype_of(¶m_type) {
|
||||||
return Err(ShellError::CantConvert {
|
return Err(ShellError::CantConvert {
|
||||||
to_type: param.shape.to_type().to_string(),
|
to_type: param.shape.to_type().to_string(),
|
||||||
from_type: result.get_type().to_string(),
|
from_type: result.get_type().to_string(),
|
||||||
|
@ -229,7 +229,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
error: "invalid error format.".into(),
|
error: "invalid error format.".into(),
|
||||||
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
|
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
|
||||||
span: Some(label_span),
|
span: Some(label_span),
|
||||||
help: Some(format!("{span_start} > {span_end}")),
|
help: Some(format!("{} > {}", span_start, span_end)),
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -60,13 +60,11 @@ impl Command for If {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let call = call.assert_ast_call()?;
|
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_expr = call.positional_nth(1).expect("checked through parser");
|
let then_block = call
|
||||||
let then_block = then_expr
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
.as_block()
|
.as_block()
|
||||||
.ok_or_else(|| ShellError::TypeMismatch {
|
.expect("internal error: missing block");
|
||||||
err_message: "expected block".into(),
|
|
||||||
span: then_expr.span,
|
|
||||||
})?;
|
|
||||||
let else_case = call.positional_nth(2);
|
let else_case = call.positional_nth(2);
|
||||||
|
|
||||||
if eval_constant(working_set, cond)?.as_bool()? {
|
if eval_constant(working_set, cond)?.as_bool()? {
|
||||||
|
@ -69,5 +69,5 @@ pub use return_::Return;
|
|||||||
pub use scope::*;
|
pub use scope::*;
|
||||||
pub use try_::Try;
|
pub use try_::Try;
|
||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
pub use version::{VERSION_NU_FEATURES, Version};
|
pub use version::Version;
|
||||||
pub use while_::While;
|
pub use while_::While;
|
||||||
|
@ -96,7 +96,6 @@ impl Command for OverlayHide {
|
|||||||
for (name, val) in env_vars_to_keep {
|
for (name, val) in env_vars_to_keep {
|
||||||
stack.add_env_var(name, val);
|
stack.add_env_var(name, val);
|
||||||
}
|
}
|
||||||
stack.update_config(engine_state)?;
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ impl Command for OverlayUse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
let _ = eval_block(engine_state, &mut callee_stack, block, input)?;
|
let _ = eval_block(engine_state, &mut callee_stack, block, input);
|
||||||
|
|
||||||
// The export-env block should see the env vars *before* activating this overlay
|
// The export-env block should see the env vars *before* activating this overlay
|
||||||
caller_stack.add_overlay(overlay_name);
|
caller_stack.add_overlay(overlay_name);
|
||||||
@ -178,7 +178,6 @@ impl Command for OverlayUse {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
caller_stack.add_overlay(overlay_name);
|
caller_stack.add_overlay(overlay_name);
|
||||||
caller_stack.update_config(engine_state)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
|
@ -1,48 +1,11 @@
|
|||||||
use std::{borrow::Cow, sync::OnceLock};
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use shadow_rs::shadow;
|
use shadow_rs::shadow;
|
||||||
|
|
||||||
shadow!(build);
|
shadow!(build);
|
||||||
|
|
||||||
/// Static container for the cargo features used by the `version` command.
|
|
||||||
///
|
|
||||||
/// This `OnceLock` holds the features from `nu`.
|
|
||||||
/// When you build `nu_cmd_lang`, Cargo doesn't pass along the same features that `nu` itself uses.
|
|
||||||
/// By setting this static before calling `version`, you make it show `nu`'s features instead
|
|
||||||
/// of `nu_cmd_lang`'s.
|
|
||||||
///
|
|
||||||
/// Embedders can set this to any feature list they need, but in most cases you'll probably want to
|
|
||||||
/// pass the cargo features of your host binary.
|
|
||||||
///
|
|
||||||
/// # How to get cargo features in your build script
|
|
||||||
///
|
|
||||||
/// In your binary's build script:
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// // Re-export CARGO_CFG_FEATURE to the main binary.
|
|
||||||
/// // It holds all the features that cargo sets for your binary as a comma-separated list.
|
|
||||||
/// println!(
|
|
||||||
/// "cargo:rustc-env=NU_FEATURES={}",
|
|
||||||
/// std::env::var("CARGO_CFG_FEATURE").expect("set by cargo")
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Then, before you call `version`:
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// // This uses static strings, but since we're using `Cow`, you can also pass owned strings.
|
|
||||||
/// let features = env!("NU_FEATURES")
|
|
||||||
/// .split(',')
|
|
||||||
/// .map(Cow::Borrowed)
|
|
||||||
/// .collect();
|
|
||||||
///
|
|
||||||
/// nu_cmd_lang::VERSION_NU_FEATURES
|
|
||||||
/// .set(features)
|
|
||||||
/// .expect("couldn't set VERSION_NU_FEATURES");
|
|
||||||
/// ```
|
|
||||||
pub static VERSION_NU_FEATURES: OnceLock<Vec<Cow<'static, str>>> = OnceLock::new();
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Version;
|
pub struct Version;
|
||||||
|
|
||||||
@ -150,17 +113,7 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
|
|||||||
|
|
||||||
record.push(
|
record.push(
|
||||||
"features",
|
"features",
|
||||||
Value::string(
|
Value::string(features_enabled().join(", "), span),
|
||||||
VERSION_NU_FEATURES
|
|
||||||
.get()
|
|
||||||
.as_ref()
|
|
||||||
.map(|v| v.as_slice())
|
|
||||||
.unwrap_or_default()
|
|
||||||
.iter()
|
|
||||||
.filter(|f| !f.starts_with("dep:"))
|
|
||||||
.join(", "),
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "plugin"))]
|
#[cfg(not(feature = "plugin"))]
|
||||||
@ -188,17 +141,6 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
record.push(
|
|
||||||
"experimental_options",
|
|
||||||
Value::string(
|
|
||||||
nu_experimental::ALL
|
|
||||||
.iter()
|
|
||||||
.map(|option| format!("{}={}", option.identifier(), option.get()))
|
|
||||||
.join(", "),
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Value::record(record, span).into_pipeline_data())
|
Ok(Value::record(record, span).into_pipeline_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,12 +164,42 @@ fn global_allocator() -> &'static str {
|
|||||||
"standard"
|
"standard"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn features_enabled() -> Vec<String> {
|
||||||
|
let mut names = vec!["default".to_string()];
|
||||||
|
|
||||||
|
// NOTE: There should be another way to know features on.
|
||||||
|
|
||||||
|
#[cfg(feature = "trash-support")]
|
||||||
|
{
|
||||||
|
names.push("trash".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlite")]
|
||||||
|
{
|
||||||
|
names.push("sqlite".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "static-link-openssl")]
|
||||||
|
{
|
||||||
|
names.push("static-link-openssl".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "system-clipboard")]
|
||||||
|
{
|
||||||
|
names.push("system-clipboard".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
names.sort();
|
||||||
|
|
||||||
|
names
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
use super::Version;
|
use super::Version;
|
||||||
use crate::test_examples;
|
use crate::test_examples;
|
||||||
test_examples(Version)
|
test_examples(Version {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,23 +221,23 @@ impl std::fmt::Debug for DebuggableValue<'_> {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Value::Bool { val, .. } => {
|
Value::Bool { val, .. } => {
|
||||||
write!(f, "{val:?}")
|
write!(f, "{:?}", val)
|
||||||
}
|
}
|
||||||
Value::Int { val, .. } => {
|
Value::Int { val, .. } => {
|
||||||
write!(f, "{val:?}")
|
write!(f, "{:?}", val)
|
||||||
}
|
}
|
||||||
Value::Float { val, .. } => {
|
Value::Float { val, .. } => {
|
||||||
write!(f, "{val:?}f")
|
write!(f, "{:?}f", val)
|
||||||
}
|
}
|
||||||
Value::Filesize { val, .. } => {
|
Value::Filesize { val, .. } => {
|
||||||
write!(f, "Filesize({val:?})")
|
write!(f, "Filesize({:?})", val)
|
||||||
}
|
}
|
||||||
Value::Duration { val, .. } => {
|
Value::Duration { val, .. } => {
|
||||||
let duration = std::time::Duration::from_nanos(*val as u64);
|
let duration = std::time::Duration::from_nanos(*val as u64);
|
||||||
write!(f, "Duration({duration:?})")
|
write!(f, "Duration({:?})", duration)
|
||||||
}
|
}
|
||||||
Value::Date { val, .. } => {
|
Value::Date { val, .. } => {
|
||||||
write!(f, "Date({val:?})")
|
write!(f, "Date({:?})", val)
|
||||||
}
|
}
|
||||||
Value::Range { val, .. } => match **val {
|
Value::Range { val, .. } => match **val {
|
||||||
Range::IntRange(range) => match range.end() {
|
Range::IntRange(range) => match range.end() {
|
||||||
@ -280,7 +280,7 @@ impl std::fmt::Debug for DebuggableValue<'_> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Value::String { val, .. } | Value::Glob { val, .. } => {
|
Value::String { val, .. } | Value::Glob { val, .. } => {
|
||||||
write!(f, "{val:?}")
|
write!(f, "{:?}", val)
|
||||||
}
|
}
|
||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
write!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
@ -305,22 +305,22 @@ impl std::fmt::Debug for DebuggableValue<'_> {
|
|||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
write!(f, "Closure({val:?})")
|
write!(f, "Closure({:?})", val)
|
||||||
}
|
}
|
||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
write!(f, "Nothing")
|
write!(f, "Nothing")
|
||||||
}
|
}
|
||||||
Value::Error { error, .. } => {
|
Value::Error { error, .. } => {
|
||||||
write!(f, "Error({error:?})")
|
write!(f, "Error({:?})", error)
|
||||||
}
|
}
|
||||||
Value::Binary { val, .. } => {
|
Value::Binary { val, .. } => {
|
||||||
write!(f, "Binary({val:?})")
|
write!(f, "Binary({:?})", val)
|
||||||
}
|
}
|
||||||
Value::CellPath { val, .. } => {
|
Value::CellPath { val, .. } => {
|
||||||
write!(f, "CellPath({:?})", val.to_string())
|
write!(f, "CellPath({:?})", val.to_string())
|
||||||
}
|
}
|
||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
write!(f, "CustomValue({val:?})")
|
write!(f, "CustomValue({:?})", val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
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.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
# 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,10 +13,10 @@ version = "0.106.1"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1" }
|
nu-engine = { path = "../nu-engine", version = "0.105.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.106.1" }
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["plugin"] }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", features = ["plugin"] }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1" }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.105.2" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
@ -14,12 +14,12 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-json = { path = "../nu-json", version = "0.106.1" }
|
nu-json = { path = "../nu-json", version = "0.105.2" }
|
||||||
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.106.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
|
@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
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.106.1"
|
version = "0.105.2"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -16,22 +16,21 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.105.2" }
|
||||||
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
|
nu-glob = { path = "../nu-glob", version = "0.105.2" }
|
||||||
|
nu-json = { path = "../nu-json", version = "0.105.2" }
|
||||||
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.105.2" }
|
||||||
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
|
nu-system = { path = "../nu-system", version = "0.105.2" }
|
||||||
|
nu-table = { path = "../nu-table", version = "0.105.2" }
|
||||||
|
nu-term-grid = { path = "../nu-term-grid", version = "0.105.2" }
|
||||||
|
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
|
nuon = { path = "../nuon", version = "0.105.2" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
|
|
||||||
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
|
|
||||||
nu-glob = { path = "../nu-glob", version = "0.106.1" }
|
|
||||||
nu-json = { path = "../nu-json", version = "0.106.1" }
|
|
||||||
nu-parser = { path = "../nu-parser", version = "0.106.1" }
|
|
||||||
nu-path = { path = "../nu-path", version = "0.106.1" }
|
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.106.1" }
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
|
|
||||||
nu-system = { path = "../nu-system", version = "0.106.1" }
|
|
||||||
nu-table = { path = "../nu-table", version = "0.106.1" }
|
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.106.1" }
|
|
||||||
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
|
|
||||||
nuon = { path = "../nuon", version = "0.106.1" }
|
|
||||||
|
|
||||||
alphanumeric-sort = { workspace = true }
|
alphanumeric-sort = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
@ -227,8 +226,8 @@ sqlite = ["rusqlite"]
|
|||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
|
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
mockito = { workspace = true, default-features = false }
|
mockito = { workspace = true, default-features = false }
|
||||||
|
@ -89,7 +89,7 @@ impl Command for Histogram {
|
|||||||
"frequency-column-name can't be {}",
|
"frequency-column-name can't be {}",
|
||||||
forbidden_column_names
|
forbidden_column_names
|
||||||
.iter()
|
.iter()
|
||||||
.map(|val| format!("'{val}'"))
|
.map(|val| format!("'{}'", val))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
|
@ -142,7 +142,7 @@ fn into_binary(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||||
let value = match input {
|
let value = match input {
|
||||||
Value::Binary { .. } => input.clone(),
|
Value::Binary { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
|
||||||
@ -168,7 +168,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.compact {
|
if _args.compact {
|
||||||
let val_span = value.span();
|
let val_span = value.span();
|
||||||
if let Value::Binary { val, .. } = value {
|
if let Value::Binary { val, .. } = value {
|
||||||
let val = if cfg!(target_endian = "little") {
|
let val = if cfg!(target_endian = "little") {
|
||||||
|
@ -678,7 +678,7 @@ fn parse_value_from_record_as_u32(
|
|||||||
Value::Int { val, .. } => {
|
Value::Int { val, .. } => {
|
||||||
if *val < 0 || *val > u32::MAX as i64 {
|
if *val < 0 || *val > u32::MAX as i64 {
|
||||||
return Err(ShellError::IncorrectValue {
|
return Err(ShellError::IncorrectValue {
|
||||||
msg: format!("incorrect value for {col}"),
|
msg: format!("incorrect value for {}", col),
|
||||||
val_span: *head,
|
val_span: *head,
|
||||||
call_span: *span,
|
call_span: *span,
|
||||||
});
|
});
|
||||||
|
@ -368,7 +368,8 @@ fn merge_record(record: &Record, head: Span, span: Span) -> Result<Value, ShellE
|
|||||||
if !ALLOWED_SIGNS.contains(&val.as_str()) {
|
if !ALLOWED_SIGNS.contains(&val.as_str()) {
|
||||||
let allowed_signs = ALLOWED_SIGNS.join(", ");
|
let allowed_signs = ALLOWED_SIGNS.join(", ");
|
||||||
return Err(ShellError::IncorrectValue {
|
return Err(ShellError::IncorrectValue {
|
||||||
msg: format!("Invalid sign. Allowed signs are {allowed_signs}").to_string(),
|
msg: format!("Invalid sign. Allowed signs are {}", allowed_signs)
|
||||||
|
.to_string(),
|
||||||
val_span: sign.span(),
|
val_span: sign.span(),
|
||||||
call_span: head,
|
call_span: head,
|
||||||
});
|
});
|
||||||
|
@ -122,8 +122,8 @@ impl Table {
|
|||||||
.conn
|
.conn
|
||||||
.query_row(&table_exists_query, [], |row| row.get(0))
|
.query_row(&table_exists_query, [], |row| row.get(0))
|
||||||
.map_err(|err| ShellError::GenericError {
|
.map_err(|err| ShellError::GenericError {
|
||||||
error: format!("{err:#?}"),
|
error: format!("{:#?}", err),
|
||||||
msg: format!("{err:#?}"),
|
msg: format!("{:#?}", err),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: Vec::new(),
|
inner: Vec::new(),
|
||||||
@ -241,7 +241,7 @@ fn insert_in_transaction(
|
|||||||
let tx = table.try_init(&first_val)?;
|
let tx = table.try_init(&first_val)?;
|
||||||
|
|
||||||
for stream_value in stream {
|
for stream_value in stream {
|
||||||
if let Err(err) = signals.check(&span) {
|
if let Err(err) = signals.check(span) {
|
||||||
tx.rollback().map_err(|e| ShellError::GenericError {
|
tx.rollback().map_err(|e| ShellError::GenericError {
|
||||||
error: "Failed to rollback SQLite transaction".into(),
|
error: "Failed to rollback SQLite transaction".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
@ -257,7 +257,7 @@ fn insert_in_transaction(
|
|||||||
let insert_statement = format!(
|
let insert_statement = format!(
|
||||||
"INSERT INTO [{}] ({}) VALUES ({})",
|
"INSERT INTO [{}] ({}) VALUES ({})",
|
||||||
table_name,
|
table_name,
|
||||||
Itertools::intersperse(val.columns().map(|c| format!("`{c}`")), ", ".to_string())
|
Itertools::intersperse(val.columns().map(|c| format!("`{}`", c)), ", ".to_string())
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
Itertools::intersperse(itertools::repeat_n("?", val.len()), ", ").collect::<String>(),
|
Itertools::intersperse(itertools::repeat_n("?", val.len()), ", ").collect::<String>(),
|
||||||
);
|
);
|
||||||
@ -353,7 +353,6 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> {
|
|||||||
|
|
||||||
// intentionally enumerated so that any future types get handled
|
// intentionally enumerated so that any future types get handled
|
||||||
Type::Any
|
Type::Any
|
||||||
| Type::Block
|
|
||||||
| Type::CellPath
|
| Type::CellPath
|
||||||
| Type::Closure
|
| Type::Closure
|
||||||
| Type::Custom(_)
|
| Type::Custom(_)
|
||||||
@ -382,7 +381,7 @@ fn get_columns_with_sqlite_types(
|
|||||||
.map(|name| (format!("`{}`", name.0), name.1))
|
.map(|name| (format!("`{}`", name.0), name.1))
|
||||||
.any(|(name, _)| name == *c)
|
.any(|(name, _)| name == *c)
|
||||||
{
|
{
|
||||||
columns.push((format!("`{c}`"), nu_value_to_sqlite_type(v)?));
|
columns.push((format!("`{}`", c), nu_value_to_sqlite_type(v)?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,31 +112,16 @@ impl SQLiteDatabase {
|
|||||||
if self.path == PathBuf::from(MEMORY_DB) {
|
if self.path == PathBuf::from(MEMORY_DB) {
|
||||||
open_connection_in_memory_custom()
|
open_connection_in_memory_custom()
|
||||||
} else {
|
} else {
|
||||||
let conn = Connection::open(&self.path).map_err(|e| ShellError::GenericError {
|
Connection::open(&self.path).map_err(|e| ShellError::GenericError {
|
||||||
error: "Failed to open SQLite database from open_connection".into(),
|
error: "Failed to open SQLite database from open_connection".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})?;
|
})
|
||||||
conn.busy_handler(Some(SQLiteDatabase::sleeper))
|
|
||||||
.map_err(|e| ShellError::GenericError {
|
|
||||||
error: "Failed to set busy handler for SQLite database".into(),
|
|
||||||
msg: e.to_string(),
|
|
||||||
span: None,
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
})?;
|
|
||||||
Ok(conn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sleeper(attempts: i32) -> bool {
|
|
||||||
log::warn!("SQLITE_BUSY, retrying after 250ms (attempt {})", attempts);
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(250));
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, SqliteError> {
|
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, SqliteError> {
|
||||||
let mut table_names =
|
let mut table_names =
|
||||||
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
|
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
|
||||||
@ -173,7 +158,7 @@ impl SQLiteDatabase {
|
|||||||
filename: String,
|
filename: String,
|
||||||
) -> Result<(), SqliteError> {
|
) -> Result<(), SqliteError> {
|
||||||
//vacuum main into 'c:\\temp\\foo.db'
|
//vacuum main into 'c:\\temp\\foo.db'
|
||||||
conn.execute(&format!("vacuum main into '{filename}'"), [])?;
|
conn.execute(&format!("vacuum main into '{}'", filename), [])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -588,7 +573,7 @@ fn prepared_statement_to_nu_list(
|
|||||||
let mut row_values = vec![];
|
let mut row_values = vec![];
|
||||||
|
|
||||||
for row_result in row_results {
|
for row_result in row_results {
|
||||||
signals.check(&call_span)?;
|
signals.check(call_span)?;
|
||||||
if let Ok(row_value) = row_result {
|
if let Ok(row_value) = row_result {
|
||||||
row_values.push(row_value);
|
row_values.push(row_value);
|
||||||
}
|
}
|
||||||
@ -614,7 +599,7 @@ fn prepared_statement_to_nu_list(
|
|||||||
let mut row_values = vec![];
|
let mut row_values = vec![];
|
||||||
|
|
||||||
for row_result in row_results {
|
for row_result in row_results {
|
||||||
signals.check(&call_span)?;
|
signals.check(call_span)?;
|
||||||
if let Ok(row_value) = row_result {
|
if let Ok(row_value) = row_result {
|
||||||
row_values.push(row_value);
|
row_values.push(row_value);
|
||||||
}
|
}
|
||||||
@ -683,23 +668,13 @@ pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
|
|||||||
|
|
||||||
pub fn open_connection_in_memory_custom() -> Result<Connection, ShellError> {
|
pub fn open_connection_in_memory_custom() -> Result<Connection, ShellError> {
|
||||||
let flags = OpenFlags::default();
|
let flags = OpenFlags::default();
|
||||||
let conn =
|
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
|
||||||
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
|
error: "Failed to open SQLite custom connection in memory".into(),
|
||||||
error: "Failed to open SQLite custom connection in memory".into(),
|
msg: e.to_string(),
|
||||||
msg: e.to_string(),
|
span: Some(Span::test_data()),
|
||||||
span: Some(Span::test_data()),
|
help: None,
|
||||||
help: None,
|
inner: vec![],
|
||||||
inner: vec![],
|
})
|
||||||
})?;
|
|
||||||
conn.busy_handler(Some(SQLiteDatabase::sleeper))
|
|
||||||
.map_err(|e| ShellError::GenericError {
|
|
||||||
error: "Failed to set busy handler for SQLite custom connection in memory".into(),
|
|
||||||
msg: e.to_string(),
|
|
||||||
span: Some(Span::test_data()),
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
})?;
|
|
||||||
Ok(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
use nu_engine::command_prelude::*;
|
|
||||||
use nu_experimental::Status;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DebugExperimentalOptions;
|
|
||||||
|
|
||||||
impl Command for DebugExperimentalOptions {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"debug experimental-options"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::new(self.name())
|
|
||||||
.input_output_type(
|
|
||||||
Type::Nothing,
|
|
||||||
Type::Table(Box::from([
|
|
||||||
(String::from("identifier"), Type::String),
|
|
||||||
(String::from("enabled"), Type::Bool),
|
|
||||||
(String::from("status"), Type::String),
|
|
||||||
(String::from("description"), Type::String),
|
|
||||||
])),
|
|
||||||
)
|
|
||||||
.add_help()
|
|
||||||
.category(Category::Debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
"Show all experimental options."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(PipelineData::Value(
|
|
||||||
Value::list(
|
|
||||||
nu_experimental::ALL
|
|
||||||
.iter()
|
|
||||||
.map(|option| {
|
|
||||||
Value::record(
|
|
||||||
nu_protocol::record! {
|
|
||||||
"identifier" => Value::string(option.identifier(), call.head),
|
|
||||||
"enabled" => Value::bool(option.get(), call.head),
|
|
||||||
"status" => Value::string(match option.status() {
|
|
||||||
Status::OptIn => "opt-in",
|
|
||||||
Status::OptOut => "opt-out",
|
|
||||||
Status::DeprecatedDiscard => "deprecated-discard",
|
|
||||||
Status::DeprecatedDefault => "deprecated-default"
|
|
||||||
}, call.head),
|
|
||||||
"description" => Value::string(option.description(), call.head),
|
|
||||||
},
|
|
||||||
call.head,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
call.head,
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -43,17 +43,6 @@ impl Command for Metadata {
|
|||||||
let arg = call.positional_nth(stack, 0);
|
let arg = call.positional_nth(stack, 0);
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
if !matches!(input, PipelineData::Empty) {
|
|
||||||
if let Some(arg_expr) = arg {
|
|
||||||
return Err(ShellError::IncompatibleParameters {
|
|
||||||
left_message: "pipeline input was provided".into(),
|
|
||||||
left_span: head,
|
|
||||||
right_message: "but a positional metadata expression was also given".into(),
|
|
||||||
right_span: arg_expr.span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match arg {
|
match arg {
|
||||||
Some(Expression {
|
Some(Expression {
|
||||||
expr: Expr::FullCellPath(full_cell_path),
|
expr: Expr::FullCellPath(full_cell_path),
|
||||||
@ -67,6 +56,7 @@ impl Command for Metadata {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let origin = stack.get_var_with_origin(*var_id, *span)?;
|
let origin = stack.get_var_with_origin(*var_id, *span)?;
|
||||||
|
|
||||||
Ok(build_metadata_record_value(
|
Ok(build_metadata_record_value(
|
||||||
&origin,
|
&origin,
|
||||||
input.metadata().as_ref(),
|
input.metadata().as_ref(),
|
||||||
@ -97,9 +87,10 @@ impl Command for Metadata {
|
|||||||
.into_pipeline_data(),
|
.into_pipeline_data(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
None => {
|
None => Ok(
|
||||||
Ok(Value::record(build_metadata_record(&input, head), head).into_pipeline_data())
|
Value::record(build_metadata_record(input.metadata().as_ref(), head), head)
|
||||||
}
|
.into_pipeline_data(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +116,19 @@ fn build_metadata_record_value(
|
|||||||
head: Span,
|
head: Span,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
let mut record = Record::new();
|
let mut record = Record::new();
|
||||||
record.push("span", arg.span().into_value(head));
|
|
||||||
|
let span = arg.span();
|
||||||
|
record.push(
|
||||||
|
"span",
|
||||||
|
Value::record(
|
||||||
|
record! {
|
||||||
|
"start" => Value::int(span.start as i64,span),
|
||||||
|
"end" => Value::int(span.end as i64, span),
|
||||||
|
},
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
Value::record(extend_record_with_metadata(record, metadata, head), head)
|
Value::record(extend_record_with_metadata(record, metadata, head), head)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,10 @@ impl Command for MetadataAccess {
|
|||||||
// `ClosureEvalOnce` is not used as it uses `Stack::captures_to_stack` rather than
|
// `ClosureEvalOnce` is not used as it uses `Stack::captures_to_stack` rather than
|
||||||
// `Stack::captures_to_stack_preserve_out_dest`. This command shouldn't collect streams
|
// `Stack::captures_to_stack_preserve_out_dest`. This command shouldn't collect streams
|
||||||
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(closure.captures);
|
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(closure.captures);
|
||||||
let metadata_record = Value::record(build_metadata_record(&input, call.head), call.head);
|
let metadata_record = Value::record(
|
||||||
|
build_metadata_record(input.metadata().as_ref(), call.head),
|
||||||
|
call.head,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(var_id) = block.signature.get_positional(0).and_then(|var| var.var_id) {
|
if let Some(var_id) = block.signature.get_positional(0).and_then(|var| var.var_id) {
|
||||||
callee_stack.add_var(var_id, metadata_record)
|
callee_stack.add_var(var_id, metadata_record)
|
||||||
@ -55,10 +58,12 @@ impl Command for MetadataAccess {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Access metadata and data from a stream together",
|
description: "Access metadata and data from a stream together",
|
||||||
example: r#"{foo: bar} | to json --raw | metadata access {|meta| {in: $in, content: $meta.content_type}}"#,
|
example: r#"{foo: bar} | to json --raw | metadata access {|meta| {in: $in, meta: $meta}}"#,
|
||||||
result: Some(Value::test_record(record! {
|
result: Some(Value::test_record(record! {
|
||||||
"in" => Value::test_string(r#"{"foo":"bar"}"#),
|
"in" => Value::test_string(r#"{"foo":"bar"}"#),
|
||||||
"content" => Value::test_string(r#"application/json"#)
|
"meta" => Value::test_record(record! {
|
||||||
|
"content_type" => Value::test_string(r#"application/json"#)
|
||||||
|
})
|
||||||
})),
|
})),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -63,18 +63,7 @@ impl Command for MetadataSet {
|
|||||||
match (ds_fp, ds_ls) {
|
match (ds_fp, ds_ls) {
|
||||||
(Some(path), false) => metadata.data_source = DataSource::FilePath(path.into()),
|
(Some(path), false) => metadata.data_source = DataSource::FilePath(path.into()),
|
||||||
(None, true) => metadata.data_source = DataSource::Ls,
|
(None, true) => metadata.data_source = DataSource::Ls,
|
||||||
(Some(_), true) => {
|
(Some(_), true) => (), // TODO: error here
|
||||||
return Err(ShellError::IncompatibleParameters {
|
|
||||||
left_message: "cannot use `--datasource-filepath`".into(),
|
|
||||||
left_span: call
|
|
||||||
.get_flag_span(stack, "datasource-filepath")
|
|
||||||
.expect("has flag"),
|
|
||||||
right_message: "with `--datasource-ls`".into(),
|
|
||||||
right_span: call
|
|
||||||
.get_flag_span(stack, "datasource-ls")
|
|
||||||
.expect("has flag"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
(None, false) => (),
|
(None, false) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,13 +79,15 @@ 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 --datasource-filepath $'(pwd)/crates'",
|
example: "'crates' | metadata set --datasource-filepath $'(pwd)/crates' | metadata",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
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 | get content_type",
|
example: "'crates' | metadata set --content-type text/plain | metadata",
|
||||||
result: Some(Value::test_string("text/plain")),
|
result: Some(Value::test_record(record! {
|
||||||
|
"content_type" => Value::test_string("text/plain"),
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod ast;
|
mod ast;
|
||||||
mod debug_;
|
mod debug_;
|
||||||
mod env;
|
mod env;
|
||||||
mod experimental_options;
|
|
||||||
mod explain;
|
mod explain;
|
||||||
mod info;
|
mod info;
|
||||||
mod inspect;
|
mod inspect;
|
||||||
@ -22,7 +21,6 @@ mod view_span;
|
|||||||
pub use ast::Ast;
|
pub use ast::Ast;
|
||||||
pub use debug_::Debug;
|
pub use debug_::Debug;
|
||||||
pub use env::DebugEnv;
|
pub use env::DebugEnv;
|
||||||
pub use experimental_options::DebugExperimentalOptions;
|
|
||||||
pub use explain::Explain;
|
pub use explain::Explain;
|
||||||
pub use info::DebugInfo;
|
pub use info::DebugInfo;
|
||||||
pub use inspect::Inspect;
|
pub use inspect::Inspect;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_protocol::{DataSource, IntoValue, PipelineData, PipelineMetadata, Record, Span, Value};
|
use nu_protocol::{DataSource, PipelineMetadata, Record, Span, Value};
|
||||||
|
|
||||||
pub fn extend_record_with_metadata(
|
pub fn extend_record_with_metadata(
|
||||||
mut record: Record,
|
mut record: Record,
|
||||||
@ -29,10 +29,6 @@ pub fn extend_record_with_metadata(
|
|||||||
record
|
record
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_metadata_record(pipeline: &PipelineData, head: Span) -> Record {
|
pub fn build_metadata_record(metadata: Option<&PipelineMetadata>, head: Span) -> Record {
|
||||||
let mut record = Record::new();
|
extend_record_with_metadata(Record::new(), metadata, head)
|
||||||
if let Some(span) = pipeline.span() {
|
|
||||||
record.insert("span", span.into_value(head));
|
|
||||||
}
|
|
||||||
extend_record_with_metadata(record, pipeline.metadata().as_ref(), head)
|
|
||||||
}
|
}
|
||||||
|
@ -126,10 +126,10 @@ impl Command for ViewSource {
|
|||||||
}
|
}
|
||||||
let _ = write!(&mut final_contents, "--{}", n.long);
|
let _ = write!(&mut final_contents, "--{}", n.long);
|
||||||
if let Some(short) = n.short {
|
if let Some(short) = n.short {
|
||||||
let _ = write!(&mut final_contents, "(-{short})");
|
let _ = write!(&mut final_contents, "(-{})", short);
|
||||||
}
|
}
|
||||||
if let Some(arg) = &n.arg {
|
if let Some(arg) = &n.arg {
|
||||||
let _ = write!(&mut final_contents, ": {arg}");
|
let _ = write!(&mut final_contents, ": {}", arg);
|
||||||
}
|
}
|
||||||
final_contents.push(' ');
|
final_contents.push(' ');
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ impl Command for ViewSource {
|
|||||||
let mut c = 0;
|
let mut c = 0;
|
||||||
for (insig, outsig) in type_signatures {
|
for (insig, outsig) in type_signatures {
|
||||||
c += 1;
|
c += 1;
|
||||||
let s = format!("{insig} -> {outsig}");
|
let s = format!("{} -> {}", insig, outsig);
|
||||||
final_contents.push_str(&s);
|
final_contents.push_str(&s);
|
||||||
if c != len {
|
if c != len {
|
||||||
final_contents.push_str(", ")
|
final_contents.push_str(", ")
|
||||||
|
@ -153,7 +153,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||||||
Ast,
|
Ast,
|
||||||
Debug,
|
Debug,
|
||||||
DebugEnv,
|
DebugEnv,
|
||||||
DebugExperimentalOptions,
|
|
||||||
DebugInfo,
|
DebugInfo,
|
||||||
DebugProfile,
|
DebugProfile,
|
||||||
Explain,
|
Explain,
|
||||||
@ -189,9 +188,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||||||
|
|
||||||
// Strings
|
// Strings
|
||||||
bind_command! {
|
bind_command! {
|
||||||
Ansi,
|
|
||||||
AnsiLink,
|
|
||||||
AnsiStrip,
|
|
||||||
Char,
|
Char,
|
||||||
Decode,
|
Decode,
|
||||||
Encode,
|
Encode,
|
||||||
@ -254,6 +250,9 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||||||
// Platform
|
// Platform
|
||||||
#[cfg(feature = "os")]
|
#[cfg(feature = "os")]
|
||||||
bind_command! {
|
bind_command! {
|
||||||
|
Ansi,
|
||||||
|
AnsiLink,
|
||||||
|
AnsiStrip,
|
||||||
Clear,
|
Clear,
|
||||||
Du,
|
Du,
|
||||||
Input,
|
Input,
|
||||||
|
@ -85,7 +85,7 @@ impl Command for Glob {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Search for files or folders that do not begin with c, C, b, M, or s",
|
description: "Search for files for folders that do not begin with c, C, b, M, or s",
|
||||||
example: r#"glob "[!cCbMs]*""#,
|
example: r#"glob "[!cCbMs]*""#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -329,7 +329,7 @@ fn glob_to_value(
|
|||||||
) -> ListStream {
|
) -> ListStream {
|
||||||
let map_signals = signals.clone();
|
let map_signals = signals.clone();
|
||||||
let result = glob_results.filter_map(move |entry| {
|
let result = glob_results.filter_map(move |entry| {
|
||||||
if let Err(err) = map_signals.check(&span) {
|
if let Err(err) = map_signals.check(span) {
|
||||||
return Some(Value::error(err, span));
|
return Some(Value::error(err, span));
|
||||||
};
|
};
|
||||||
let file_type = entry.file_type();
|
let file_type = entry.file_type();
|
||||||
|
@ -341,7 +341,7 @@ fn ls_for_one_pattern(
|
|||||||
|
|
||||||
let mut paths_peek = paths.peekable();
|
let mut paths_peek = paths.peekable();
|
||||||
let no_matches = paths_peek.peek().is_none();
|
let no_matches = paths_peek.peek().is_none();
|
||||||
signals.check(&call_span)?;
|
signals.check(call_span)?;
|
||||||
if no_matches {
|
if no_matches {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("No matches found for {:?}", path.item),
|
error: format!("No matches found for {:?}", path.item),
|
||||||
@ -979,14 +979,14 @@ fn read_dir(
|
|||||||
.read_dir()
|
.read_dir()
|
||||||
.map_err(|err| IoError::new(err, span, f.clone()))?
|
.map_err(|err| IoError::new(err, span, f.clone()))?
|
||||||
.map(move |d| {
|
.map(move |d| {
|
||||||
signals_clone.check(&span)?;
|
signals_clone.check(span)?;
|
||||||
d.map(|r| r.path())
|
d.map(|r| r.path())
|
||||||
.map_err(|err| IoError::new(err, span, f.clone()))
|
.map_err(|err| IoError::new(err, span, f.clone()))
|
||||||
.map_err(ShellError::from)
|
.map_err(ShellError::from)
|
||||||
});
|
});
|
||||||
if !use_threads {
|
if !use_threads {
|
||||||
let mut collected = items.collect::<Vec<_>>();
|
let mut collected = items.collect::<Vec<_>>();
|
||||||
signals.check(&span)?;
|
signals.check(span)?;
|
||||||
collected.sort_by(|a, b| match (a, b) {
|
collected.sort_by(|a, b| match (a, b) {
|
||||||
(Ok(a), Ok(b)) => a.cmp(b),
|
(Ok(a), Ok(b)) => a.cmp(b),
|
||||||
(Ok(_), Err(_)) => Ordering::Greater,
|
(Ok(_), Err(_)) => Ordering::Greater,
|
||||||
|
@ -112,8 +112,8 @@ impl Command for Mktemp {
|
|||||||
.map_err(|_| ShellError::NonUtf8 { span })?,
|
.map_err(|_| ShellError::NonUtf8 { span })?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("{e}"),
|
error: format!("{}", e),
|
||||||
msg: format!("{e}"),
|
msg: format!("{}", e),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
|
@ -198,7 +198,7 @@ impl Command for Open {
|
|||||||
let converter = exts_opt.and_then(|exts| {
|
let converter = exts_opt.and_then(|exts| {
|
||||||
exts.iter().find_map(|ext| {
|
exts.iter().find_map(|ext| {
|
||||||
engine_state
|
engine_state
|
||||||
.find_decl(format!("from {ext}").as_bytes(), &[])
|
.find_decl(format!("from {}", ext).as_bytes(), &[])
|
||||||
.map(|id| (id, ext.to_string()))
|
.map(|id| (id, ext.to_string()))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -314,7 +314,7 @@ fn extract_extensions(filename: &str) -> Vec<String> {
|
|||||||
if current_extension.is_empty() {
|
if current_extension.is_empty() {
|
||||||
current_extension.push_str(part);
|
current_extension.push_str(part);
|
||||||
} else {
|
} else {
|
||||||
current_extension = format!("{part}.{current_extension}");
|
current_extension = format!("{}.{}", part, current_extension);
|
||||||
}
|
}
|
||||||
extensions.push(current_extension.clone());
|
extensions.push(current_extension.clone());
|
||||||
}
|
}
|
||||||
|
@ -454,7 +454,7 @@ fn rm(
|
|||||||
});
|
});
|
||||||
|
|
||||||
for result in iter {
|
for result in iter {
|
||||||
engine_state.signals().check(&call.head)?;
|
engine_state.signals().check(call.head)?;
|
||||||
match result {
|
match result {
|
||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Ok(Some(msg)) => eprintln!("{msg}"),
|
Ok(Some(msg)) => eprintln!("{msg}"),
|
||||||
|
@ -91,8 +91,7 @@ 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) =
|
let (file, stderr_file) = get_files(&path, stderr_path.as_ref(), append, force)?;
|
||||||
get_files(engine_state, &path, stderr_path.as_ref(), append, force)?;
|
|
||||||
|
|
||||||
let size = stream.known_size();
|
let size = stream.known_size();
|
||||||
let signals = engine_state.signals();
|
let signals = engine_state.signals();
|
||||||
@ -202,8 +201,7 @@ impl Command for Save {
|
|||||||
stderr_path.as_ref(),
|
stderr_path.as_ref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (mut file, _) =
|
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
|
||||||
get_files(engine_state, &path, stderr_path.as_ref(), 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(&from_io_error)?;
|
.map_err(&from_io_error)?;
|
||||||
@ -228,8 +226,7 @@ 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, _) =
|
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
|
||||||
get_files(engine_state, &path, stderr_path.as_ref(), append, force)?;
|
|
||||||
|
|
||||||
file.write_all(&bytes).map_err(&from_io_error)?;
|
file.write_all(&bytes).map_err(&from_io_error)?;
|
||||||
file.flush().map_err(&from_io_error)?;
|
file.flush().map_err(&from_io_error)?;
|
||||||
@ -425,14 +422,13 @@ fn prepare_path(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_file(
|
fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError> {
|
||||||
engine_state: &EngineState,
|
let file: Result<File, nu_protocol::shell_error::io::ErrorKind> = match (append, path.exists())
|
||||||
path: &Path,
|
{
|
||||||
span: Span,
|
(true, true) => std::fs::OpenOptions::new()
|
||||||
append: bool,
|
.append(true)
|
||||||
) -> Result<File, ShellError> {
|
.open(path)
|
||||||
let file: std::io::Result<File> = match (append, path.exists()) {
|
.map_err(|err| err.into()),
|
||||||
(true, true) => std::fs::OpenOptions::new().append(true).open(path),
|
|
||||||
_ => {
|
_ => {
|
||||||
// This is a temporary solution until `std::fs::File::create` is fixed on Windows (rust-lang/rust#134893)
|
// This is a temporary solution until `std::fs::File::create` is fixed on Windows (rust-lang/rust#134893)
|
||||||
// A TOCTOU problem exists here, which may cause wrong error message to be shown
|
// A TOCTOU problem exists here, which may cause wrong error message to be shown
|
||||||
@ -442,51 +438,22 @@ fn open_file(
|
|||||||
deprecated,
|
deprecated,
|
||||||
reason = "we don't get a IsADirectory error, so we need to provide it"
|
reason = "we don't get a IsADirectory error, so we need to provide it"
|
||||||
)]
|
)]
|
||||||
Err(std::io::ErrorKind::IsADirectory.into())
|
Err(nu_protocol::shell_error::io::ErrorKind::from_std(
|
||||||
|
std::io::ErrorKind::IsADirectory,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
std::fs::File::create(path)
|
std::fs::File::create(path).map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
std::fs::File::create(path)
|
std::fs::File::create(path).map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match file {
|
file.map_err(|err_kind| ShellError::Io(IoError::new(err_kind, span, PathBuf::from(path))))
|
||||||
Ok(file) => Ok(file),
|
|
||||||
Err(err) => {
|
|
||||||
// In caase of NotFound, search for the missing parent directory.
|
|
||||||
// This also presents a TOCTOU (or TOUTOC, technically?)
|
|
||||||
if err.kind() == std::io::ErrorKind::NotFound {
|
|
||||||
if let Some(missing_component) =
|
|
||||||
path.ancestors().skip(1).filter(|dir| !dir.exists()).last()
|
|
||||||
{
|
|
||||||
// By looking at the postfix to remove, rather than the prefix
|
|
||||||
// to keep, we are able to handle relative paths too.
|
|
||||||
let components_to_remove = path
|
|
||||||
.strip_prefix(missing_component)
|
|
||||||
.expect("Stripping ancestor from a path should never fail")
|
|
||||||
.as_os_str()
|
|
||||||
.as_encoded_bytes();
|
|
||||||
|
|
||||||
return Err(ShellError::Io(IoError::new(
|
|
||||||
ErrorKind::DirectoryNotFound,
|
|
||||||
engine_state
|
|
||||||
.span_match_postfix(span, components_to_remove)
|
|
||||||
.map(|(pre, _post)| pre)
|
|
||||||
.unwrap_or(span),
|
|
||||||
PathBuf::from(missing_component),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(ShellError::Io(IoError::new(err, span, PathBuf::from(path))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get output file and optional stderr file
|
/// Get output file and optional stderr file
|
||||||
fn get_files(
|
fn get_files(
|
||||||
engine_state: &EngineState,
|
|
||||||
path: &Spanned<PathBuf>,
|
path: &Spanned<PathBuf>,
|
||||||
stderr_path: Option<&Spanned<PathBuf>>,
|
stderr_path: Option<&Spanned<PathBuf>>,
|
||||||
append: bool,
|
append: bool,
|
||||||
@ -500,7 +467,7 @@ fn get_files(
|
|||||||
.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(engine_state, path, path_span, append)?;
|
let file = open_file(path, path_span, 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)| {
|
||||||
@ -513,7 +480,7 @@ fn get_files(
|
|||||||
inner: vec![],
|
inner: vec![],
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
open_file(engine_state, stderr_path, stderr_path_span, append)
|
open_file(stderr_path, stderr_path_span, append)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
@ -543,7 +510,7 @@ fn stream_to_file(
|
|||||||
let mut reader = BufReader::new(source);
|
let mut reader = BufReader::new(source);
|
||||||
|
|
||||||
let res = loop {
|
let res = loop {
|
||||||
if let Err(err) = signals.check(&span) {
|
if let Err(err) = signals.check(span) {
|
||||||
bar.abandoned_msg("# Cancelled #".to_owned());
|
bar.abandoned_msg("# Cancelled #".to_owned());
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,7 @@ impl Command for Start {
|
|||||||
}
|
}
|
||||||
// If it's not a URL, treat it as a file path
|
// If it's not a URL, treat it as a file path
|
||||||
let cwd = engine_state.cwd(Some(stack))?;
|
let cwd = engine_state.cwd(Some(stack))?;
|
||||||
let full_path = nu_path::expand_path_with(path_no_whitespace, &cwd, true);
|
let full_path = cwd.join(path_no_whitespace);
|
||||||
|
|
||||||
// Check if the path exists or if it's a valid file/directory
|
// Check if the path exists or if it's a valid file/directory
|
||||||
if full_path.exists() {
|
if full_path.exists() {
|
||||||
open_path(full_path, engine_state, stack, path.span)?;
|
open_path(full_path, engine_state, stack, path.span)?;
|
||||||
|
@ -272,8 +272,8 @@ impl Command for UCp {
|
|||||||
uu_cp::Error::NotAllFilesCopied => {}
|
uu_cp::Error::NotAllFilesCopied => {}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("{error}"),
|
error: format!("{}", error),
|
||||||
msg: format!("{error}"),
|
msg: format!("{}", error),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
@ -373,7 +373,7 @@ fn parse_and_set_attribute(
|
|||||||
"xattr" => &mut attribute.xattr,
|
"xattr" => &mut attribute.xattr,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::IncompatibleParametersSingle {
|
return Err(ShellError::IncompatibleParametersSingle {
|
||||||
msg: format!("--preserve flag got an unexpected attribute \"{val}\""),
|
msg: format!("--preserve flag got an unexpected attribute \"{}\"", val),
|
||||||
span: value.span(),
|
span: value.span(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,8 @@ impl Command for UMkdir {
|
|||||||
for dir in directories {
|
for dir in directories {
|
||||||
if let Err(error) = mkdir(&dir, IS_RECURSIVE, get_mode(), is_verbose) {
|
if let Err(error) = mkdir(&dir, IS_RECURSIVE, get_mode(), is_verbose) {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("{error}"),
|
error: format!("{}", error),
|
||||||
msg: format!("{error}"),
|
msg: format!("{}", error),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
|
@ -195,8 +195,8 @@ impl Command for UMv {
|
|||||||
};
|
};
|
||||||
if let Err(error) = uu_mv::mv(&files, &options) {
|
if let Err(error) = uu_mv::mv(&files, &options) {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("{error}"),
|
error: format!("{}", error),
|
||||||
msg: format!("{error}"),
|
msg: format!("{}", error),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: Vec::new(),
|
inner: Vec::new(),
|
||||||
|
@ -220,7 +220,7 @@ impl Command for UTouch {
|
|||||||
inner: Vec::new(),
|
inner: Vec::new(),
|
||||||
},
|
},
|
||||||
TouchError::InvalidDateFormat(date) => ShellError::IncorrectValue {
|
TouchError::InvalidDateFormat(date) => ShellError::IncorrectValue {
|
||||||
msg: format!("Invalid date: {date}"),
|
msg: format!("Invalid date: {}", date),
|
||||||
val_span: date_span.expect("touch should've been given a date"),
|
val_span: date_span.expect("touch should've been given a date"),
|
||||||
call_span: call.head,
|
call_span: call.head,
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,11 @@ use notify_debouncer_full::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use nu_engine::{ClosureEval, command_prelude::*};
|
use nu_engine::{ClosureEval, command_prelude::*};
|
||||||
use nu_protocol::{engine::Closure, report_shell_error, shell_error::io::IoError};
|
use nu_protocol::{
|
||||||
|
engine::{Closure, StateWorkingSet},
|
||||||
|
format_shell_error,
|
||||||
|
shell_error::io::IoError,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::mpsc::{RecvTimeoutError, channel},
|
sync::mpsc::{RecvTimeoutError, channel},
|
||||||
@ -199,9 +203,14 @@ impl Command for Watch {
|
|||||||
.run_with_input(PipelineData::Empty);
|
.run_with_input(PipelineData::Empty);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(val) => val.print_table(engine_state, stack, false, false)?,
|
Ok(val) => {
|
||||||
Err(err) => report_shell_error(engine_state, &err),
|
val.print_table(engine_state, stack, false, false)?;
|
||||||
};
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
eprintln!("{}", format_shell_error(&working_set, &err));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref};
|
|||||||
|
|
||||||
use nu_engine::{ClosureEval, command_prelude::*};
|
use nu_engine::{ClosureEval, command_prelude::*};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ListStream, ReportMode, ShellWarning, Signals,
|
ListStream, Signals,
|
||||||
ast::{Expr, Expression},
|
ast::{Expr, Expression},
|
||||||
report_shell_warning,
|
report_shell_warning,
|
||||||
};
|
};
|
||||||
@ -82,7 +82,7 @@ impl Command for Default {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Get the env value of `MY_ENV` with a default value 'abc' if not present",
|
description: "Get the env value of `MY_ENV` with a default value 'abc' if not present",
|
||||||
example: "$env | get --optional MY_ENV | default 'abc'",
|
example: "$env | get --ignore-errors MY_ENV | default 'abc'",
|
||||||
result: Some(Value::test_string("abc")),
|
result: Some(Value::test_string("abc")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -213,7 +213,7 @@ fn default(
|
|||||||
|| (default_when_empty
|
|| (default_when_empty
|
||||||
&& matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
|
&& matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
|
||||||
{
|
{
|
||||||
default_value.single_run_pipeline_data()
|
default_value.pipeline_data()
|
||||||
} else if default_when_empty && matches!(input, PipelineData::ListStream(..)) {
|
} else if default_when_empty && matches!(input, PipelineData::ListStream(..)) {
|
||||||
let PipelineData::ListStream(ls, metadata) = input else {
|
let PipelineData::ListStream(ls, metadata) = input else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -221,7 +221,7 @@ fn default(
|
|||||||
let span = ls.span();
|
let span = ls.span();
|
||||||
let mut stream = ls.into_inner().peekable();
|
let mut stream = ls.into_inner().peekable();
|
||||||
if stream.peek().is_none() {
|
if stream.peek().is_none() {
|
||||||
return default_value.single_run_pipeline_data();
|
return default_value.pipeline_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stream's internal state already preserves the original signals config, so if this
|
// stream's internal state already preserves the original signals config, so if this
|
||||||
@ -278,14 +278,8 @@ impl DefaultValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used when we know the value won't need to be cached to allow streaming.
|
fn pipeline_data(&mut self) -> Result<PipelineData, ShellError> {
|
||||||
fn single_run_pipeline_data(self) -> Result<PipelineData, ShellError> {
|
self.value().map(|x| x.into_pipeline_data())
|
||||||
match self {
|
|
||||||
DefaultValue::Uncalculated(mut closure) => {
|
|
||||||
closure.item.run_with_input(PipelineData::Empty)
|
|
||||||
}
|
|
||||||
DefaultValue::Calculated(val) => Ok(val.into_pipeline_data()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,25 +323,24 @@ fn closure_variable_warning(
|
|||||||
(Value::Closure { .. }, true) => {
|
(Value::Closure { .. }, true) => {
|
||||||
let span_contents = String::from_utf8_lossy(engine_state.get_span_contents(span));
|
let span_contents = String::from_utf8_lossy(engine_state.get_span_contents(span));
|
||||||
let carapace_suggestion = "re-run carapace init with version v1.3.3 or later\nor, change this to `{ $carapace_completer }`";
|
let carapace_suggestion = "re-run carapace init with version v1.3.3 or later\nor, change this to `{ $carapace_completer }`";
|
||||||
let label = match span_contents {
|
let suggestion = match span_contents {
|
||||||
Cow::Borrowed("$carapace_completer") => carapace_suggestion.to_string(),
|
Cow::Borrowed("$carapace_completer") => carapace_suggestion.to_string(),
|
||||||
Cow::Owned(s) if s.deref() == "$carapace_completer" => {
|
Cow::Owned(s) if s.deref() == "$carapace_completer" => {
|
||||||
carapace_suggestion.to_string()
|
carapace_suggestion.to_string()
|
||||||
}
|
}
|
||||||
_ => format!("change this to {{ {span_contents} }}").to_string(),
|
_ => format!("change this to {{ {} }}", span_contents).to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
report_shell_warning(
|
report_shell_warning(
|
||||||
engine_state,
|
engine_state,
|
||||||
&ShellWarning::Deprecated {
|
&ShellError::DeprecationWarning {
|
||||||
dep_type: "Behavior".to_string(),
|
deprecation_type: "Behavior",
|
||||||
label,
|
suggestion,
|
||||||
span,
|
span,
|
||||||
help: Some(
|
help: Some(
|
||||||
r"Since 0.105.0, closure literals passed to default are lazily evaluated, rather than returned as a value.
|
r"Since 0.105.0, closure literals passed to default are lazily evaluated, rather than returned as a value.
|
||||||
In a future release, closures passed by variable will also be lazily evaluated.".to_string(),
|
In a future release, closures passed by variable will also be lazily evaluated.",
|
||||||
),
|
),
|
||||||
report_mode: ReportMode::FirstUse,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
use itertools::Either;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{PipelineIterator, Range};
|
use nu_protocol::{PipelineIterator, Range};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -18,11 +18,13 @@ impl Command for DropNth {
|
|||||||
(Type::list(Type::Any), Type::list(Type::Any)),
|
(Type::list(Type::Any), Type::list(Type::Any)),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.required(
|
||||||
"rest",
|
"row number or row range",
|
||||||
|
// FIXME: we can make this accept either Int or Range when we can compose SyntaxShapes
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"The row numbers or ranges to drop.",
|
"The number of the row to drop or a range to drop consecutive rows.",
|
||||||
)
|
)
|
||||||
|
.rest("rest", SyntaxShape::Any, "The number of the row to drop.")
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,125 +103,110 @@ impl Command for DropNth {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
let number_or_range = extract_int_or_range(engine_state, stack, call)?;
|
||||||
|
|
||||||
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let rows = match number_or_range.item {
|
||||||
if args.is_empty() {
|
Either::Left(row_number) => {
|
||||||
return Ok(input);
|
let and_rows: Vec<Spanned<i64>> = call.rest(engine_state, stack, 1)?;
|
||||||
}
|
let mut rows: Vec<_> = and_rows.into_iter().map(|x| x.item as usize).collect();
|
||||||
|
rows.push(row_number as usize);
|
||||||
|
rows.sort_unstable();
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
Either::Right(Range::FloatRange(_)) => {
|
||||||
|
return Err(ShellError::UnsupportedInput {
|
||||||
|
msg: "float range".into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: number_or_range.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Either::Right(Range::IntRange(range)) => {
|
||||||
|
// check for negative range inputs, e.g., (2..-5)
|
||||||
|
let end_negative = match range.end() {
|
||||||
|
Bound::Included(end) | Bound::Excluded(end) => end < 0,
|
||||||
|
Bound::Unbounded => false,
|
||||||
|
};
|
||||||
|
if range.start().is_negative() || end_negative {
|
||||||
|
return Err(ShellError::UnsupportedInput {
|
||||||
|
msg: "drop nth accepts only positive ints".into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: number_or_range.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if the upper bound is smaller than the lower bound, e.g., do not accept 4..2
|
||||||
|
if range.step() < 0 {
|
||||||
|
return Err(ShellError::UnsupportedInput {
|
||||||
|
msg: "The upper bound needs to be equal or larger to the lower bound"
|
||||||
|
.into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: number_or_range.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let (rows_to_drop, min_unbounded_start) = get_rows_to_drop(&args, head)?;
|
let start = range.start() as usize;
|
||||||
|
|
||||||
let input = if let Some(cutoff) = min_unbounded_start {
|
let end = match range.end() {
|
||||||
input
|
Bound::Included(end) => end as usize,
|
||||||
.into_iter()
|
Bound::Excluded(end) => (end - 1) as usize,
|
||||||
.take(cutoff)
|
Bound::Unbounded => {
|
||||||
.into_pipeline_data_with_metadata(
|
return Ok(input
|
||||||
head,
|
.into_iter()
|
||||||
engine_state.signals().clone(),
|
.take(start)
|
||||||
metadata.clone(),
|
.into_pipeline_data_with_metadata(
|
||||||
)
|
head,
|
||||||
} else {
|
engine_state.signals().clone(),
|
||||||
input
|
metadata,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = if let PipelineData::Value(Value::List { vals, .. }, _) = &input {
|
||||||
|
end.min(vals.len() - 1)
|
||||||
|
} else {
|
||||||
|
end
|
||||||
|
};
|
||||||
|
|
||||||
|
(start..=end).collect()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(DropNthIterator {
|
Ok(DropNthIterator {
|
||||||
input: input.into_iter(),
|
input: input.into_iter(),
|
||||||
rows: rows_to_drop,
|
rows,
|
||||||
current: 0,
|
current: 0,
|
||||||
}
|
}
|
||||||
.into_pipeline_data_with_metadata(head, engine_state.signals().clone(), metadata))
|
.into_pipeline_data_with_metadata(head, engine_state.signals().clone(), metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rows_to_drop(
|
fn extract_int_or_range(
|
||||||
args: &[Value],
|
engine_state: &EngineState,
|
||||||
head: Span,
|
stack: &mut Stack,
|
||||||
) -> Result<(VecDeque<usize>, Option<usize>), ShellError> {
|
call: &Call,
|
||||||
let mut rows_to_drop = Vec::new();
|
) -> Result<Spanned<Either<i64, Range>>, ShellError> {
|
||||||
let mut min_unbounded_start: Option<usize> = None;
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
for value in args {
|
let int_opt = value.as_int().map(Either::Left).ok();
|
||||||
if let Ok(i) = value.as_int() {
|
let range_opt = value.as_range().map(Either::Right).ok();
|
||||||
if i < 0 {
|
|
||||||
return Err(ShellError::UnsupportedInput {
|
|
||||||
msg: "drop nth accepts only positive ints".into(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: value.span(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
rows_to_drop.push(i as usize);
|
|
||||||
} else if let Ok(range) = value.as_range() {
|
|
||||||
match range {
|
|
||||||
Range::IntRange(range) => {
|
|
||||||
let start = range.start();
|
|
||||||
if start < 0 {
|
|
||||||
return Err(ShellError::UnsupportedInput {
|
|
||||||
msg: "drop nth accepts only positive ints".into(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: value.span(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
match range.end() {
|
int_opt
|
||||||
Bound::Included(end) => {
|
.or(range_opt)
|
||||||
if end < start {
|
.ok_or_else(|| ShellError::TypeMismatch {
|
||||||
return Err(ShellError::UnsupportedInput {
|
err_message: "int or range".into(),
|
||||||
msg: "The upper bound must be greater than or equal to the lower bound".into(),
|
span: value.span(),
|
||||||
input: "value originates from here".into(),
|
})
|
||||||
msg_span: head,
|
.map(|either| Spanned {
|
||||||
input_span: value.span(),
|
item: either,
|
||||||
});
|
span: value.span(),
|
||||||
}
|
})
|
||||||
rows_to_drop.extend((start as usize)..=(end as usize));
|
|
||||||
}
|
|
||||||
Bound::Excluded(end) => {
|
|
||||||
if end <= start {
|
|
||||||
return Err(ShellError::UnsupportedInput {
|
|
||||||
msg: "The upper bound must be greater than the lower bound"
|
|
||||||
.into(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: value.span(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
rows_to_drop.extend((start as usize)..(end as usize));
|
|
||||||
}
|
|
||||||
Bound::Unbounded => {
|
|
||||||
let start_usize = start as usize;
|
|
||||||
min_unbounded_start = Some(
|
|
||||||
min_unbounded_start.map_or(start_usize, |s| s.min(start_usize)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Range::FloatRange(_) => {
|
|
||||||
return Err(ShellError::UnsupportedInput {
|
|
||||||
msg: "float range not supported".into(),
|
|
||||||
input: "value originates from here".into(),
|
|
||||||
msg_span: head,
|
|
||||||
input_span: value.span(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::TypeMismatch {
|
|
||||||
err_message: "Expected int or range".into(),
|
|
||||||
span: value.span(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows_to_drop.sort_unstable();
|
|
||||||
rows_to_drop.dedup();
|
|
||||||
|
|
||||||
Ok((VecDeque::from(rows_to_drop), min_unbounded_start))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DropNthIterator {
|
struct DropNthIterator {
|
||||||
input: PipelineIterator,
|
input: PipelineIterator,
|
||||||
rows: VecDeque<usize>,
|
rows: Vec<usize>,
|
||||||
current: usize,
|
current: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,9 +215,9 @@ impl Iterator for DropNthIterator {
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(row) = self.rows.front() {
|
if let Some(row) = self.rows.first() {
|
||||||
if self.current == *row {
|
if self.current == *row {
|
||||||
self.rows.pop_front();
|
self.rows.remove(0);
|
||||||
self.current += 1;
|
self.current += 1;
|
||||||
let _ = self.input.next();
|
let _ = self.input.next();
|
||||||
continue;
|
continue;
|
||||||
|
@ -47,7 +47,7 @@ impl Command for Find {
|
|||||||
.named(
|
.named(
|
||||||
"columns",
|
"columns",
|
||||||
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||||
"column names to be searched",
|
"column names to be searched (with rest parameter, not regex yet)",
|
||||||
Some('c'),
|
Some('c'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
@ -163,12 +163,7 @@ impl Command for Find {
|
|||||||
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find --regex "rr""#,
|
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find --regex "rr""#,
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![Value::list(
|
vec![Value::list(
|
||||||
vec![
|
vec![Value::test_string("Larry"), Value::test_string("Moe")],
|
||||||
Value::test_string(
|
|
||||||
"\u{1b}[37mLa\u{1b}[0m\u{1b}[41;37mrr\u{1b}[0m\u{1b}[37my\u{1b}[0m",
|
|
||||||
),
|
|
||||||
Value::test_string("Moe"),
|
|
||||||
],
|
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)],
|
)],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
@ -349,10 +344,7 @@ fn get_match_pattern_from_arguments(
|
|||||||
// map functions
|
// map functions
|
||||||
|
|
||||||
fn highlight_matches_in_string(pattern: &MatchPattern, val: String) -> String {
|
fn highlight_matches_in_string(pattern: &MatchPattern, val: String) -> String {
|
||||||
if !pattern.regex.is_match(&val).unwrap_or(false) {
|
// strip haystack to remove existing ansi style
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stripped_val = nu_utils::strip_ansi_string_unlikely(val);
|
let stripped_val = nu_utils::strip_ansi_string_unlikely(val);
|
||||||
let mut last_match_end = 0;
|
let mut last_match_end = 0;
|
||||||
let mut highlighted = String::new();
|
let mut highlighted = String::new();
|
||||||
@ -398,7 +390,7 @@ fn highlight_matches_in_string(pattern: &MatchPattern, val: String) -> String {
|
|||||||
highlighted
|
highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_matches_in_value(
|
fn highlight_matches_in_record_or_value(
|
||||||
pattern: &MatchPattern,
|
pattern: &MatchPattern,
|
||||||
value: Value,
|
value: Value,
|
||||||
columns_to_search: &[String],
|
columns_to_search: &[String],
|
||||||
@ -420,16 +412,16 @@ fn highlight_matches_in_value(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = highlight_matches_in_value(pattern, std::mem::take(val), &[]);
|
if let Value::String { val: val_str, .. } = val {
|
||||||
|
if pattern.regex.is_match(val_str).unwrap_or(false) {
|
||||||
|
let val_str = std::mem::take(val_str);
|
||||||
|
*val = highlight_matches_in_string(pattern, val_str).into_value(span)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::record(record, span)
|
Value::record(record, span)
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => vals
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| highlight_matches_in_value(pattern, item, &[]))
|
|
||||||
.collect::<Vec<Value>>()
|
|
||||||
.into_value(span),
|
|
||||||
Value::String { val, .. } => highlight_matches_in_string(pattern, val).into_value(span),
|
Value::String { val, .. } => highlight_matches_in_string(pattern, val).into_value(span),
|
||||||
_ => value,
|
_ => value,
|
||||||
}
|
}
|
||||||
@ -452,22 +444,24 @@ fn find_in_pipelinedata(
|
|||||||
PipelineData::Value(_, _) => input
|
PipelineData::Value(_, _) => input
|
||||||
.filter(
|
.filter(
|
||||||
move |value| {
|
move |value| {
|
||||||
value_should_be_printed(&pattern, value, &columns_to_search, &config)
|
record_or_value_should_be_printed(&pattern, value, &columns_to_search, &config)
|
||||||
!= pattern.invert
|
|
||||||
},
|
},
|
||||||
engine_state.signals(),
|
engine_state.signals(),
|
||||||
)?
|
)?
|
||||||
.map(
|
.map(
|
||||||
move |x| highlight_matches_in_value(&map_pattern, x, &map_columns_to_search),
|
move |x| {
|
||||||
|
highlight_matches_in_record_or_value(&map_pattern, x, &map_columns_to_search)
|
||||||
|
},
|
||||||
engine_state.signals(),
|
engine_state.signals(),
|
||||||
),
|
),
|
||||||
PipelineData::ListStream(stream, metadata) => {
|
PipelineData::ListStream(stream, metadata) => {
|
||||||
let stream = stream.modify(|iter| {
|
let stream = stream.modify(|iter| {
|
||||||
iter.filter(move |value| {
|
iter.filter(move |value| {
|
||||||
value_should_be_printed(&pattern, value, &columns_to_search, &config)
|
record_or_value_should_be_printed(&pattern, value, &columns_to_search, &config)
|
||||||
!= pattern.invert
|
})
|
||||||
|
.map(move |x| {
|
||||||
|
highlight_matches_in_record_or_value(&map_pattern, x, &map_columns_to_search)
|
||||||
})
|
})
|
||||||
.map(move |x| highlight_matches_in_value(&map_pattern, x, &map_columns_to_search))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(PipelineData::ListStream(stream, metadata))
|
Ok(PipelineData::ListStream(stream, metadata))
|
||||||
@ -501,12 +495,7 @@ fn string_should_be_printed(pattern: &MatchPattern, value: &str) -> bool {
|
|||||||
pattern.regex.is_match(value).unwrap_or(false)
|
pattern.regex.is_match(value).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_should_be_printed(
|
fn value_should_be_printed(pattern: &MatchPattern, value: &Value, config: &Config) -> bool {
|
||||||
pattern: &MatchPattern,
|
|
||||||
value: &Value,
|
|
||||||
columns_to_search: &[String],
|
|
||||||
config: &Config,
|
|
||||||
) -> bool {
|
|
||||||
let lower_value = value.to_expanded_string("", config).to_lowercase();
|
let lower_value = value.to_expanded_string("", config).to_lowercase();
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
@ -518,7 +507,8 @@ fn value_should_be_printed(
|
|||||||
| Value::Range { .. }
|
| Value::Range { .. }
|
||||||
| Value::Float { .. }
|
| Value::Float { .. }
|
||||||
| Value::Closure { .. }
|
| Value::Closure { .. }
|
||||||
| Value::Nothing { .. } => {
|
| Value::Nothing { .. }
|
||||||
|
| Value::Error { .. } => {
|
||||||
if !pattern.lower_terms.is_empty() {
|
if !pattern.lower_terms.is_empty() {
|
||||||
// look for exact match when searching with terms
|
// look for exact match when searching with terms
|
||||||
pattern
|
pattern
|
||||||
@ -529,25 +519,37 @@ fn value_should_be_printed(
|
|||||||
string_should_be_printed(pattern, &lower_value)
|
string_should_be_printed(pattern, &lower_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Glob { .. } | Value::CellPath { .. } | Value::Custom { .. } => {
|
Value::Glob { .. }
|
||||||
string_should_be_printed(pattern, &lower_value)
|
| Value::List { .. }
|
||||||
}
|
| Value::CellPath { .. }
|
||||||
|
| Value::Record { .. }
|
||||||
|
| Value::Custom { .. } => string_should_be_printed(pattern, &lower_value),
|
||||||
Value::String { val, .. } => string_should_be_printed(pattern, val),
|
Value::String { val, .. } => string_should_be_printed(pattern, val),
|
||||||
Value::List { vals, .. } => vals
|
Value::Binary { .. } => false,
|
||||||
.iter()
|
}
|
||||||
.any(|item| value_should_be_printed(pattern, item, &[], config)),
|
}
|
||||||
|
|
||||||
|
fn record_or_value_should_be_printed(
|
||||||
|
pattern: &MatchPattern,
|
||||||
|
value: &Value,
|
||||||
|
columns_to_search: &[String],
|
||||||
|
config: &Config,
|
||||||
|
) -> bool {
|
||||||
|
let match_found = match value {
|
||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => {
|
||||||
|
// Only perform column selection if given columns.
|
||||||
let col_select = !columns_to_search.is_empty();
|
let col_select = !columns_to_search.is_empty();
|
||||||
record.iter().any(|(col, val)| {
|
record.iter().any(|(col, val)| {
|
||||||
if col_select && !columns_to_search.contains(col) {
|
if col_select && !columns_to_search.contains(col) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
value_should_be_printed(pattern, val, &[], config)
|
value_should_be_printed(pattern, val, config)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Value::Binary { .. } => false,
|
_ => value_should_be_printed(pattern, value, config),
|
||||||
Value::Error { .. } => true,
|
};
|
||||||
}
|
|
||||||
|
match_found != pattern.invert
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
@ -572,46 +574,6 @@ fn split_string_if_multiline(input: PipelineData, head_span: Span) -> PipelineDa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// function for using find from other commands
|
|
||||||
pub fn find_internal(
|
|
||||||
input: PipelineData,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
search_term: &str,
|
|
||||||
columns_to_search: &[&str],
|
|
||||||
highlight: bool,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let span = input.span().unwrap_or(Span::unknown());
|
|
||||||
|
|
||||||
let style_computer = StyleComputer::from_config(engine_state, stack);
|
|
||||||
let string_style = style_computer.compute("string", &Value::string("search result", span));
|
|
||||||
let highlight_style =
|
|
||||||
style_computer.compute("search_result", &Value::string("search result", span));
|
|
||||||
|
|
||||||
let regex_str = format!("(?i){}", escape(search_term));
|
|
||||||
|
|
||||||
let regex = Regex::new(regex_str.as_str()).map_err(|e| ShellError::TypeMismatch {
|
|
||||||
err_message: format!("invalid regex: {e}"),
|
|
||||||
span: Span::unknown(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let pattern = MatchPattern {
|
|
||||||
regex,
|
|
||||||
lower_terms: vec![search_term.to_lowercase()],
|
|
||||||
highlight,
|
|
||||||
invert: false,
|
|
||||||
string_style,
|
|
||||||
highlight_style,
|
|
||||||
};
|
|
||||||
|
|
||||||
let columns_to_search = columns_to_search
|
|
||||||
.iter()
|
|
||||||
.map(|str| String::from(*str))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
find_in_pipelinedata(pattern, columns_to_search, engine_state, stack, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -40,14 +40,9 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
"The cell path to the data.",
|
"The cell path to the data.",
|
||||||
)
|
)
|
||||||
.rest("rest", SyntaxShape::CellPath, "Additional cell paths.")
|
.rest("rest", SyntaxShape::CellPath, "Additional cell paths.")
|
||||||
.switch(
|
|
||||||
"optional",
|
|
||||||
"make all cell path members optional (returns `null` for missing values)",
|
|
||||||
Some('o'),
|
|
||||||
)
|
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"ignore missing data (make all cell path members optional) (deprecated)",
|
"ignore missing data (make all cell path members optional)",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
@ -114,14 +109,13 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_path: CellPath = call.req_const(working_set, 0)?;
|
let cell_path: CellPath = call.req_const(working_set, 0)?;
|
||||||
let rest: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
let rest: Vec<CellPath> = call.rest_const(working_set, 1)?;
|
||||||
let optional = call.has_flag_const(working_set, "optional")?
|
let ignore_errors = call.has_flag_const(working_set, "ignore-errors")?;
|
||||||
|| call.has_flag_const(working_set, "ignore-errors")?;
|
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
action(
|
action(
|
||||||
input,
|
input,
|
||||||
cell_path,
|
cell_path,
|
||||||
rest,
|
rest,
|
||||||
optional,
|
ignore_errors,
|
||||||
working_set.permanent().signals().clone(),
|
working_set.permanent().signals().clone(),
|
||||||
call.head,
|
call.head,
|
||||||
)
|
)
|
||||||
@ -137,14 +131,13 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
|
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let optional = call.has_flag(engine_state, stack, "optional")?
|
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
||||||
|| call.has_flag(engine_state, stack, "ignore-errors")?;
|
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
action(
|
action(
|
||||||
input,
|
input,
|
||||||
cell_path,
|
cell_path,
|
||||||
rest,
|
rest,
|
||||||
optional,
|
ignore_errors,
|
||||||
engine_state.signals().clone(),
|
engine_state.signals().clone(),
|
||||||
call.head,
|
call.head,
|
||||||
)
|
)
|
||||||
@ -159,13 +152,6 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
since: Some("0.105.0".into()),
|
since: Some("0.105.0".into()),
|
||||||
expected_removal: None,
|
expected_removal: None,
|
||||||
help: Some("Cell-paths are now case-sensitive by default.\nTo access fields case-insensitively, add `!` after the relevant path member.".into())
|
help: Some("Cell-paths are now case-sensitive by default.\nTo access fields case-insensitively, add `!` after the relevant path member.".into())
|
||||||
},
|
|
||||||
DeprecationEntry {
|
|
||||||
ty: DeprecationType::Flag("ignore-errors".into()),
|
|
||||||
report_mode: ReportMode::FirstUse,
|
|
||||||
since: Some("0.106.0".into()),
|
|
||||||
expected_removal: None,
|
|
||||||
help: Some("This flag has been renamed to `--optional (-o)` to better reflect its behavior.".into())
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -175,19 +161,29 @@ fn action(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
mut cell_path: CellPath,
|
mut cell_path: CellPath,
|
||||||
mut rest: Vec<CellPath>,
|
mut rest: Vec<CellPath>,
|
||||||
optional: bool,
|
ignore_errors: bool,
|
||||||
signals: Signals,
|
signals: Signals,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
if optional {
|
if ignore_errors {
|
||||||
cell_path.make_optional();
|
cell_path.make_optional();
|
||||||
for path in &mut rest {
|
for path in &mut rest {
|
||||||
path.make_optional();
|
path.make_optional();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let PipelineData::Empty = input {
|
match input {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: span });
|
PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: span }),
|
||||||
|
// Allow chaining of get -i
|
||||||
|
PipelineData::Value(val @ Value::Nothing { .. }, ..) if !ignore_errors => {
|
||||||
|
return Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "table or record".into(),
|
||||||
|
wrong_type: "nothing".into(),
|
||||||
|
dst_span: span,
|
||||||
|
src_span: val.span(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if rest.is_empty() {
|
if rest.is_empty() {
|
||||||
|
@ -231,12 +231,7 @@ pub fn group_by(
|
|||||||
|
|
||||||
let values: Vec<Value> = input.into_iter().collect();
|
let values: Vec<Value> = input.into_iter().collect();
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
let val = if to_table {
|
return Ok(Value::record(Record::new(), head).into_pipeline_data());
|
||||||
Value::list(Vec::new(), head)
|
|
||||||
} else {
|
|
||||||
Value::record(Record::new(), head)
|
|
||||||
};
|
|
||||||
return Ok(val.into_pipeline_data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let grouped = match &groupers[..] {
|
let grouped = match &groupers[..] {
|
||||||
|
@ -379,7 +379,10 @@ fn merge_records(left: &Record, right: &Record, shared_key: Option<&str>) -> Rec
|
|||||||
let k_shared = shared_key == Some(k.as_str());
|
let k_shared = shared_key == Some(k.as_str());
|
||||||
// Do not output shared join key twice
|
// Do not output shared join key twice
|
||||||
if !(k_seen && k_shared) {
|
if !(k_seen && k_shared) {
|
||||||
record.push(if k_seen { format!("{k}_") } else { k.clone() }, v.clone());
|
record.push(
|
||||||
|
if k_seen { format!("{}_", k) } else { k.clone() },
|
||||||
|
v.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
record
|
record
|
||||||
|
@ -100,7 +100,7 @@ impl Command for Last {
|
|||||||
let mut buf = VecDeque::new();
|
let mut buf = VecDeque::new();
|
||||||
|
|
||||||
for row in iterator {
|
for row in iterator {
|
||||||
engine_state.signals().check(&head)?;
|
engine_state.signals().check(head)?;
|
||||||
if buf.len() == rows {
|
if buf.len() == rows {
|
||||||
buf.pop_front();
|
buf.pop_front();
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ pub use empty::empty;
|
|||||||
pub use enumerate::Enumerate;
|
pub use enumerate::Enumerate;
|
||||||
pub use every::Every;
|
pub use every::Every;
|
||||||
pub use filter::Filter;
|
pub use filter::Filter;
|
||||||
pub use find::{Find, find_internal};
|
pub use find::Find;
|
||||||
pub use first::First;
|
pub use first::First;
|
||||||
pub use flatten::Flatten;
|
pub use flatten::Flatten;
|
||||||
pub use get::Get;
|
pub use get::Get;
|
||||||
|
@ -119,7 +119,7 @@ impl Command for Reduce {
|
|||||||
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||||
|
|
||||||
for value in iter {
|
for value in iter {
|
||||||
engine_state.signals().check(&head)?;
|
engine_state.signals().check(head)?;
|
||||||
acc = closure
|
acc = closure
|
||||||
.add_arg(value)
|
.add_arg(value)
|
||||||
.add_arg(acc.clone())
|
.add_arg(acc.clone())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{DeprecationEntry, DeprecationType, ReportMode, ast::PathMember, casing::Casing};
|
use nu_protocol::{ast::PathMember, casing::Casing};
|
||||||
use std::{cmp::Reverse, collections::HashSet};
|
use std::{cmp::Reverse, collections::HashSet};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -17,10 +17,9 @@ impl Command for Reject {
|
|||||||
(Type::table(), Type::table()),
|
(Type::table(), Type::table()),
|
||||||
(Type::list(Type::Any), Type::list(Type::Any)),
|
(Type::list(Type::Any), Type::list(Type::Any)),
|
||||||
])
|
])
|
||||||
.switch("optional", "make all cell path members optional", Some('o'))
|
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"ignore missing data (make all cell path members optional) (deprecated)",
|
"ignore missing data (make all cell path members optional)",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.rest(
|
.rest(
|
||||||
@ -91,9 +90,8 @@ impl Command for Reject {
|
|||||||
}
|
}
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
let optional = call.has_flag(engine_state, stack, "optional")?
|
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
||||||
|| call.has_flag(engine_state, stack, "ignore-errors")?;
|
if ignore_errors {
|
||||||
if optional {
|
|
||||||
for cell_path in &mut new_columns {
|
for cell_path in &mut new_columns {
|
||||||
cell_path.make_optional();
|
cell_path.make_optional();
|
||||||
}
|
}
|
||||||
@ -102,19 +100,6 @@ impl Command for Reject {
|
|||||||
reject(engine_state, span, input, new_columns)
|
reject(engine_state, span, input, new_columns)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
|
|
||||||
vec![DeprecationEntry {
|
|
||||||
ty: DeprecationType::Flag("ignore-errors".into()),
|
|
||||||
report_mode: ReportMode::FirstUse,
|
|
||||||
since: Some("0.106.0".into()),
|
|
||||||
expected_removal: None,
|
|
||||||
help: Some(
|
|
||||||
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{
|
use nu_protocol::{PipelineIterator, ast::PathMember, casing::Casing};
|
||||||
DeprecationEntry, DeprecationType, PipelineIterator, ReportMode, ast::PathMember,
|
|
||||||
casing::Casing,
|
|
||||||
};
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -21,14 +18,9 @@ impl Command for Select {
|
|||||||
(Type::table(), Type::table()),
|
(Type::table(), Type::table()),
|
||||||
(Type::List(Box::new(Type::Any)), Type::Any),
|
(Type::List(Box::new(Type::Any)), Type::Any),
|
||||||
])
|
])
|
||||||
.switch(
|
|
||||||
"optional",
|
|
||||||
"make all cell path members optional (returns `null` for missing values)",
|
|
||||||
Some('o'),
|
|
||||||
)
|
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"ignore missing data (make all cell path members optional) (deprecated)",
|
"ignore missing data (make all cell path members optional)",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.rest(
|
.rest(
|
||||||
@ -108,11 +100,10 @@ produce a table, a list will produce a list, and a record will produce a record.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let optional = call.has_flag(engine_state, stack, "optional")?
|
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
|
||||||
|| call.has_flag(engine_state, stack, "ignore-errors")?;
|
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
if optional {
|
if ignore_errors {
|
||||||
for cell_path in &mut new_columns {
|
for cell_path in &mut new_columns {
|
||||||
cell_path.make_optional();
|
cell_path.make_optional();
|
||||||
}
|
}
|
||||||
@ -121,19 +112,6 @@ produce a table, a list will produce a list, and a record will produce a record.
|
|||||||
select(engine_state, span, new_columns, input)
|
select(engine_state, span, new_columns, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
|
|
||||||
vec![DeprecationEntry {
|
|
||||||
ty: DeprecationType::Flag("ignore-errors".into()),
|
|
||||||
report_mode: ReportMode::FirstUse,
|
|
||||||
since: Some("0.106.0".into()),
|
|
||||||
expected_removal: None,
|
|
||||||
help: Some(
|
|
||||||
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -31,7 +31,7 @@ pub fn boolean_fold(
|
|||||||
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||||
|
|
||||||
for value in input {
|
for value in input {
|
||||||
engine_state.signals().check(&head)?;
|
engine_state.signals().check(head)?;
|
||||||
let pred = closure.run_with_value(value)?.into_value(head)?.is_true();
|
let pred = closure.run_with_value(value)?.into_value(head)?.is_true();
|
||||||
|
|
||||||
if pred == accumulator {
|
if pred == accumulator {
|
||||||
|
@ -43,7 +43,10 @@ Row conditions cannot be stored in a variable. To pass a condition with a variab
|
|||||||
])
|
])
|
||||||
.required(
|
.required(
|
||||||
"condition",
|
"condition",
|
||||||
SyntaxShape::RowCondition,
|
SyntaxShape::OneOf(vec![
|
||||||
|
SyntaxShape::RowCondition,
|
||||||
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||||
|
]),
|
||||||
"Filter row condition or closure.",
|
"Filter row condition or closure.",
|
||||||
)
|
)
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
|
@ -203,7 +203,6 @@ mod test {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -222,7 +221,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromCsv {}));
|
working_set.add_decl(Box::new(FromCsv {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -231,7 +229,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#""a,b\n1,2" | metadata set --content-type 'text/csv' --datasource-ls | from csv | metadata | reject span | $in"#;
|
let cmd = r#""a,b\n1,2" | metadata set --content-type 'text/csv' --datasource-ls | from csv | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -248,7 +248,6 @@ fn convert_string_to_value_strict(string_input: &str, span: Span) -> Result<Valu
|
|||||||
mod test {
|
mod test {
|
||||||
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -269,7 +268,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromJson {}));
|
working_set.add_decl(Box::new(FromJson {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -278,7 +276,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#"'{"a":1,"b":2}' | metadata set --content-type 'application/json' --datasource-ls | from json | metadata | reject span | $in"#;
|
let cmd = r#"'{"a":1,"b":2}' | metadata set --content-type 'application/json' --datasource-ls | from json | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -212,7 +212,7 @@ impl From<ReadError> for ShellError {
|
|||||||
},
|
},
|
||||||
ReadError::TypeMismatch(marker, span) => ShellError::GenericError {
|
ReadError::TypeMismatch(marker, span) => ShellError::GenericError {
|
||||||
error: "Invalid marker while reading MessagePack data".into(),
|
error: "Invalid marker while reading MessagePack data".into(),
|
||||||
msg: format!("unexpected {marker:?} in data"),
|
msg: format!("unexpected {:?} in data", marker),
|
||||||
span: Some(span),
|
span: Some(span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
@ -514,7 +514,6 @@ fn assert_eof(input: &mut impl io::Read, span: Span) -> Result<(), ShellError> {
|
|||||||
mod test {
|
mod test {
|
||||||
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet, ToMsgpack};
|
use crate::{Metadata, MetadataSet, ToMsgpack};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -536,7 +535,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromMsgpack {}));
|
working_set.add_decl(Box::new(FromMsgpack {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -545,7 +543,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#"{a: 1 b: 2} | to msgpack | metadata set --datasource-ls | from msgpack | metadata | reject span | $in"#;
|
let cmd = r#"{a: 1 b: 2} | to msgpack | metadata set --datasource-ls | from msgpack | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -74,7 +74,6 @@ impl Command for FromNuon {
|
|||||||
mod test {
|
mod test {
|
||||||
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -95,7 +94,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromNuon {}));
|
working_set.add_decl(Box::new(FromNuon {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -104,7 +102,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#"'[[a, b]; [1, 2]]' | metadata set --content-type 'application/x-nuon' --datasource-ls | from nuon | metadata | reject span | $in"#;
|
let cmd = r#"'[[a, b]; [1, 2]]' | metadata set --content-type 'application/x-nuon' --datasource-ls | from nuon | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -167,7 +167,7 @@ fn parse_aligned_columns<'a>(
|
|||||||
let headers: Vec<(String, usize)> = indices
|
let headers: Vec<(String, usize)> = indices
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, position)| (format!("column{i}"), *position))
|
.map(|(i, position)| (format!("column{}", i), *position))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
construct(ls.iter().map(|s| s.to_owned()), headers)
|
construct(ls.iter().map(|s| s.to_owned()), headers)
|
||||||
|
@ -145,7 +145,6 @@ pub fn convert_string_to_value(string_input: String, span: Span) -> Result<Value
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -346,7 +345,6 @@ mod tests {
|
|||||||
working_set.add_decl(Box::new(FromToml {}));
|
working_set.add_decl(Box::new(FromToml {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -355,7 +353,7 @@ mod tests {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#""[a]\nb = 1\nc = 1" | metadata set --content-type 'text/x-toml' --datasource-ls | from toml | metadata | reject span | $in"#;
|
let cmd = r#""[a]\nb = 1\nc = 1" | metadata set --content-type 'text/x-toml' --datasource-ls | from toml | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -160,7 +160,6 @@ fn from_tsv(
|
|||||||
mod test {
|
mod test {
|
||||||
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -181,7 +180,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromTsv {}));
|
working_set.add_decl(Box::new(FromTsv {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -190,7 +188,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#""a\tb\n1\t2" | metadata set --content-type 'text/tab-separated-values' --datasource-ls | from tsv | metadata | reject span | $in"#;
|
let cmd = r#""a\tb\n1\t2" | metadata set --content-type 'text/tab-separated-values' --datasource-ls | from tsv | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -252,7 +252,7 @@ fn process_xml_parse_error(source: String, err: roxmltree::Error, span: Span) ->
|
|||||||
pos,
|
pos,
|
||||||
),
|
),
|
||||||
roxmltree::Error::UnknownNamespace(prefix, pos) => {
|
roxmltree::Error::UnknownNamespace(prefix, pos) => {
|
||||||
make_xml_error_spanned(format!("Unknown prefix {prefix}"), source, pos)
|
make_xml_error_spanned(format!("Unknown prefix {}", prefix), source, pos)
|
||||||
}
|
}
|
||||||
roxmltree::Error::UnexpectedCloseTag(expected, actual, pos) => make_xml_error_spanned(
|
roxmltree::Error::UnexpectedCloseTag(expected, actual, pos) => make_xml_error_spanned(
|
||||||
format!("Unexpected close tag {actual}, expected {expected}"),
|
format!("Unexpected close tag {actual}, expected {expected}"),
|
||||||
@ -370,7 +370,6 @@ fn make_xml_error_spanned(msg: impl Into<String>, src: String, pos: TextPos) ->
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::Metadata;
|
use crate::Metadata;
|
||||||
use crate::MetadataSet;
|
use crate::MetadataSet;
|
||||||
use crate::Reject;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -542,7 +541,6 @@ mod tests {
|
|||||||
working_set.add_decl(Box::new(FromXml {}));
|
working_set.add_decl(Box::new(FromXml {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -554,7 +552,7 @@ mod tests {
|
|||||||
let cmd = r#"'<?xml version="1.0" encoding="UTF-8"?>
|
let cmd = r#"'<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<note>
|
<note>
|
||||||
<remember>Event</remember>
|
<remember>Event</remember>
|
||||||
</note>' | metadata set --content-type 'application/xml' --datasource-ls | from xml | metadata | reject span | $in"#;
|
</note>' | metadata set --content-type 'application/xml' --datasource-ls | from xml | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -158,26 +158,27 @@ fn convert_yaml_value_to_nu_value(
|
|||||||
}
|
}
|
||||||
serde_yaml::Value::Tagged(t) => {
|
serde_yaml::Value::Tagged(t) => {
|
||||||
let tag = &t.tag;
|
let tag = &t.tag;
|
||||||
|
let value = match &t.value {
|
||||||
match &t.value {
|
|
||||||
serde_yaml::Value::String(s) => {
|
serde_yaml::Value::String(s) => {
|
||||||
let val = format!("{tag} {s}").trim().to_string();
|
let val = format!("{} {}", tag, s).trim().to_string();
|
||||||
Value::string(val, span)
|
Value::string(val, span)
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Number(n) => {
|
serde_yaml::Value::Number(n) => {
|
||||||
let val = format!("{tag} {n}").trim().to_string();
|
let val = format!("{} {}", tag, n).trim().to_string();
|
||||||
Value::string(val, span)
|
Value::string(val, span)
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Bool(b) => {
|
serde_yaml::Value::Bool(b) => {
|
||||||
let val = format!("{tag} {b}").trim().to_string();
|
let val = format!("{} {}", tag, b).trim().to_string();
|
||||||
Value::string(val, span)
|
Value::string(val, span)
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Null => {
|
serde_yaml::Value::Null => {
|
||||||
let val = format!("{tag}").trim().to_string();
|
let val = format!("{}", tag).trim().to_string();
|
||||||
Value::string(val, span)
|
Value::string(val, span)
|
||||||
}
|
}
|
||||||
v => convert_yaml_value_to_nu_value(v, span, val_span)?,
|
v => convert_yaml_value_to_nu_value(v, span, val_span)?,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
value
|
||||||
}
|
}
|
||||||
serde_yaml::Value::Null => Value::nothing(span),
|
serde_yaml::Value::Null => Value::nothing(span),
|
||||||
x => unimplemented!("Unsupported YAML case: {:?}", x),
|
x => unimplemented!("Unsupported YAML case: {:?}", x),
|
||||||
@ -243,7 +244,6 @@ fn from_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::Reject;
|
|
||||||
use crate::{Metadata, MetadataSet};
|
use crate::{Metadata, MetadataSet};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -410,7 +410,6 @@ mod test {
|
|||||||
working_set.add_decl(Box::new(FromYaml {}));
|
working_set.add_decl(Box::new(FromYaml {}));
|
||||||
working_set.add_decl(Box::new(Metadata {}));
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
working_set.add_decl(Box::new(MetadataSet {}));
|
working_set.add_decl(Box::new(MetadataSet {}));
|
||||||
working_set.add_decl(Box::new(Reject {}));
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
@ -419,7 +418,7 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = r#""a: 1\nb: 2" | metadata set --content-type 'application/yaml' --datasource-ls | from yaml | metadata | reject span | $in"#;
|
let cmd = r#""a: 1\nb: 2" | metadata set --content-type 'application/yaml' --datasource-ls | from yaml | metadata | $in"#;
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
|
@ -166,14 +166,14 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type | $in";
|
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type";
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
&mut engine_state,
|
&mut engine_state,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Value::test_string("text/csv"),
|
Value::test_record(record!("content_type" => Value::test_string("text/csv"))),
|
||||||
result.expect("There should be a result")
|
result.expect("There should be a result")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ fn make_unsupported_input_error(
|
|||||||
) -> ShellError {
|
) -> ShellError {
|
||||||
ShellError::UnsupportedInput {
|
ShellError::UnsupportedInput {
|
||||||
msg: "expected table or record".to_string(),
|
msg: "expected table or record".to_string(),
|
||||||
input: format!("input type: {type}"),
|
input: format!("input type: {}", r#type),
|
||||||
msg_span: head,
|
msg_span: head,
|
||||||
input_span: span,
|
input_span: span,
|
||||||
}
|
}
|
||||||
|
@ -229,14 +229,14 @@ mod test {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type | $in";
|
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type";
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
&mut engine_state,
|
&mut engine_state,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Value::test_string("application/json"),
|
Value::test_record(record!("content_type" => Value::test_string("application/json"))),
|
||||||
result.expect("There should be a result")
|
result.expect("There should be a result")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ fn table(
|
|||||||
escaped_rows.push(escaped_row);
|
escaped_rows.push(escaped_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column_widths.is_empty() || column_widths.iter().all(|x| *x == 0))
|
let output_string = if (column_widths.is_empty() || column_widths.iter().all(|x| *x == 0))
|
||||||
&& escaped_rows.is_empty()
|
&& escaped_rows.is_empty()
|
||||||
{
|
{
|
||||||
String::from("")
|
String::from("")
|
||||||
@ -260,7 +260,9 @@ fn table(
|
|||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
};
|
||||||
|
|
||||||
|
output_string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineData, bool) {
|
pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineData, bool) {
|
||||||
@ -904,14 +906,14 @@ mod tests {
|
|||||||
.merge_delta(delta)
|
.merge_delta(delta)
|
||||||
.expect("Error merging delta");
|
.expect("Error merging delta");
|
||||||
|
|
||||||
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type | $in";
|
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type";
|
||||||
let result = eval_pipeline_without_terminal_expression(
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
cmd,
|
cmd,
|
||||||
std::env::temp_dir().as_ref(),
|
std::env::temp_dir().as_ref(),
|
||||||
&mut engine_state,
|
&mut engine_state,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Value::test_string("text/markdown"),
|
Value::test_record(record!("content_type" => Value::test_string("text/markdown"))),
|
||||||
result.expect("There should be a result")
|
result.expect("There should be a result")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user