mirror of
https://github.com/nushell/nushell.git
synced 2025-07-04 08:31:05 +02:00
Compare commits
138 Commits
Author | SHA1 | Date | |
---|---|---|---|
5d8763ed1d | |||
58124e66a4 | |||
25af32d3a8 | |||
5a746c0ed6 | |||
89ccdc2b06 | |||
76ee00e013 | |||
4e5a1ced13 | |||
1f62024a15 | |||
6181ea5fc1 | |||
1751ac12f4 | |||
6cff54ed0d | |||
ec3e4ce120 | |||
f97443aff6 | |||
c925537c48 | |||
c5545c59c6 | |||
55044aa7d6 | |||
4be7004289 | |||
814a5caf9a | |||
81ece18d5e | |||
0ba81f1d51 | |||
c81fa397b6 | |||
8c36e9df44 | |||
1402508416 | |||
dc3c34275d | |||
f77fe04425 | |||
20ac30b6e2 | |||
4007256cfd | |||
b634f1b010 | |||
8a77d1ed92 | |||
69a17fb247 | |||
9f144798d3 | |||
0b651b6372 | |||
ce09186e2e | |||
0c67d742f0 | |||
2ef34a3b4b | |||
2d72f892fe | |||
ee4e0a933b | |||
765b303689 | |||
e427c68731 | |||
ff6c0fcb81 | |||
bcf3537395 | |||
4efccb2b1c | |||
67b5e1bde9 | |||
eb4fd144eb | |||
d51e82f33b | |||
7827b1fb87 | |||
7dbda76fad | |||
0dbd014d8b | |||
d064d187ab | |||
399319476a | |||
4f4e8c984e | |||
129ae0bf3e | |||
471c58448e | |||
a03c1c266c | |||
afdb68dc71 | |||
bc1b2fa5bd | |||
8c507dc984 | |||
4a82ee6c11 | |||
e8da57b05e | |||
f481879ed3 | |||
7c1487e18d | |||
2cc4191ec9 | |||
844cb1213b | |||
783f2a9342 | |||
eb6870cab5 | |||
0d367af24a | |||
5c15a4dd6e | |||
eeade99452 | |||
679879f79b | |||
c4bbc3edad | |||
6c6d215197 | |||
4b9ec03110 | |||
b9ecfeb890 | |||
4dbbacc35d | |||
28ef14399c | |||
20aaaaf90c | |||
7c274ad4d8 | |||
30c331e882 | |||
fa2e6e5d53 | |||
7eaa6d01ab | |||
d34581db4a | |||
85d6529f0d | |||
50039164f1 | |||
c64017de7b | |||
9e445fd4c5 | |||
cc4f8bbd82 | |||
16453b6986 | |||
d6b9153ac5 | |||
80a183dde2 | |||
f2af12af2c | |||
7ad4c679b3 | |||
9a0c6f2e02 | |||
78d0e1d0b8 | |||
dc739f703a | |||
ac7263d957 | |||
9c52b93975 | |||
80220b722b | |||
d1dc610769 | |||
cc767463e6 | |||
8f4ea69c22 | |||
6c026242d4 | |||
4a26719b0c | |||
d2f513da36 | |||
feef612388 | |||
65074ec449 | |||
a19cac2673 | |||
5326e51e4f | |||
b39cca91e5 | |||
57825a5c45 | |||
19cee5fda1 | |||
95a4649cc9 | |||
e96039fb1b | |||
65e2733571 | |||
bc437da5c7 | |||
65c4083ae6 | |||
6a2fd91a01 | |||
b6d31e0e45 | |||
d2c87ad4b4 | |||
a26a01c8d0 | |||
414216edfa | |||
6df001f72d | |||
4880721b73 | |||
1bb953877e | |||
ef7ade59f3 | |||
1c677c9577 | |||
1072bd06c3 | |||
4aa9102523 | |||
0c7a8e3634 | |||
addf8ca942 | |||
7cfd4d2cfa | |||
4ae53d93fb | |||
29e809ad77 | |||
e1c6be0682 | |||
bf40f035f6 | |||
989a14709c | |||
8d8b44342b | |||
7980ad9f7f | |||
af15f794b4 |
@ -26,3 +26,8 @@ rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-stati
|
|||||||
# [target.aarch64-apple-darwin]
|
# [target.aarch64-apple-darwin]
|
||||||
# linker = "clang"
|
# linker = "clang"
|
||||||
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
||||||
|
[target.aarch64-apple-darwin]
|
||||||
|
# We can guarantee that this target will always run on a CPU with _at least_
|
||||||
|
# these capabilities, so let's optimize for them
|
||||||
|
rustflags = ["-Ctarget-cpu=apple-m1"]
|
1
.github/.typos.toml
vendored
1
.github/.typos.toml
vendored
@ -11,3 +11,4 @@ Plasticos = "Plasticos"
|
|||||||
IIF = "IIF"
|
IIF = "IIF"
|
||||||
numer = "numer"
|
numer = "numer"
|
||||||
ratatui = "ratatui"
|
ratatui = "ratatui"
|
||||||
|
doas = "doas"
|
||||||
|
129
.github/workflows/nightly-build.yml
vendored
129
.github/workflows/nightly-build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
uses: hustcer/setup-nu@v3.6
|
uses: hustcer/setup-nu@v3.6
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.84.0
|
version: 0.85.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ jobs:
|
|||||||
git push origin --tags
|
git push origin --tags
|
||||||
}
|
}
|
||||||
|
|
||||||
release:
|
standard:
|
||||||
name: Release
|
name: Std
|
||||||
needs: prepare
|
needs: prepare
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -117,10 +117,10 @@ jobs:
|
|||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: '--exclude=nu-cmd-dataframe'
|
target_rustflags: ''
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: '--exclude=nu-cmd-dataframe'
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
@ -128,6 +128,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Update Rust Toolchain Target
|
||||||
run: |
|
run: |
|
||||||
@ -135,13 +136,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||||
with:
|
|
||||||
rustflags: ''
|
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.6
|
uses: hustcer/setup-nu@v3.6
|
||||||
with:
|
with:
|
||||||
version: 0.84.0
|
version: 0.85.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -149,6 +148,118 @@ jobs:
|
|||||||
id: nu
|
id: nu
|
||||||
run: nu .github/workflows/release-pkg.nu
|
run: nu .github/workflows/release-pkg.nu
|
||||||
env:
|
env:
|
||||||
|
RELEASE_TYPE: standard
|
||||||
|
OS: ${{ matrix.os }}
|
||||||
|
REF: ${{ github.ref }}
|
||||||
|
TARGET: ${{ matrix.target }}
|
||||||
|
_EXTRA_: ${{ matrix.extra }}
|
||||||
|
TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
|
||||||
|
|
||||||
|
- name: Create an Issue for Release Failure
|
||||||
|
if: ${{ failure() }}
|
||||||
|
uses: JasonEtco/create-an-issue@v2.9.1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
update_existing: true
|
||||||
|
search_existing: open
|
||||||
|
filename: .github/AUTO_ISSUE_TEMPLATE/nightly-build-fail.md
|
||||||
|
|
||||||
|
- name: Set Outputs of Short SHA
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||||
|
sha_short=$(git rev-parse --short HEAD)
|
||||||
|
echo "sha_short=${sha_short:0:7}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
|
# Create a release only in nushell/nightly repo
|
||||||
|
- name: Publish Archive
|
||||||
|
uses: softprops/action-gh-release@v0.1.15
|
||||||
|
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
files: ${{ steps.nu.outputs.archive }}
|
||||||
|
tag_name: nightly-${{ steps.vars.outputs.sha_short }}
|
||||||
|
name: Nu-nightly-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.sha_short }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
full:
|
||||||
|
name: Full
|
||||||
|
needs: prepare
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- aarch64-apple-darwin
|
||||||
|
- x86_64-apple-darwin
|
||||||
|
- x86_64-pc-windows-msvc
|
||||||
|
- aarch64-pc-windows-msvc
|
||||||
|
- x86_64-unknown-linux-gnu
|
||||||
|
- x86_64-unknown-linux-musl
|
||||||
|
- aarch64-unknown-linux-gnu
|
||||||
|
extra: ['bin']
|
||||||
|
include:
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
extra: 'bin'
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
extra: msi
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
extra: 'bin'
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
extra: msi
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-unknown-linux-gnu
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-unknown-linux-gnu
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
|
||||||
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Update Rust Toolchain Target
|
||||||
|
run: |
|
||||||
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
|
- name: Setup Rust toolchain and cache
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||||
|
|
||||||
|
- name: Setup Nushell
|
||||||
|
uses: hustcer/setup-nu@v3.6
|
||||||
|
with:
|
||||||
|
version: 0.85.0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Release Nu Binary
|
||||||
|
id: nu
|
||||||
|
run: nu .github/workflows/release-pkg.nu
|
||||||
|
env:
|
||||||
|
RELEASE_TYPE: full
|
||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
@ -206,7 +317,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.6
|
uses: hustcer/setup-nu@v3.6
|
||||||
with:
|
with:
|
||||||
version: 0.84.0
|
version: 0.85.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
25
.github/workflows/release-pkg.nu
vendored
25
.github/workflows/release-pkg.nu
vendored
@ -51,11 +51,26 @@ let dist = $'($env.GITHUB_WORKSPACE)/output'
|
|||||||
let version = (open Cargo.toml | get package.version)
|
let version = (open Cargo.toml | get package.version)
|
||||||
|
|
||||||
print $'Debugging info:'
|
print $'Debugging info:'
|
||||||
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
print { version: $version, bin: $bin, os: $os, releaseType: $env.RELEASE_TYPE, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
||||||
|
|
||||||
|
# Rename the full release name so that we won't break the existing scripts for standard release downloading, such as:
|
||||||
|
# curl -s https://api.github.com/repos/chmln/sd/releases/latest | grep browser_download_url | cut -d '"' -f 4 | grep x86_64-unknown-linux-musl
|
||||||
|
const FULL_RLS_NAMING = {
|
||||||
|
x86_64-apple-darwin: 'x86_64-darwin-full',
|
||||||
|
aarch64-apple-darwin: 'aarch64-darwin-full',
|
||||||
|
x86_64-unknown-linux-gnu: 'x86_64-linux-gnu-full',
|
||||||
|
x86_64-pc-windows-msvc: 'x86_64-windows-msvc-full',
|
||||||
|
x86_64-unknown-linux-musl: 'x86_64-linux-musl-full',
|
||||||
|
aarch64-unknown-linux-gnu: 'aarch64-linux-gnu-full',
|
||||||
|
aarch64-pc-windows-msvc: 'aarch64-windows-msvc-full',
|
||||||
|
riscv64gc-unknown-linux-gnu: 'riscv64-linux-gnu-full',
|
||||||
|
armv7-unknown-linux-gnueabihf: 'armv7-linux-gnueabihf-full',
|
||||||
|
}
|
||||||
|
|
||||||
# $env
|
# $env
|
||||||
|
|
||||||
let USE_UBUNTU = 'ubuntu-20.04'
|
let USE_UBUNTU = 'ubuntu-20.04'
|
||||||
|
let FULL_NAME = $FULL_RLS_NAMING | get -i $target | default 'unknown-target-full'
|
||||||
|
|
||||||
print $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
print $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
||||||
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
||||||
@ -141,7 +156,7 @@ cd $dist; print $'(char nl)Creating release archive...'; hr-line
|
|||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
|
|
||||||
let files = (ls | get name)
|
let files = (ls | get name)
|
||||||
let dest = $'($bin)-($version)-($target)'
|
let dest = if $env.RELEASE_TYPE == 'full' { $'($bin)-($version)-($FULL_NAME)' } else { $'($bin)-($version)-($target)' }
|
||||||
let archive = $'($dist)/($dest).tar.gz'
|
let archive = $'($dist)/($dest).tar.gz'
|
||||||
|
|
||||||
mkdir $dest
|
mkdir $dest
|
||||||
@ -156,7 +171,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
|
|
||||||
} else if $os == 'windows-latest' {
|
} else if $os == 'windows-latest' {
|
||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = if $env.RELEASE_TYPE == 'full' { $'($bin)-($version)-($FULL_NAME)' } else { $'($bin)-($version)-($target)' }
|
||||||
|
|
||||||
print $'(char nl)Download less related stuffs...'; hr-line
|
print $'(char nl)Download less related stuffs...'; hr-line
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
||||||
@ -173,7 +188,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
cargo install cargo-wix --version 0.3.4
|
cargo install cargo-wix --version 0.3.4
|
||||||
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
||||||
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||||
let archive = ($wixRelease | str replace -a '\' '/')
|
let archive = ($wixRelease | str replace --all '\' '/')
|
||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)';
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
||||||
|
|
||||||
@ -185,7 +200,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
let pkg = (ls -f $archive | get name)
|
let pkg = (ls -f $archive | get name)
|
||||||
if not ($pkg | is-empty) {
|
if not ($pkg | is-empty) {
|
||||||
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||||
let archive = ($pkg | get 0 | str replace -a '\' '/')
|
let archive = ($pkg | get 0 | str replace --all '\' '/')
|
||||||
print $'archive: ---> ($archive)'
|
print $'archive: ---> ($archive)'
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
||||||
}
|
}
|
||||||
|
101
.github/workflows/release.yml
vendored
101
.github/workflows/release.yml
vendored
@ -14,8 +14,8 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
all:
|
standard:
|
||||||
name: All
|
name: Std
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -64,10 +64,10 @@ jobs:
|
|||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: '--exclude=nu-cmd-dataframe'
|
target_rustflags: ''
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: '--exclude=nu-cmd-dataframe'
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
@ -80,13 +80,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||||
with:
|
|
||||||
rustflags: ''
|
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.6
|
uses: hustcer/setup-nu@v3.6
|
||||||
with:
|
with:
|
||||||
version: 0.84.0
|
version: 0.85.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -94,6 +92,95 @@ jobs:
|
|||||||
id: nu
|
id: nu
|
||||||
run: nu .github/workflows/release-pkg.nu
|
run: nu .github/workflows/release-pkg.nu
|
||||||
env:
|
env:
|
||||||
|
RELEASE_TYPE: standard
|
||||||
|
OS: ${{ matrix.os }}
|
||||||
|
REF: ${{ github.ref }}
|
||||||
|
TARGET: ${{ matrix.target }}
|
||||||
|
_EXTRA_: ${{ matrix.extra }}
|
||||||
|
TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
|
||||||
|
|
||||||
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
|
- name: Publish Archive
|
||||||
|
uses: softprops/action-gh-release@v0.1.15
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
files: ${{ steps.nu.outputs.archive }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
full:
|
||||||
|
name: Full
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- aarch64-apple-darwin
|
||||||
|
- x86_64-apple-darwin
|
||||||
|
- x86_64-pc-windows-msvc
|
||||||
|
- aarch64-pc-windows-msvc
|
||||||
|
- x86_64-unknown-linux-gnu
|
||||||
|
- x86_64-unknown-linux-musl
|
||||||
|
- aarch64-unknown-linux-gnu
|
||||||
|
extra: ['bin']
|
||||||
|
include:
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
os: macos-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
extra: 'bin'
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
extra: msi
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
extra: 'bin'
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
extra: msi
|
||||||
|
os: windows-latest
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-unknown-linux-gnu
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
- target: aarch64-unknown-linux-gnu
|
||||||
|
os: ubuntu-20.04
|
||||||
|
target_rustflags: '--features=dataframe,extra'
|
||||||
|
|
||||||
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Update Rust Toolchain Target
|
||||||
|
run: |
|
||||||
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
|
- name: Setup Rust toolchain and cache
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
||||||
|
|
||||||
|
- name: Setup Nushell
|
||||||
|
uses: hustcer/setup-nu@v3.6
|
||||||
|
with:
|
||||||
|
version: 0.85.0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Release Nu Binary
|
||||||
|
id: nu
|
||||||
|
run: nu .github/workflows/release-pkg.nu
|
||||||
|
env:
|
||||||
|
RELEASE_TYPE: full
|
||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.16.11
|
uses: crate-ci/typos@v1.16.19
|
||||||
with:
|
with:
|
||||||
config: ./.github/.typos.toml
|
config: ./.github/.typos.toml
|
||||||
|
3
.github/workflows/winget-submission.yml
vendored
3
.github/workflows/winget-submission.yml
vendored
@ -20,6 +20,9 @@ jobs:
|
|||||||
uses: vedantmgoyal2009/winget-releaser@v2
|
uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
with:
|
with:
|
||||||
identifier: Nushell.Nushell
|
identifier: Nushell.Nushell
|
||||||
|
# Exclude all `*-msvc-full.msi` full release files,
|
||||||
|
# and only the default `*msvc.msi` files will be included
|
||||||
|
installers-regex: 'msvc\.msi$'
|
||||||
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
token: ${{ secrets.NUSHELL_PAT }}
|
token: ${{ secrets.NUSHELL_PAT }}
|
||||||
|
@ -46,7 +46,7 @@ cargo build
|
|||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
It is a good practice to cover your changes with a test. Also, try to think about corner cases and various ways how your changes could break. Cover those in the tests as well.
|
It is good practice to cover your changes with a test. Also, try to think about corner cases and various ways how your changes could break. Cover those in the tests as well.
|
||||||
|
|
||||||
Tests can be found in different places:
|
Tests can be found in different places:
|
||||||
* `/tests`
|
* `/tests`
|
||||||
@ -58,7 +58,7 @@ Most of the tests are built upon the `nu-test-support` crate. For testing specif
|
|||||||
|
|
||||||
### Useful Commands
|
### Useful Commands
|
||||||
|
|
||||||
As Nushell is build using a cargo workspace consisting of multiple crates keep in mind that you may need to pass additional flags compared to how you may be used to it from a single crate project.
|
As Nushell is built using a cargo workspace consisting of multiple crates keep in mind that you may need to pass additional flags compared to how you may be used to it from a single crate project.
|
||||||
Read cargo's documentation for more details: https://doc.rust-lang.org/cargo/reference/workspaces.html
|
Read cargo's documentation for more details: https://doc.rust-lang.org/cargo/reference/workspaces.html
|
||||||
|
|
||||||
- Build and run Nushell:
|
- Build and run Nushell:
|
||||||
|
1101
Cargo.lock
generated
1101
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
49
Cargo.toml
49
Cargo.toml
@ -11,7 +11,7 @@ license = "MIT"
|
|||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -46,28 +46,28 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.85.0" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.86.0" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.85.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.86.0" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.85.0" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.86.0" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.85.0" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.86.0" }
|
||||||
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.85.0", features = ["dataframe"], optional = true }
|
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.86.0", features = ["dataframe"], optional = true }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.85.0", optional = true }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.86.0", optional = true }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.85.0" }
|
nu-command = { path = "./crates/nu-command", version = "0.86.0" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.85.0" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.86.0" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.85.0" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.86.0" }
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.85.0" }
|
nu-json = { path = "./crates/nu-json", version = "0.86.0" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.85.0" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.86.0" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.85.0" }
|
nu-path = { path = "./crates/nu-path", version = "0.86.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.85.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.86.0" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.85.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.86.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.86.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.85.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.86.0" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.85.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.86.0" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.85.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.86.0" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.85.0" }
|
nu-std = { path = "./crates/nu-std", version = "0.86.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.85.0" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.86.0" }
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
reedline = { version = "0.24.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
ctrlc = "3.4"
|
ctrlc = "3.4"
|
||||||
@ -95,7 +95,7 @@ nix = { version = "0.27", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.86.0" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
criterion = "0.5"
|
criterion = "0.5"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
@ -164,8 +164,9 @@ bench = false
|
|||||||
# To use a development version of a dependency please use a global override here
|
# To use a development version of a dependency please use a global override here
|
||||||
# changing versions in each sub-crate of the workspace is tedious
|
# changing versions in each sub-crate of the workspace is tedious
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main"}
|
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
|
||||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||||
|
# uu_cp = { git = "https://github.com/uutils/coreutils.git", branch = "main" }
|
||||||
|
|
||||||
# Criterion benchmarking setup
|
# Criterion benchmarking setup
|
||||||
# Run all benchmarks with `cargo bench`
|
# Run all benchmarks with `cargo bench`
|
||||||
|
@ -220,6 +220,7 @@ Please submit an issue or PR to be added to this list.
|
|||||||
- [atuin](https://github.com/ellie/atuin)
|
- [atuin](https://github.com/ellie/atuin)
|
||||||
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
|
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
|
||||||
- [Dorothy](http://github.com/bevry/dorothy)
|
- [Dorothy](http://github.com/bevry/dorothy)
|
||||||
|
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.85.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.85.0" }
|
nu-command = { path = "../nu-command", version = "0.86.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||||
rstest = { version = "0.18.1", default-features = false }
|
rstest = { version = "0.18.1", default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.85.0" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.85.0" }
|
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.85.0" }
|
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.85.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
reedline = { version = "0.24.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.25.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4" }
|
chrono = { default-features = false, features = ["std"], version = "0.4" }
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
@ -36,8 +36,10 @@ log = "0.4"
|
|||||||
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
|
pathdiff = "0.2"
|
||||||
sysinfo = "0.29"
|
sysinfo = "0.29"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
|
uuid = { version = "1.4.1", features = ["v4"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
@ -91,7 +91,7 @@ impl Command for Commandline {
|
|||||||
from_type: "string".to_string(),
|
from_type: "string".to_string(),
|
||||||
span: cmd.span(),
|
span: cmd.span(),
|
||||||
help: Some(format!(
|
help: Some(format!(
|
||||||
r#"string "{cmd_str}" does not represent a valid integer"#
|
r#"string "{cmd_str}" does not represent a valid int"#
|
||||||
)),
|
)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crossterm::execute;
|
||||||
use crossterm::QueueableCommand;
|
use crossterm::QueueableCommand;
|
||||||
use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal};
|
use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
@ -69,6 +70,32 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
|
|
||||||
stdout().flush()?;
|
stdout().flush()?;
|
||||||
terminal::enable_raw_mode()?;
|
terminal::enable_raw_mode()?;
|
||||||
|
|
||||||
|
if config.use_kitty_protocol {
|
||||||
|
if let Ok(false) = crossterm::terminal::supports_keyboard_enhancement() {
|
||||||
|
println!("WARN: The terminal doesn't support use_kitty_protocol config.\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable kitty protocol
|
||||||
|
//
|
||||||
|
// Note that, currently, only the following support this protocol:
|
||||||
|
// * [kitty terminal](https://sw.kovidgoyal.net/kitty/)
|
||||||
|
// * [foot terminal](https://codeberg.org/dnkl/foot/issues/319)
|
||||||
|
// * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html)
|
||||||
|
// * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131)
|
||||||
|
// * [neovim text editor](https://github.com/neovim/neovim/pull/18181)
|
||||||
|
// * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103)
|
||||||
|
// * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138)
|
||||||
|
//
|
||||||
|
// Refer to https://sw.kovidgoyal.net/kitty/keyboard-protocol/ if you're curious.
|
||||||
|
let _ = execute!(
|
||||||
|
stdout(),
|
||||||
|
crossterm::event::PushKeyboardEnhancementFlags(
|
||||||
|
crossterm::event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut stdout = std::io::BufWriter::new(std::io::stderr());
|
let mut stdout = std::io::BufWriter::new(std::io::stderr());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -95,6 +122,14 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
stdout.queue(crossterm::style::Print("\r\n"))?;
|
stdout.queue(crossterm::style::Print("\r\n"))?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.use_kitty_protocol {
|
||||||
|
let _ = execute!(
|
||||||
|
std::io::stdout(),
|
||||||
|
crossterm::event::PopKeyboardEnhancementFlags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
terminal::disable_raw_mode()?;
|
terminal::disable_raw_mode()?;
|
||||||
|
|
||||||
Ok(Value::nothing(Span::unknown()))
|
Ok(Value::nothing(Span::unknown()))
|
||||||
|
@ -234,7 +234,7 @@ pub fn is_passthrough_command(working_set_file_contents: &[(Vec<u8>, usize, usiz
|
|||||||
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
|
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
|
||||||
|
|
||||||
let result = match contents.get(cur_pos..) {
|
let result = match contents.get(cur_pos..) {
|
||||||
Some(contents) => contents.starts_with(b"sudo "),
|
Some(contents) => contents.starts_with(b"sudo ") || contents.starts_with(b"doas "),
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
if result {
|
if result {
|
||||||
|
@ -136,7 +136,9 @@ impl NuCompleter {
|
|||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
let is_passthrough_command = spans
|
let is_passthrough_command = spans
|
||||||
.first()
|
.first()
|
||||||
.filter(|content| content.as_str() == "sudo")
|
.filter(|content| {
|
||||||
|
content.as_str() == "sudo" || content.as_str() == "doas"
|
||||||
|
})
|
||||||
.is_some();
|
.is_some();
|
||||||
// Read the current spam to string
|
// Read the current spam to string
|
||||||
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
||||||
|
160
crates/nu-cli/src/completions/completion_common.rs
Normal file
160
crates/nu-cli/src/completions/completion_common.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
use crate::completions::{matches, CompletionOptions};
|
||||||
|
use nu_path::home_dir;
|
||||||
|
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||||
|
|
||||||
|
fn complete_rec(
|
||||||
|
partial: &[String],
|
||||||
|
cwd: &Path,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
dir: bool,
|
||||||
|
isdir: bool,
|
||||||
|
) -> Vec<PathBuf> {
|
||||||
|
let mut completions = vec![];
|
||||||
|
|
||||||
|
if let Ok(result) = cwd.read_dir() {
|
||||||
|
for entry in result.filter_map(|e| e.ok()) {
|
||||||
|
let entry_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
if !dir || path.is_dir() {
|
||||||
|
match partial.first() {
|
||||||
|
Some(base) if matches(base, &entry_name, options) => {
|
||||||
|
let partial = &partial[1..];
|
||||||
|
if !partial.is_empty() || isdir {
|
||||||
|
completions.extend(complete_rec(partial, &path, options, dir, isdir))
|
||||||
|
} else {
|
||||||
|
completions.push(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => completions.push(path),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completions
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OriginalCwd {
|
||||||
|
None,
|
||||||
|
Home(PathBuf),
|
||||||
|
Some(PathBuf),
|
||||||
|
// referencing a single local file
|
||||||
|
Local(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OriginalCwd {
|
||||||
|
fn apply(&self, p: &Path) -> String {
|
||||||
|
let mut ret = match self {
|
||||||
|
Self::None => p.to_string_lossy().into_owned(),
|
||||||
|
Self::Some(base) => pathdiff::diff_paths(p, base)
|
||||||
|
.unwrap_or(p.to_path_buf())
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
Self::Home(home) => match p.strip_prefix(home) {
|
||||||
|
Ok(suffix) => format!("~{}{}", SEP, suffix.to_string_lossy()),
|
||||||
|
_ => p.to_string_lossy().into_owned(),
|
||||||
|
},
|
||||||
|
Self::Local(base) => Path::new(".")
|
||||||
|
.join(pathdiff::diff_paths(p, base).unwrap_or(p.to_path_buf()))
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if p.is_dir() {
|
||||||
|
ret.push(SEP);
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn surround_remove(partial: &str) -> String {
|
||||||
|
for c in ['`', '"', '\''] {
|
||||||
|
if partial.starts_with(c) {
|
||||||
|
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
||||||
|
return match ret.split(c).collect::<Vec<_>>()[..] {
|
||||||
|
[inside] => inside.to_string(),
|
||||||
|
[inside, outside] if inside.ends_with(is_separator) => format!("{inside}{outside}"),
|
||||||
|
_ => ret.to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partial.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete_item(
|
||||||
|
want_directory: bool,
|
||||||
|
span: nu_protocol::Span,
|
||||||
|
partial: &str,
|
||||||
|
cwd: &str,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
) -> Vec<(nu_protocol::Span, String)> {
|
||||||
|
let partial = surround_remove(partial);
|
||||||
|
let isdir = partial.ends_with(is_separator);
|
||||||
|
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
||||||
|
let mut original_cwd = OriginalCwd::None;
|
||||||
|
let mut components = Path::new(&partial).components().peekable();
|
||||||
|
let mut cwd = match components.peek().cloned() {
|
||||||
|
Some(c @ Component::Prefix(..)) => {
|
||||||
|
// windows only by definition
|
||||||
|
components.next();
|
||||||
|
if let Some(Component::RootDir) = components.peek().cloned() {
|
||||||
|
components.next();
|
||||||
|
};
|
||||||
|
[c, Component::RootDir].iter().collect()
|
||||||
|
}
|
||||||
|
Some(c @ Component::RootDir) => {
|
||||||
|
components.next();
|
||||||
|
PathBuf::from(c.as_os_str())
|
||||||
|
}
|
||||||
|
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
|
||||||
|
components.next();
|
||||||
|
original_cwd = OriginalCwd::Home(home_dir().unwrap_or(cwd_pathbuf.clone()));
|
||||||
|
home_dir().unwrap_or(cwd_pathbuf)
|
||||||
|
}
|
||||||
|
Some(Component::CurDir) => {
|
||||||
|
components.next();
|
||||||
|
original_cwd = match components.peek().cloned() {
|
||||||
|
Some(Component::Normal(_)) | None => OriginalCwd::Local(cwd_pathbuf.clone()),
|
||||||
|
_ => OriginalCwd::Some(cwd_pathbuf.clone()),
|
||||||
|
};
|
||||||
|
cwd_pathbuf
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
original_cwd = OriginalCwd::Some(cwd_pathbuf.clone());
|
||||||
|
cwd_pathbuf
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut partial = vec![];
|
||||||
|
|
||||||
|
for component in components {
|
||||||
|
match component {
|
||||||
|
Component::Prefix(..) => unreachable!(),
|
||||||
|
Component::RootDir => unreachable!(),
|
||||||
|
Component::CurDir => {}
|
||||||
|
Component::ParentDir => {
|
||||||
|
if partial.pop().is_none() {
|
||||||
|
cwd.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component::Normal(c) => partial.push(c.to_string_lossy().into_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_rec(partial.as_slice(), &cwd, options, want_directory, isdir)
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| (span, escape_path(original_cwd.apply(&p), want_directory)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix files or folders with quotes or hashes
|
||||||
|
pub fn escape_path(path: String, dir: bool) -> String {
|
||||||
|
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
|
||||||
|
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
|
||||||
|
if filename_contaminated || dirname_contaminated || path.parse::<f64>().is_ok() {
|
||||||
|
format!("`{path}`")
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,12 @@
|
|||||||
use crate::completions::{matches, Completer, CompletionOptions};
|
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
levenshtein_distance, Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::fs;
|
use std::path::{Path, MAIN_SEPARATOR as SEP};
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{partial_from, prepend_base_dir, SortBy};
|
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DirectoryCompletion {
|
pub struct DirectoryCompletion {
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
@ -33,11 +28,15 @@ impl Completer for DirectoryCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
|
||||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
let output: Vec<_> = directory_completion(span, &partial, &cwd, options)
|
let output: Vec<_> = directory_completion(
|
||||||
|
span,
|
||||||
|
&partial,
|
||||||
|
&self.engine_state.current_work_dir(),
|
||||||
|
options,
|
||||||
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| Suggestion {
|
.map(move |x| Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
@ -111,60 +110,5 @@ pub fn directory_completion(
|
|||||||
cwd: &str,
|
cwd: &str,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<(nu_protocol::Span, String)> {
|
) -> Vec<(nu_protocol::Span, String)> {
|
||||||
let original_input = partial;
|
complete_item(true, span, partial, cwd, options)
|
||||||
|
|
||||||
let (base_dir_name, partial) = partial_from(partial);
|
|
||||||
|
|
||||||
let base_dir = nu_path::expand_path_with(&base_dir_name, cwd);
|
|
||||||
|
|
||||||
// This check is here as base_dir.read_dir() with base_dir == "" will open the current dir
|
|
||||||
// which we don't want in this case (if we did, base_dir would already be ".")
|
|
||||||
if base_dir == Path::new("") {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = base_dir.read_dir() {
|
|
||||||
return result
|
|
||||||
.filter_map(|entry| {
|
|
||||||
entry.ok().and_then(|entry| {
|
|
||||||
if let Ok(metadata) = fs::metadata(entry.path()) {
|
|
||||||
if metadata.is_dir() {
|
|
||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
|
||||||
if matches(&partial, &file_name, options) {
|
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
|
||||||
format!("{base_dir_name}{file_name}")
|
|
||||||
} else {
|
|
||||||
file_name.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
if entry.path().is_dir() {
|
|
||||||
path.push(SEP);
|
|
||||||
file_name.push(SEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix files or folders with quotes or hash
|
|
||||||
if path.contains('\'')
|
|
||||||
|| path.contains('"')
|
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((span, path))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy};
|
||||||
file_path_completion, partial_from, Completer, CompletionOptions, SortBy,
|
|
||||||
};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::sync::Arc;
|
use std::{
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
path::{is_separator, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DotNuCompletion {
|
pub struct DotNuCompletion {
|
||||||
@ -30,9 +30,16 @@ impl Completer for DotNuCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
let prefix_str = String::from_utf8_lossy(&prefix).replace('`', "");
|
||||||
let mut search_dirs: Vec<String> = vec![];
|
let mut search_dirs: Vec<String> = vec![];
|
||||||
let (base_dir, mut partial) = partial_from(&prefix_str);
|
|
||||||
|
// If prefix_str is only a word we want to search in the current dir
|
||||||
|
let (base, partial) = prefix_str
|
||||||
|
.rsplit_once(is_separator)
|
||||||
|
.unwrap_or((".", &prefix_str));
|
||||||
|
let base_dir = base.replace(is_separator, MAIN_SEPARATOR_STR);
|
||||||
|
let mut partial = partial.to_string();
|
||||||
|
// On windows, this standardizes paths to use \
|
||||||
let mut is_current_folder = false;
|
let mut is_current_folder = false;
|
||||||
|
|
||||||
// Fetch the lib dirs
|
// Fetch the lib dirs
|
||||||
@ -58,7 +65,8 @@ impl Completer for DotNuCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if the base_dir is a folder
|
// Check if the base_dir is a folder
|
||||||
if base_dir != format!(".{SEP}") {
|
// rsplit_once removes the separator
|
||||||
|
if base_dir != "." {
|
||||||
// Add the base dir into the directories to be searched
|
// Add the base dir into the directories to be searched
|
||||||
search_dirs.push(base_dir.clone());
|
search_dirs.push(base_dir.clone());
|
||||||
|
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::{completion_common::complete_item, Completer, CompletionOptions, SortBy};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
levenshtein_distance, Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{is_separator, Path};
|
use std::path::{Path, MAIN_SEPARATOR as SEP};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::SortBy;
|
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FileCompletion {
|
pub struct FileCompletion {
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
@ -32,9 +28,13 @@ impl Completer for FileCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
|
||||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
let output: Vec<_> = file_path_completion(
|
||||||
|
span,
|
||||||
|
&prefix,
|
||||||
|
&self.engine_state.current_work_dir(),
|
||||||
|
options,
|
||||||
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| Suggestion {
|
.map(move |x| Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
@ -102,84 +102,13 @@ impl Completer for FileCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_from(input: &str) -> (String, String) {
|
|
||||||
let partial = input.replace('`', "");
|
|
||||||
|
|
||||||
// If partial is only a word we want to search in the current dir
|
|
||||||
let (base, rest) = partial.rsplit_once(is_separator).unwrap_or((".", &partial));
|
|
||||||
// On windows, this standardizes paths to use \
|
|
||||||
let mut base = base.replace(is_separator, &SEP.to_string());
|
|
||||||
|
|
||||||
// rsplit_once removes the separator
|
|
||||||
base.push(SEP);
|
|
||||||
|
|
||||||
(base.to_string(), rest.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_path_completion(
|
pub fn file_path_completion(
|
||||||
span: nu_protocol::Span,
|
span: nu_protocol::Span,
|
||||||
partial: &str,
|
partial: &str,
|
||||||
cwd: &str,
|
cwd: &str,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<(nu_protocol::Span, String)> {
|
) -> Vec<(nu_protocol::Span, String)> {
|
||||||
let original_input = partial;
|
complete_item(false, span, partial, cwd, options)
|
||||||
let (base_dir_name, partial) = partial_from(partial);
|
|
||||||
|
|
||||||
let base_dir = nu_path::expand_path_with(&base_dir_name, cwd);
|
|
||||||
// This check is here as base_dir.read_dir() with base_dir == "" will open the current dir
|
|
||||||
// which we don't want in this case (if we did, base_dir would already be ".")
|
|
||||||
if base_dir == Path::new("") {
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(result) = base_dir.read_dir() {
|
|
||||||
return result
|
|
||||||
.filter_map(|entry| {
|
|
||||||
entry.ok().and_then(|entry| {
|
|
||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
|
||||||
if matches(&partial, &file_name, options) {
|
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
|
||||||
format!("{base_dir_name}{file_name}")
|
|
||||||
} else {
|
|
||||||
file_name.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
if entry.path().is_dir() {
|
|
||||||
path.push(SEP);
|
|
||||||
file_name.push(SEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix files or folders with quotes or hashes
|
|
||||||
if path.contains('\'')
|
|
||||||
|| path.contains('"')
|
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
|| path.contains('(')
|
|
||||||
|| path.contains(')')
|
|
||||||
|| path.starts_with('0')
|
|
||||||
|| path.starts_with('1')
|
|
||||||
|| path.starts_with('2')
|
|
||||||
|| path.starts_with('3')
|
|
||||||
|| path.starts_with('4')
|
|
||||||
|| path.starts_with('5')
|
|
||||||
|| path.starts_with('6')
|
|
||||||
|| path.starts_with('7')
|
|
||||||
|| path.starts_with('8')
|
|
||||||
|| path.starts_with('9')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((span, path))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec::new()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
||||||
@ -192,23 +121,3 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
|||||||
|
|
||||||
options.match_algorithm.matches_str(from, partial)
|
options.match_algorithm.matches_str(from, partial)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the base_dir should be prepended to the file path
|
|
||||||
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
|
||||||
if base_dir == format!(".{SEP}") {
|
|
||||||
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
|
||||||
// input already includes a local folder prefix.
|
|
||||||
let manually_entered = {
|
|
||||||
let mut chars = input.chars();
|
|
||||||
let first_char = chars.next();
|
|
||||||
let second_char = chars.next();
|
|
||||||
|
|
||||||
first_char == Some('.') && second_char.map(is_separator).unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
manually_entered
|
|
||||||
} else {
|
|
||||||
// always prepend the base dir if it is a subfolder
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod base;
|
mod base;
|
||||||
mod command_completions;
|
mod command_completions;
|
||||||
mod completer;
|
mod completer;
|
||||||
|
mod completion_common;
|
||||||
mod completion_options;
|
mod completion_options;
|
||||||
mod custom_completions;
|
mod custom_completions;
|
||||||
mod directory_completions;
|
mod directory_completions;
|
||||||
@ -16,8 +17,6 @@ pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
|
|||||||
pub use custom_completions::CustomCompletion;
|
pub use custom_completions::CustomCompletion;
|
||||||
pub use directory_completions::DirectoryCompletion;
|
pub use directory_completions::DirectoryCompletion;
|
||||||
pub use dotnu_completions::DotNuCompletion;
|
pub use dotnu_completions::DotNuCompletion;
|
||||||
pub use file_completions::{
|
pub use file_completions::{file_path_completion, matches, FileCompletion};
|
||||||
file_path_completion, matches, partial_from, prepend_base_dir, FileCompletion,
|
|
||||||
};
|
|
||||||
pub use flag_completions::FlagCompletion;
|
pub use flag_completions::FlagCompletion;
|
||||||
pub use variable_completions::VariableCompletion;
|
pub use variable_completions::VariableCompletion;
|
||||||
|
@ -2,7 +2,7 @@ use crate::util::eval_source;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_engine::eval_block_with_early_return;
|
use nu_engine::eval_block;
|
||||||
use nu_engine::{convert_env_values, current_dir};
|
use nu_engine::{convert_env_values, current_dir};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
@ -126,14 +126,22 @@ pub fn evaluate_file(
|
|||||||
if engine_state.find_decl(b"main", &[]).is_some() {
|
if engine_state.find_decl(b"main", &[]).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
let args = format!("main {}", args.join(" "));
|
||||||
|
|
||||||
let pipeline_data = eval_block_with_early_return(
|
let pipeline_data = eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
)
|
);
|
||||||
|
let pipeline_data = match pipeline_data {
|
||||||
|
Err(ShellError::Return(_, _)) => {
|
||||||
|
// allows early exists before `main` is run.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
x => x,
|
||||||
|
}
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
|
@ -57,7 +57,7 @@ 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, |v| {
|
long_desc.push_str(&get_flags_section(Some(&*self.0.clone()), sig, |v| {
|
||||||
v.into_string_parsable(", ", &self.0.config)
|
v.into_string_parsable(", ", &self.0.config)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ use nu_protocol::{
|
|||||||
Config, PipelineData, Value,
|
Config, PipelineData, Value,
|
||||||
};
|
};
|
||||||
use reedline::Prompt;
|
use reedline::Prompt;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
// Name of environment variable where the prompt could be stored
|
// Name of environment variable where the prompt could be stored
|
||||||
pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
|
pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
|
||||||
@ -15,6 +17,15 @@ pub(crate) const PROMPT_INDICATOR: &str = "PROMPT_INDICATOR";
|
|||||||
pub(crate) const PROMPT_INDICATOR_VI_INSERT: &str = "PROMPT_INDICATOR_VI_INSERT";
|
pub(crate) const PROMPT_INDICATOR_VI_INSERT: &str = "PROMPT_INDICATOR_VI_INSERT";
|
||||||
pub(crate) const PROMPT_INDICATOR_VI_NORMAL: &str = "PROMPT_INDICATOR_VI_NORMAL";
|
pub(crate) const PROMPT_INDICATOR_VI_NORMAL: &str = "PROMPT_INDICATOR_VI_NORMAL";
|
||||||
pub(crate) const PROMPT_MULTILINE_INDICATOR: &str = "PROMPT_MULTILINE_INDICATOR";
|
pub(crate) const PROMPT_MULTILINE_INDICATOR: &str = "PROMPT_MULTILINE_INDICATOR";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_COMMAND: &str = "TRANSIENT_PROMPT_COMMAND";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_COMMAND_RIGHT: &str = "TRANSIENT_PROMPT_COMMAND_RIGHT";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_INDICATOR: &str = "TRANSIENT_PROMPT_INDICATOR";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_INSERT: &str =
|
||||||
|
"TRANSIENT_PROMPT_INDICATOR_VI_INSERT";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_NORMAL: &str =
|
||||||
|
"TRANSIENT_PROMPT_INDICATOR_VI_NORMAL";
|
||||||
|
pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
|
||||||
|
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
|
||||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
||||||
@ -145,3 +156,119 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
|
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TransientPrompt {
|
||||||
|
engine_state: Arc<EngineState>,
|
||||||
|
stack: Stack,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try getting `$env.TRANSIENT_PROMPT_<X>`, and get `$env.PROMPT_<X>` if that fails
|
||||||
|
fn get_transient_prompt_string(
|
||||||
|
transient_prompt: &str,
|
||||||
|
prompt: &str,
|
||||||
|
config: &Config,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
) -> Option<String> {
|
||||||
|
get_prompt_string(transient_prompt, config, engine_state, stack)
|
||||||
|
.or_else(|| get_prompt_string(prompt, config, engine_state, stack))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prompt for TransientPrompt {
|
||||||
|
fn render_prompt_left(&self) -> Cow<str> {
|
||||||
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
let config = &self.engine_state.get_config().clone();
|
||||||
|
let mut stack = self.stack.clone();
|
||||||
|
nu_prompt.update_prompt_left(get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_COMMAND,
|
||||||
|
PROMPT_COMMAND,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
));
|
||||||
|
nu_prompt.render_prompt_left().to_string().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_right(&self) -> Cow<str> {
|
||||||
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
let config = &self.engine_state.get_config().clone();
|
||||||
|
let mut stack = self.stack.clone();
|
||||||
|
nu_prompt.update_prompt_right(
|
||||||
|
get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_COMMAND_RIGHT,
|
||||||
|
PROMPT_COMMAND_RIGHT,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
),
|
||||||
|
config.render_right_prompt_on_last_line,
|
||||||
|
);
|
||||||
|
nu_prompt.render_prompt_right().to_string().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_indicator(&self, prompt_mode: reedline::PromptEditMode) -> Cow<str> {
|
||||||
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
let config = &self.engine_state.get_config().clone();
|
||||||
|
let mut stack = self.stack.clone();
|
||||||
|
nu_prompt.update_prompt_indicator(get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_INDICATOR,
|
||||||
|
PROMPT_INDICATOR,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
));
|
||||||
|
nu_prompt.update_prompt_vi_insert(get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
|
||||||
|
PROMPT_INDICATOR_VI_INSERT,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
));
|
||||||
|
nu_prompt.update_prompt_vi_normal(get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
|
||||||
|
PROMPT_INDICATOR_VI_NORMAL,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
));
|
||||||
|
nu_prompt
|
||||||
|
.render_prompt_indicator(prompt_mode)
|
||||||
|
.to_string()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||||
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
let config = &self.engine_state.get_config().clone();
|
||||||
|
let mut stack = self.stack.clone();
|
||||||
|
nu_prompt.update_prompt_multiline(get_transient_prompt_string(
|
||||||
|
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
|
||||||
|
PROMPT_MULTILINE_INDICATOR,
|
||||||
|
config,
|
||||||
|
&self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
));
|
||||||
|
nu_prompt
|
||||||
|
.render_prompt_multiline_indicator()
|
||||||
|
.to_string()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_history_search_indicator(
|
||||||
|
&self,
|
||||||
|
history_search: reedline::PromptHistorySearch,
|
||||||
|
) -> Cow<str> {
|
||||||
|
NushellPrompt::new()
|
||||||
|
.render_prompt_history_search_indicator(history_search)
|
||||||
|
.to_string()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct the transient prompt
|
||||||
|
pub(crate) fn transient_prompt(engine_state: Arc<EngineState>, stack: &Stack) -> Box<dyn Prompt> {
|
||||||
|
Box::new(TransientPrompt {
|
||||||
|
engine_state,
|
||||||
|
stack: stack.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -8,8 +8,8 @@ use crate::{
|
|||||||
use crossterm::cursor::SetCursorStyle;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use miette::{ErrReport, IntoDiagnostic, Result};
|
use miette::{ErrReport, IntoDiagnostic, Result};
|
||||||
use nu_cmd_base::hook::eval_hook;
|
|
||||||
use nu_cmd_base::util::get_guaranteed_cwd;
|
use nu_cmd_base::util::get_guaranteed_cwd;
|
||||||
|
use nu_cmd_base::{hook::eval_hook, util::get_editor};
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::convert_env_values;
|
use nu_engine::convert_env_values;
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
@ -26,6 +26,7 @@ use reedline::{
|
|||||||
SqliteBackedHistory, Vi,
|
SqliteBackedHistory, Vi,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
env::temp_dir,
|
||||||
io::{self, IsTerminal, Write},
|
io::{self, IsTerminal, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
@ -94,6 +95,7 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let mut start_time = std::time::Instant::now();
|
let mut start_time = std::time::Instant::now();
|
||||||
let mut line_editor = Reedline::create();
|
let mut line_editor = Reedline::create();
|
||||||
|
let temp_file = temp_dir().join(format!("{}.nu", uuid::Uuid::new_v4()));
|
||||||
|
|
||||||
// Now that reedline is created, get the history session id and store it in engine_state
|
// Now that reedline is created, get the history session id and store it in engine_state
|
||||||
store_history_id_in_engine(engine_state, &line_editor);
|
store_history_id_in_engine(engine_state, &line_editor);
|
||||||
@ -180,6 +182,14 @@ pub fn evaluate_repl(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if engine_state.get_config().use_kitty_protocol {
|
||||||
|
if line_editor.can_use_kitty_protocol() {
|
||||||
|
line_editor.enable_kitty_protocol();
|
||||||
|
} else {
|
||||||
|
warn!("Terminal doesn't support use_kitty_protocol config");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let loop_start_time = std::time::Instant::now();
|
let loop_start_time = std::time::Instant::now();
|
||||||
|
|
||||||
@ -271,7 +281,11 @@ pub fn evaluate_repl(
|
|||||||
.with_quick_completions(config.quick_completions)
|
.with_quick_completions(config.quick_completions)
|
||||||
.with_partial_completions(config.partial_completions)
|
.with_partial_completions(config.partial_completions)
|
||||||
.with_ansi_colors(config.use_ansi_coloring)
|
.with_ansi_colors(config.use_ansi_coloring)
|
||||||
.with_cursor_config(cursor_config);
|
.with_cursor_config(cursor_config)
|
||||||
|
.with_transient_prompt(prompt_update::transient_prompt(
|
||||||
|
engine_reference.clone(),
|
||||||
|
stack,
|
||||||
|
));
|
||||||
perf(
|
perf(
|
||||||
"reedline builder",
|
"reedline builder",
|
||||||
start_time,
|
start_time,
|
||||||
@ -318,23 +332,17 @@ pub fn evaluate_repl(
|
|||||||
);
|
);
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
let buffer_editor = if !config.buffer_editor.is_empty() {
|
let buffer_editor = get_editor(engine_state, stack, Span::unknown());
|
||||||
Some(config.buffer_editor.clone())
|
|
||||||
} else {
|
|
||||||
stack
|
|
||||||
.get_env_var(engine_state, "EDITOR")
|
|
||||||
.map(|v| v.as_string().unwrap_or_default())
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
.or_else(|| {
|
|
||||||
stack
|
|
||||||
.get_env_var(engine_state, "VISUAL")
|
|
||||||
.map(|v| v.as_string().unwrap_or_default())
|
|
||||||
.filter(|v| !v.is_empty())
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
line_editor = if let Some(buffer_editor) = buffer_editor {
|
line_editor = if let Ok((cmd, args)) = buffer_editor {
|
||||||
line_editor.with_buffer_editor(buffer_editor, "nu".into())
|
let mut command = std::process::Command::new(&cmd);
|
||||||
|
command.args(args).envs(
|
||||||
|
engine_state
|
||||||
|
.render_env_vars()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(k, v)| v.as_string().ok().map(|v| (k, v))),
|
||||||
|
);
|
||||||
|
line_editor.with_buffer_editor(command, temp_file.clone())
|
||||||
} else {
|
} else {
|
||||||
line_editor
|
line_editor
|
||||||
};
|
};
|
||||||
@ -508,7 +516,10 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
&ShellError::DirectoryNotFound(tokens.0[0].span, None),
|
&ShellError::DirectoryNotFound(
|
||||||
|
tokens.0[0].span,
|
||||||
|
path.to_string_lossy().to_string(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let path = nu_path::canonicalize_with(path, &cwd)
|
let path = nu_path::canonicalize_with(path, &cwd)
|
||||||
@ -809,16 +820,29 @@ fn looks_like_path(orig: &str) -> bool {
|
|||||||
|| orig.starts_with('~')
|
|| orig.starts_with('~')
|
||||||
|| orig.starts_with('/')
|
|| orig.starts_with('/')
|
||||||
|| orig.starts_with('\\')
|
|| orig.starts_with('\\')
|
||||||
|
|| orig.ends_with(std::path::MAIN_SEPARATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
#[test]
|
#[test]
|
||||||
fn looks_like_path_windows_drive_path_works() {
|
fn looks_like_path_windows_drive_path_works() {
|
||||||
let on_windows = cfg!(windows);
|
assert!(looks_like_path("C:"));
|
||||||
assert_eq!(looks_like_path("C:"), on_windows);
|
assert!(looks_like_path("D:\\"));
|
||||||
assert_eq!(looks_like_path("D:\\"), on_windows);
|
assert!(looks_like_path("E:/"));
|
||||||
assert_eq!(looks_like_path("E:/"), on_windows);
|
assert!(looks_like_path("F:\\some_dir"));
|
||||||
assert_eq!(looks_like_path("F:\\some_dir"), on_windows);
|
assert!(looks_like_path("G:/some_dir"));
|
||||||
assert_eq!(looks_like_path("G:/some_dir"), on_windows);
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn trailing_slash_looks_like_path() {
|
||||||
|
assert!(looks_like_path("foo\\"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[test]
|
||||||
|
fn trailing_slash_looks_like_path() {
|
||||||
|
assert!(looks_like_path("foo/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
pub mod support;
|
pub mod support;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_cli::NuCompleter;
|
use nu_cli::NuCompleter;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use support::{completions_helpers::new_quote_engine, file, folder, match_suggestions, new_engine};
|
use support::{
|
||||||
|
completions_helpers::{new_partial_engine, new_quote_engine},
|
||||||
|
file, folder, match_suggestions, new_engine,
|
||||||
|
};
|
||||||
|
|
||||||
#[fixture]
|
#[fixture]
|
||||||
fn completer() -> NuCompleter {
|
fn completer() -> NuCompleter {
|
||||||
@ -201,6 +206,87 @@ fn file_completions() {
|
|||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(expected_paths, suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, engine, stack) = new_partial_engine();
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Test completions for a folder's name
|
||||||
|
let target_dir = format!("cd {}", file(dir.join("pa")));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
folder(dir.join("partial_a")),
|
||||||
|
folder(dir.join("partial_b")),
|
||||||
|
folder(dir.join("partial_c")),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
// Test completions for the files whose name begin with "h"
|
||||||
|
// and are present under directories whose names begin with "pa"
|
||||||
|
let dir_str = file(dir.join("pa").join("h"));
|
||||||
|
let target_dir = format!("cp {dir_str}");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
file(dir.join("partial_a").join("hello")),
|
||||||
|
file(dir.join("partial_a").join("hola")),
|
||||||
|
file(dir.join("partial_b").join("hello_b")),
|
||||||
|
file(dir.join("partial_b").join("hi_b")),
|
||||||
|
file(dir.join("partial_c").join("hello_c")),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
|
let dir_str = folder(dir.join("pa"));
|
||||||
|
let target_dir = format!("ls {dir_str}");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
file(dir.join("partial_a").join("anotherfile")),
|
||||||
|
file(dir.join("partial_a").join("hello")),
|
||||||
|
file(dir.join("partial_a").join("hola")),
|
||||||
|
file(dir.join("partial_b").join("hello_b")),
|
||||||
|
file(dir.join("partial_b").join("hi_b")),
|
||||||
|
file(dir.join("partial_c").join("hello_c")),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
// Test completion for a single file
|
||||||
|
let dir_str = file(dir.join("fi").join("so"));
|
||||||
|
let target_dir = format!("rm {dir_str}");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
// Test completion where there is a sneaky `..` in the path
|
||||||
|
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
||||||
|
let target_dir = format!("rm {dir_str}");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_ls_with_filecompletion() {
|
fn command_ls_with_filecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
@ -445,6 +531,18 @@ fn file_completion_quoted() {
|
|||||||
"`te#st.txt`".to_string(),
|
"`te#st.txt`".to_string(),
|
||||||
"`te'st.txt`".to_string(),
|
"`te'st.txt`".to_string(),
|
||||||
"`te(st).txt`".to_string(),
|
"`te(st).txt`".to_string(),
|
||||||
|
format!("`{}`", folder("test dir".into())),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
let dir: PathBuf = "test dir".into();
|
||||||
|
let target_dir = format!("open '{}'", folder(dir.clone()));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
format!("`{}`", file(dir.join("double quote"))),
|
||||||
|
format!("`{}`", file(dir.join("single quote"))),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
@ -109,6 +109,42 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
|||||||
(dir, dir_str, engine_state, stack)
|
(dir, dir_str, engine_state, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
|
// Target folder inside assets
|
||||||
|
let dir = fs::fixtures().join("partial_completions");
|
||||||
|
let mut dir_str = dir
|
||||||
|
.clone()
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.unwrap_or_default();
|
||||||
|
dir_str.push(SEP);
|
||||||
|
|
||||||
|
// Create a new engine with default context
|
||||||
|
let mut engine_state = create_default_context();
|
||||||
|
|
||||||
|
// New stack
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
// Add pwd as env var
|
||||||
|
stack.add_env_var(
|
||||||
|
"PWD".to_string(),
|
||||||
|
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
||||||
|
);
|
||||||
|
stack.add_env_var(
|
||||||
|
"TEST".to_string(),
|
||||||
|
Value::string(
|
||||||
|
"NUSHELL".to_string(),
|
||||||
|
nu_protocol::Span::new(0, dir_str.len()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge environment into the permanent state
|
||||||
|
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
||||||
|
assert!(merge_result.is_ok());
|
||||||
|
|
||||||
|
(dir, dir_str, engine_state, stack)
|
||||||
|
}
|
||||||
|
|
||||||
// match a list of suggestions with the expected values
|
// match a list of suggestions with the expected values
|
||||||
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
||||||
let expected_len = expected.len();
|
let expected_len = expected.len();
|
||||||
|
@ -5,14 +5,14 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-base"
|
name = "nu-cmd-base"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.85.0" }
|
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||||
nu-protocol = { version = "0.85.0", path = "../nu-protocol" }
|
nu-protocol = { version = "0.86.0", path = "../nu-protocol" }
|
||||||
indexmap = { version = "2.0" }
|
indexmap = { version = "2.0" }
|
||||||
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
||||||
|
@ -55,3 +55,72 @@ pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
|||||||
|
|
||||||
Ok((start, end))
|
Ok((start, end))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HELP_MSG: &str = "Nushell's config file can be found with the command: $nu.config-path. \
|
||||||
|
For more help: (https://nushell.sh/book/configuration.html#configurations-with-built-in-commands)";
|
||||||
|
|
||||||
|
fn get_editor_commandline(
|
||||||
|
value: &Value,
|
||||||
|
var_name: &str,
|
||||||
|
) -> Result<(String, Vec<String>), ShellError> {
|
||||||
|
match value {
|
||||||
|
Value::String { val, .. } if !val.is_empty() => Ok((val.to_string(), Vec::new())),
|
||||||
|
Value::List { vals, .. } if !vals.is_empty() => {
|
||||||
|
let mut editor_cmd = vals.iter().map(|l| l.as_string());
|
||||||
|
match editor_cmd.next().transpose()? {
|
||||||
|
Some(editor) if !editor.is_empty() => {
|
||||||
|
let params = editor_cmd.collect::<Result<_, ShellError>>()?;
|
||||||
|
Ok((editor, params))
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::GenericError(
|
||||||
|
"Editor executable is missing".into(),
|
||||||
|
"Set the first element to an executable".into(),
|
||||||
|
Some(value.span()),
|
||||||
|
Some(HELP_MSG.into()),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::String { .. } | Value::List { .. } => Err(ShellError::GenericError(
|
||||||
|
format!("{var_name} should be a non-empty string or list<String>"),
|
||||||
|
"Specify an executable here".into(),
|
||||||
|
Some(value.span()),
|
||||||
|
Some(HELP_MSG.into()),
|
||||||
|
vec![],
|
||||||
|
)),
|
||||||
|
x => Err(ShellError::CantConvert {
|
||||||
|
to_type: "string or list<string>".into(),
|
||||||
|
from_type: x.get_type().to_string(),
|
||||||
|
span: value.span(),
|
||||||
|
help: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_editor(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<(String, Vec<String>), ShellError> {
|
||||||
|
let config = engine_state.get_config();
|
||||||
|
let env_vars = stack.get_env_vars(engine_state);
|
||||||
|
|
||||||
|
if let Ok(buff_editor) =
|
||||||
|
get_editor_commandline(&config.buffer_editor, "$env.config.buffer_editor")
|
||||||
|
{
|
||||||
|
Ok(buff_editor)
|
||||||
|
} else if let Some(value) = env_vars.get("EDITOR") {
|
||||||
|
get_editor_commandline(value, "$env.EDITOR")
|
||||||
|
} else if let Some(value) = env_vars.get("VISUAL") {
|
||||||
|
get_editor_commandline(value, "$env.VISUAL")
|
||||||
|
} else {
|
||||||
|
Err(ShellError::GenericError(
|
||||||
|
"No editor configured".into(),
|
||||||
|
"Please specify one via `$env.config.buffer_editor` or `$env.EDITOR`/`$env.VISUAL`"
|
||||||
|
.into(),
|
||||||
|
Some(span),
|
||||||
|
Some(HELP_MSG.into()),
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-dataframe"
|
name = "nu-cmd-dataframe"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
# 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,9 +13,9 @@ version = "0.85.0"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
|
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
|
||||||
@ -24,7 +24,7 @@ indexmap = { version = "2.0" }
|
|||||||
num = { version = "0.4", optional = true }
|
num = { version = "0.4", optional = true }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sqlparser = { version = "0.36.1", optional = true }
|
sqlparser = { version = "0.36.1", optional = true }
|
||||||
polars-io = { version = "0.32", features = ["avro"], optional = true }
|
polars-io = { version = "0.33", features = ["avro"], optional = true }
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
features = [
|
features = [
|
||||||
@ -38,7 +38,7 @@ features = [
|
|||||||
"dtype-categorical",
|
"dtype-categorical",
|
||||||
"dtype-datetime",
|
"dtype-datetime",
|
||||||
"dtype-struct",
|
"dtype-struct",
|
||||||
"dynamic_groupby",
|
"dynamic_group_by",
|
||||||
"ipc",
|
"ipc",
|
||||||
"is_in",
|
"is_in",
|
||||||
"json",
|
"json",
|
||||||
@ -54,12 +54,12 @@ features = [
|
|||||||
"to_dummies",
|
"to_dummies",
|
||||||
]
|
]
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.32"
|
version = "0.33"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dataframe = ["num", "polars", "polars-io", "sqlparser"]
|
dataframe = ["num", "polars", "polars-io", "sqlparser"]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.85.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||||
|
@ -52,12 +52,13 @@ impl Command for SampleDF {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Sample rows from dataframe",
|
description: "Sample rows from dataframe",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr sample -n 1",
|
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr sample --n-rows 1",
|
||||||
result: None, // No expected value because sampling is random
|
result: None, // No expected value because sampling is random
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shows sample row using fraction and replace",
|
description: "Shows sample row using fraction and replace",
|
||||||
example: "[[a b]; [1 2] [3 4] [5 6]] | dfr into-df | dfr sample -f 0.5 -e",
|
example:
|
||||||
|
"[[a b]; [1 2] [3 4] [5 6]] | dfr into-df | dfr sample --fraction 0.5 --replace",
|
||||||
result: None, // No expected value because sampling is random
|
result: None, // No expected value because sampling is random
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -147,7 +147,7 @@ impl SQLContext {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(agg_pj, (proj_p, expr))| (expr.clone(), (proj_p, agg_pj + group_by.len())))
|
.map(|(agg_pj, (proj_p, expr))| (expr.clone(), (proj_p, agg_pj + group_by.len())))
|
||||||
.unzip();
|
.unzip();
|
||||||
let agg_df = df.groupby(group_by).agg(agg_projection);
|
let agg_df = df.group_by(group_by).agg(agg_projection);
|
||||||
let mut final_proj_pos = groupby_pos
|
let mut final_proj_pos = groupby_pos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(agg_proj_pos)
|
.chain(agg_proj_pos)
|
||||||
|
@ -45,7 +45,7 @@ impl Command for ToCSV {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Saves dataframe to CSV file using other delimiter",
|
description: "Saves dataframe to CSV file using other delimiter",
|
||||||
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv -d '|'",
|
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv --delimiter '|'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -61,7 +61,7 @@ impl Command for ToNu {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shows tail rows from dataframe",
|
description: "Shows tail rows from dataframe",
|
||||||
example: "[[a b]; [1 2] [5 6] [3 4]] | dfr into-df | dfr into-nu -t -n 1",
|
example: "[[a b]; [1 2] [5 6] [3 4]] | dfr into-df | dfr into-nu --tail --rows 1",
|
||||||
result: Some(Value::list(vec![rec_3], Span::test_data())),
|
result: Some(Value::list(vec![rec_3], Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -131,7 +131,7 @@ impl Command for ToLazyGroupBy {
|
|||||||
let group_by = NuLazyGroupBy {
|
let group_by = NuLazyGroupBy {
|
||||||
schema: lazy.schema.clone(),
|
schema: lazy.schema.clone(),
|
||||||
from_eager: lazy.from_eager,
|
from_eager: lazy.from_eager,
|
||||||
group_by: Some(lazy.into_polars().groupby(&expressions)),
|
group_by: Some(lazy.into_polars().group_by(&expressions)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(PipelineData::Value(group_by.into_value(call.head), None))
|
Ok(PipelineData::Value(group_by.into_value(call.head), None))
|
||||||
|
@ -2,6 +2,7 @@ mod eager;
|
|||||||
mod expressions;
|
mod expressions;
|
||||||
mod lazy;
|
mod lazy;
|
||||||
mod series;
|
mod series;
|
||||||
|
mod stub;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod values;
|
mod values;
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|||||||
pub fn add_dataframe_context(mut engine_state: EngineState) -> EngineState {
|
pub fn add_dataframe_context(mut engine_state: EngineState) -> EngineState {
|
||||||
let delta = {
|
let delta = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
working_set.add_decl(Box::new(stub::Dfr));
|
||||||
add_series_decls(&mut working_set);
|
add_series_decls(&mut working_set);
|
||||||
add_eager_decls(&mut working_set);
|
add_eager_decls(&mut working_set);
|
||||||
add_expressions(&mut working_set);
|
add_expressions(&mut working_set);
|
||||||
|
@ -148,7 +148,7 @@ fn command(
|
|||||||
TimeUnit::Nanoseconds,
|
TimeUnit::Nanoseconds,
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
None,
|
&Default::default(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
casted.as_datetime(
|
casted.as_datetime(
|
||||||
@ -157,7 +157,7 @@ fn command(
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
None,
|
&Default::default(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetDay {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns day from a date",
|
description: "Returns day from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-day"#,
|
$df | dfr get-day"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetHour {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns hour from a date",
|
description: "Returns hour from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-hour"#,
|
$df | dfr get-hour"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetMinute {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns minute from a date",
|
description: "Returns minute from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-minute"#,
|
$df | dfr get-minute"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetMonth {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns month from a date",
|
description: "Returns month from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-month"#,
|
$df | dfr get-month"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetNanosecond {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns nanosecond from a date",
|
description: "Returns nanosecond from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-nanosecond"#,
|
$df | dfr get-nanosecond"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetOrdinal {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns ordinal from a date",
|
description: "Returns ordinal from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-ordinal"#,
|
$df | dfr get-ordinal"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetSecond {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns second from a date",
|
description: "Returns second from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-second"#,
|
$df | dfr get-second"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetWeek {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns week from a date",
|
description: "Returns week from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-week"#,
|
$df | dfr get-week"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetWeekDay {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns weekday from a date",
|
description: "Returns weekday from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-weekday"#,
|
$df | dfr get-weekday"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -31,7 +31,7 @@ impl Command for GetYear {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Returns year from a date",
|
description: "Returns year from a date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr get-year"#,
|
$df | dfr get-year"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
@ -61,7 +61,7 @@ impl Command for ArgSort {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns indexes for a sorted series",
|
description: "Returns indexes for a sorted series",
|
||||||
example: "[1 2 2 3 3] | dfr into-df | dfr arg-sort -r",
|
example: "[1 2 2 3 3] | dfr into-df | dfr arg-sort --reverse",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"arg_sort".to_string(),
|
"arg_sort".to_string(),
|
||||||
|
@ -41,7 +41,7 @@ impl Command for SetWithIndex {
|
|||||||
description: "Set value in selected rows from series",
|
description: "Set value in selected rows from series",
|
||||||
example: r#"let series = ([4 1 5 2 4 3] | dfr into-df);
|
example: r#"let series = ([4 1 5 2 4 3] | dfr into-df);
|
||||||
let indices = ([0 2] | dfr into-df);
|
let indices = ([0 2] | dfr into-df);
|
||||||
$series | dfr set-with-idx 6 -i $indices"#,
|
$series | dfr set-with-idx 6 --indices $indices"#,
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"0".to_string(),
|
"0".to_string(),
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
|||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::IntoSeries;
|
use polars::prelude::{is_in, IntoSeries};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IsIn;
|
pub struct IsIn;
|
||||||
@ -71,16 +71,14 @@ fn command(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
let df = NuDataFrame::try_from_pipeline(input, call.head)?.as_series(call.head)?;
|
||||||
|
|
||||||
let other_value: Value = call.req(engine_state, stack, 0)?;
|
let other_value: Value = call.req(engine_state, stack, 0)?;
|
||||||
let other_span = other_value.span();
|
let other_span = other_value.span();
|
||||||
let other_df = NuDataFrame::try_from_value(other_value)?;
|
let other_df = NuDataFrame::try_from_value(other_value)?;
|
||||||
let other = other_df.as_series(other_span)?;
|
let other = other_df.as_series(other_span)?;
|
||||||
|
|
||||||
let mut res = df
|
let mut res = is_in(&df, &other)
|
||||||
.as_series(call.head)?
|
|
||||||
.is_in(&other)
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error finding in other".into(),
|
"Error finding in other".into(),
|
||||||
|
@ -44,7 +44,7 @@ impl Command for Replace {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Replaces string",
|
description: "Replaces string",
|
||||||
example: "[abc abc abc] | dfr into-df | dfr replace -p ab -r AB",
|
example: "[abc abc abc] | dfr into-df | dfr replace --pattern ab --replace AB",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"0".to_string(),
|
"0".to_string(),
|
||||||
|
@ -44,7 +44,7 @@ impl Command for ReplaceAll {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Replaces string",
|
description: "Replaces string",
|
||||||
example: "[abac abac abac] | dfr into-df | dfr replace-all -p a -r A",
|
example: "[abac abac abac] | dfr into-df | dfr replace-all --pattern a --replace A",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"0".to_string(),
|
"0".to_string(),
|
||||||
|
@ -34,7 +34,7 @@ impl Command for StrSlice {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Creates slices from the strings",
|
description: "Creates slices from the strings",
|
||||||
example: "[abcded abc321 abc123] | dfr into-df | dfr str-slice 1 -l 2",
|
example: "[abcded abc321 abc123] | dfr into-df | dfr str-slice 1 --length 2",
|
||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"0".to_string(),
|
"0".to_string(),
|
||||||
|
@ -33,7 +33,7 @@ impl Command for StrFTime {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Formats date",
|
description: "Formats date",
|
||||||
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime -z 'UTC');
|
example: r#"let dt = ('2020-08-04T16:39:18+00:00' | into datetime --timezone 'UTC');
|
||||||
let df = ([$dt $dt] | dfr into-df);
|
let df = ([$dt $dt] | dfr into-df);
|
||||||
$df | dfr strftime "%Y/%m/%d""#,
|
$df | dfr strftime "%Y/%m/%d""#,
|
||||||
result: Some(
|
result: Some(
|
||||||
|
47
crates/nu-cmd-dataframe/src/dataframe/stub.rs
Normal file
47
crates/nu-cmd-dataframe/src/dataframe/stub.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use nu_engine::get_full_help;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Dfr;
|
||||||
|
|
||||||
|
impl Command for Dfr {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"dfr"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Operate with data in a dataframe format."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("dfr")
|
||||||
|
.category(Category::Custom("dataframe".into()))
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(Value::string(
|
||||||
|
get_full_help(
|
||||||
|
&Dfr.signature(),
|
||||||
|
&Dfr.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
|
call.head,
|
||||||
|
)
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-extra"
|
name = "nu-cmd-extra"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
# 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,22 +13,22 @@ version = "0.85.0"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.85.0" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.85.0" }
|
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
Inflector = "0.11"
|
heck = "0.4.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
ahash = "0.8.3"
|
ahash = "0.8.3"
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
fancy-regex = "0.11.0"
|
fancy-regex = "0.11.0"
|
||||||
rust-embed = "8.0.0"
|
rust-embed = "8.0.0"
|
||||||
serde = "1.0.164"
|
serde = "1.0.164"
|
||||||
nu-pretty-hex = { version = "0.85.0", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.86.0", path = "../nu-pretty-hex" }
|
||||||
nu-json = { version = "0.85.0", path = "../nu-json" }
|
nu-json = { version = "0.86.0", path = "../nu-json" }
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
|
|
||||||
@ -37,6 +37,6 @@ extra = ["default"]
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.85.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.85.0" }
|
nu-command = { path = "../nu-command", version = "0.86.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||||
|
@ -22,16 +22,12 @@ impl Command for BitsAnd {
|
|||||||
Type::List(Box::new(Type::Int)),
|
Type::List(Box::new(Type::Int)),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
.required(
|
.required("target", SyntaxShape::Int, "target int to perform bit and")
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit and",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
.category(Category::Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Performs bitwise and for integers."
|
"Performs bitwise and for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -85,7 +81,7 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -219,7 +219,7 @@ pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer, filesize, string, date, duration, binary or bool".into(),
|
exp_input_type: "int, filesize, string, date, duration, binary, or bool".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -96,7 +96,7 @@ impl Command for BitsNot {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
||||||
example: "[4 3 2] | bits not -n '2'",
|
example: "[4 3 2] | bits not --number-bytes '2'",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![
|
vec![
|
||||||
Value::test_int(65531),
|
Value::test_int(65531),
|
||||||
@ -109,7 +109,7 @@ impl Command for BitsNot {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Apply logical negation to a list of numbers, treat input as signed number",
|
"Apply logical negation to a list of numbers, treat input as signed number",
|
||||||
example: "[4 3 2] | bits not -s",
|
example: "[4 3 2] | bits not --signed",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![
|
vec![
|
||||||
Value::test_int(-5),
|
Value::test_int(-5),
|
||||||
@ -158,7 +158,7 @@ fn operate(value: Value, head: Span, signed: bool, number_size: NumberBytes) ->
|
|||||||
Value::Error { .. } => other,
|
Value::Error { .. } => other,
|
||||||
_ => Value::error(
|
_ => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -22,16 +22,12 @@ impl Command for BitsOr {
|
|||||||
Type::List(Box::new(Type::Int)),
|
Type::List(Box::new(Type::Int)),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
.required(
|
.required("target", SyntaxShape::Int, "target int to perform bit or")
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit or",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
.category(Category::Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Performs bitwise or for integers."
|
"Performs bitwise or for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -85,7 +81,7 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -41,7 +41,7 @@ impl Command for BitsRol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Bitwise rotate left for integers."
|
"Bitwise rotate left for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -145,7 +145,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -41,7 +41,7 @@ impl Command for BitsRor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Bitwise rotate right for integers."
|
"Bitwise rotate right for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -90,7 +90,7 @@ impl Command for BitsRor {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate right a list of numbers of one byte",
|
description: "Rotate right a list of numbers of one byte",
|
||||||
example: "[15 33 92] | bits ror 2 -n '1'",
|
example: "[15 33 92] | bits ror 2 --number-bytes '1'",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![
|
vec![
|
||||||
Value::test_int(195),
|
Value::test_int(195),
|
||||||
@ -149,7 +149,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -41,7 +41,7 @@ impl Command for BitsShl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Bitwise shift left for integers."
|
"Bitwise shift left for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -90,12 +90,12 @@ impl Command for BitsShl {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shift left a number with 1 byte by 7 bits",
|
description: "Shift left a number with 1 byte by 7 bits",
|
||||||
example: "2 | bits shl 7 -n '1'",
|
example: "2 | bits shl 7 --number-bytes '1'",
|
||||||
result: Some(Value::test_int(0)),
|
result: Some(Value::test_int(0)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Shift left a signed number by 1 bit",
|
description: "Shift left a signed number by 1 bit",
|
||||||
example: "0x7F | bits shl 1 -s",
|
example: "0x7F | bits shl 1 --signed",
|
||||||
result: Some(Value::test_int(254)),
|
result: Some(Value::test_int(254)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -169,7 +169,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -41,7 +41,7 @@ impl Command for BitsShr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Bitwise shift right for integers."
|
"Bitwise shift right for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -159,7 +159,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -22,16 +22,12 @@ impl Command for BitsXor {
|
|||||||
Type::List(Box::new(Type::Int)),
|
Type::List(Box::new(Type::Int)),
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
.required(
|
.required("target", SyntaxShape::Int, "target int to perform bit xor")
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit xor",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
.category(Category::Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Performs bitwise xor for integers."
|
"Performs bitwise xor for ints."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -84,7 +80,7 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
|
|||||||
Value::Error { .. } => value,
|
Value::Error { .. } => value,
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer".into(),
|
exp_input_type: "int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -88,7 +88,7 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "float , integer or filesize".into(),
|
exp_input_type: "float, int, or filesize".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::camelcase::to_camel_case;
|
use heck::ToLowerCamelCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -50,7 +50,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_camel_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToLowerCamelCase::to_lower_camel_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::kebabcase::to_kebab_case;
|
use heck::ToKebabCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -50,7 +50,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_kebab_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToKebabCase::to_kebab_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::pascalcase::to_pascal_case;
|
use heck::ToUpperCamelCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -50,7 +50,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_pascal_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToUpperCamelCase::to_upper_camel_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
use heck::ToShoutySnakeCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -50,7 +50,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_screaming_snake_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToShoutySnakeCase::to_shouty_snake_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::snakecase::to_snake_case;
|
use heck::ToSnakeCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -49,7 +49,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_snake_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToSnakeCase::to_snake_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use inflector::cases::titlecase::to_title_case;
|
use heck::ToTitleCase;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -50,7 +50,13 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input, &to_title_case)
|
operate(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
input,
|
||||||
|
&ToTitleCase::to_title_case,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -6,24 +6,24 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.85.0" }
|
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
|
|
||||||
fancy-regex = "0.11"
|
fancy-regex = "0.11"
|
||||||
itertools = "0.11"
|
itertools = "0.11"
|
||||||
shadow-rs = { version = "0.23", default-features = false }
|
shadow-rs = { version = "0.24", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.23", default-features = false }
|
shadow-rs = { version = "0.24", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mimalloc = []
|
mimalloc = []
|
||||||
|
@ -19,9 +19,11 @@ impl Command for Def {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("def")
|
Signature::build("def")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("def_name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "command name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required("body", SyntaxShape::Closure(None), "body of the definition")
|
.required("block", SyntaxShape::Closure(None), "body of the definition")
|
||||||
|
.switch("env", "keep the environment defined inside the command", None)
|
||||||
|
.switch("wrapped", "treat unknown flags and arguments as strings (requires ...rest-like parameter in signature)", None)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +58,16 @@ impl Command for Def {
|
|||||||
example: r#"def say-sth [sth: string] { echo $sth }; say-sth hi"#,
|
example: r#"def say-sth [sth: string] { echo $sth }; say-sth hi"#,
|
||||||
result: Some(Value::test_string("hi")),
|
result: Some(Value::test_string("hi")),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Set environment variable by call a custom command",
|
||||||
|
example: r#"def --env foo [] { $env.BAR = "BAZ" }; foo; $env.BAR"#,
|
||||||
|
result: Some(Value::test_string("BAZ")),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Define a custom wrapper for an external command",
|
||||||
|
example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#,
|
||||||
|
result: Some(Value::test_list(vec![Value::test_string("spam")])),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use nu_engine::{eval_block_with_early_return, CallExt};
|
use nu_engine::{eval_block_with_early_return, redirect_env, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -48,6 +48,11 @@ impl Command for Do {
|
|||||||
"catch errors as the closure runs, and return them",
|
"catch errors as the closure runs, and return them",
|
||||||
Some('c'),
|
Some('c'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"env",
|
||||||
|
"keep the environment defined inside the command",
|
||||||
|
None,
|
||||||
|
)
|
||||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the closure")
|
.rest("rest", SyntaxShape::Any, "the parameter(s) for the closure")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
@ -55,18 +60,19 @@ impl Command for Do {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
caller_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let block: Closure = call.req(engine_state, stack, 0)?;
|
let block: Closure = call.req(engine_state, caller_stack, 0)?;
|
||||||
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, caller_stack, 1)?;
|
||||||
let ignore_all_errors = call.has_flag("ignore-errors");
|
let ignore_all_errors = call.has_flag("ignore-errors");
|
||||||
let ignore_shell_errors = ignore_all_errors || call.has_flag("ignore-shell-errors");
|
let ignore_shell_errors = ignore_all_errors || call.has_flag("ignore-shell-errors");
|
||||||
let ignore_program_errors = ignore_all_errors || call.has_flag("ignore-program-errors");
|
let ignore_program_errors = ignore_all_errors || call.has_flag("ignore-program-errors");
|
||||||
let capture_errors = call.has_flag("capture-errors");
|
let capture_errors = call.has_flag("capture-errors");
|
||||||
|
let has_env = call.has_flag("env");
|
||||||
|
|
||||||
let mut stack = stack.captures_to_stack(&block.captures);
|
let mut callee_stack = caller_stack.captures_to_stack(&block.captures);
|
||||||
let block = engine_state.get_block(block.block_id);
|
let block = engine_state.get_block(block.block_id);
|
||||||
|
|
||||||
let params: Vec<_> = block
|
let params: Vec<_> = block
|
||||||
@ -78,7 +84,7 @@ impl Command for Do {
|
|||||||
|
|
||||||
for param in params.iter().zip(&rest) {
|
for param in params.iter().zip(&rest) {
|
||||||
if let Some(var_id) = param.0.var_id {
|
if let Some(var_id) = param.0.var_id {
|
||||||
stack.add_var(var_id, param.1.clone())
|
callee_stack.add_var(var_id, param.1.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +102,7 @@ impl Command for Do {
|
|||||||
call.head
|
call.head
|
||||||
};
|
};
|
||||||
|
|
||||||
stack.add_var(
|
callee_stack.add_var(
|
||||||
param
|
param
|
||||||
.var_id
|
.var_id
|
||||||
.expect("Internal error: rest positional parameter lacks var_id"),
|
.expect("Internal error: rest positional parameter lacks var_id"),
|
||||||
@ -106,13 +112,18 @@ impl Command for Do {
|
|||||||
}
|
}
|
||||||
let result = eval_block_with_early_return(
|
let result = eval_block_with_early_return(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
&mut callee_stack,
|
||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if has_env {
|
||||||
|
// Merge the block's environment to the current stack
|
||||||
|
redirect_env(engine_state, caller_stack, &callee_stack);
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout,
|
stdout,
|
||||||
@ -264,22 +275,22 @@ impl Command for Do {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure and ignore both shell and external program errors",
|
description: "Run the closure and ignore both shell and external program errors",
|
||||||
example: r#"do -i { thisisnotarealcommand }"#,
|
example: r#"do --ignore-errors { thisisnotarealcommand }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure and ignore shell errors",
|
description: "Run the closure and ignore shell errors",
|
||||||
example: r#"do -s { thisisnotarealcommand }"#,
|
example: r#"do --ignore-shell-errors { thisisnotarealcommand }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure and ignore external program errors",
|
description: "Run the closure and ignore external program errors",
|
||||||
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
|
example: r#"do --ignore-program-errors { nu --commands 'exit 1' }; echo "I'll still run""#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Abort the pipeline if a program returns a non-zero exit code",
|
description: "Abort the pipeline if a program returns a non-zero exit code",
|
||||||
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
|
example: r#"do --capture-errors { nu --commands 'exit 1' } | myscarycommand"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -292,6 +303,11 @@ impl Command for Do {
|
|||||||
example: r#"77 | do {|x| 100 + $in }"#,
|
example: r#"77 | do {|x| 100 + $in }"#,
|
||||||
result: None, // TODO: returns 177
|
result: None, // TODO: returns 177
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the closure and keep changes to the environment",
|
||||||
|
example: r#"do --env { $env.foo = 'bar' }; $env.foo"#,
|
||||||
|
result: Some(Value::test_string("bar")),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,11 @@ impl Command for ExportDef {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("export def")
|
Signature::build("export def")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "command name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required("block", SyntaxShape::Block, "body of the definition")
|
.required("block", SyntaxShape::Block, "body of the definition")
|
||||||
|
.switch("env", "keep the environment defined inside the command", None)
|
||||||
|
.switch("wrapped", "treat unknown flags and arguments as strings (requires ...rest-like parameter in signature)", None)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ impl Command for For {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Echo the square of each integer",
|
description: "Print the square of each integer",
|
||||||
example: "for x in [1 2 3] { print ($x * $x) }",
|
example: "for x in [1 2 3] { print ($x * $x) }",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -209,7 +209,7 @@ impl Command for For {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Number each item and echo a message",
|
description: "Number each item and print a message",
|
||||||
example:
|
example:
|
||||||
"for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
|
"for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
|
||||||
result: None,
|
result: None,
|
||||||
|
@ -88,7 +88,7 @@ impl Command for LazyMake {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Test the laziness of lazy records",
|
description: "Test the laziness of lazy records",
|
||||||
example: r#"lazy make -c ["hello"] -g { |key| print $"getting ($key)!"; $key | str upcase }"#,
|
example: r#"lazy make --columns ["hello"] --get-value { |key| print $"getting ($key)!"; $key | str upcase }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -62,7 +62,7 @@ impl Command for Module {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Define a custom command that participates in the environment in a module and call it",
|
description: "Define a custom command that participates in the environment in a module and call it",
|
||||||
example: r#"module foo { export def-env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
|
example: r#"module foo { export def --env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
|
||||||
result: Some(Value::test_string("BAZ")),
|
result: Some(Value::test_string("BAZ")),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -155,7 +155,7 @@ This command is a parser keyword. For details, check:
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Define a custom command that participates in the environment in a module and call it",
|
description: "Define a custom command that participates in the environment in a module and call it",
|
||||||
example: r#"module foo { export def-env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
|
example: r#"module foo { export def --env bar [] { $env.FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
|
||||||
result: Some(Value::test_string("BAZ")),
|
result: Some(Value::test_string("BAZ")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -5,19 +5,19 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.85.0" }
|
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.85.0" }
|
nu-json = { path = "../nu-json", version = "0.86.0" }
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||||
|
@ -247,6 +247,24 @@ pub fn lookup_style(s: &str) -> Style {
|
|||||||
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
|
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
|
||||||
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
|
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
|
||||||
|
|
||||||
|
"m" | "magenta" => Color::Magenta.normal(),
|
||||||
|
"mb" | "magenta_bold" => Color::Magenta.bold(),
|
||||||
|
"mu" | "magenta_underline" => Color::Magenta.underline(),
|
||||||
|
"mi" | "magenta_italic" => Color::Magenta.italic(),
|
||||||
|
"md" | "magenta_dimmed" => Color::Magenta.dimmed(),
|
||||||
|
"mr" | "magenta_reverse" => Color::Magenta.reverse(),
|
||||||
|
"mbl" | "magenta_blink" => Color::Magenta.blink(),
|
||||||
|
"mst" | "magenta_strike" => Color::Magenta.strikethrough(),
|
||||||
|
|
||||||
|
"lm" | "light_magenta" => Color::LightMagenta.normal(),
|
||||||
|
"lmb" | "light_magenta_bold" => Color::LightMagenta.bold(),
|
||||||
|
"lmu" | "light_magenta_underline" => Color::LightMagenta.underline(),
|
||||||
|
"lmi" | "light_magenta_italic" => Color::LightMagenta.italic(),
|
||||||
|
"lmd" | "light_magenta_dimmed" => Color::LightMagenta.dimmed(),
|
||||||
|
"lmr" | "light_magenta_reverse" => Color::LightMagenta.reverse(),
|
||||||
|
"lmbl" | "light_magenta_blink" => Color::LightMagenta.blink(),
|
||||||
|
"lmst" | "light_magenta_strike" => Color::LightMagenta.strikethrough(),
|
||||||
|
|
||||||
"c" | "cyan" => Color::Cyan.normal(),
|
"c" | "cyan" => Color::Cyan.normal(),
|
||||||
"cb" | "cyan_bold" => Color::Cyan.bold(),
|
"cb" | "cyan_bold" => Color::Cyan.bold(),
|
||||||
"cu" | "cyan_underline" => Color::Cyan.underline(),
|
"cu" | "cyan_underline" => Color::Cyan.underline(),
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||||
version = "0.85.0"
|
version = "0.86.0"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -14,23 +14,23 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-ansi-term = "0.49.0"
|
nu-ansi-term = "0.49.0"
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.85.0" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.86.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.85.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.86.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.85.0" }
|
nu-engine = { path = "../nu-engine", version = "0.86.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.85.0" }
|
nu-glob = { path = "../nu-glob", version = "0.86.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.85.0" }
|
nu-json = { path = "../nu-json", version = "0.86.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.85.0" }
|
nu-parser = { path = "../nu-parser", version = "0.86.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.85.0" }
|
nu-path = { path = "../nu-path", version = "0.86.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.85.0" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.86.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.85.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.86.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.85.0" }
|
nu-system = { path = "../nu-system", version = "0.86.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.85.0" }
|
nu-table = { path = "../nu-table", version = "0.86.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.85.0" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.86.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.85.0" }
|
nu-utils = { path = "../nu-utils", version = "0.86.0" }
|
||||||
|
|
||||||
alphanumeric-sort = "1.5"
|
alphanumeric-sort = "1.5"
|
||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
byteorder = "1.4"
|
byteorder = "1.5"
|
||||||
bytesize = "1.3"
|
bytesize = "1.3"
|
||||||
calamine = "0.22"
|
calamine = "0.22"
|
||||||
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
|
chrono = { version = "0.4", features = ["std", "unstable-locales"], default-features = false }
|
||||||
@ -38,7 +38,7 @@ chrono-humanize = "0.2.3"
|
|||||||
chrono-tz = "0.8"
|
chrono-tz = "0.8"
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
csv = "1.2"
|
csv = "1.2"
|
||||||
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.10" }
|
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.11" }
|
||||||
digest = { default-features = false, version = "0.10" }
|
digest = { default-features = false, version = "0.10" }
|
||||||
dtparse = "2.0"
|
dtparse = "2.0"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
@ -66,12 +66,11 @@ open = "5.0"
|
|||||||
os_pipe = "1.1"
|
os_pipe = "1.1"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
percent-encoding = "2.3"
|
percent-encoding = "2.3"
|
||||||
powierza-coefficient = "1.0"
|
|
||||||
print-positions = "0.6"
|
print-positions = "0.6"
|
||||||
quick-xml = "0.30"
|
quick-xml = "0.30"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rayon = "1.7"
|
rayon = "1.8"
|
||||||
regex = "1.7"
|
regex = "1.9.5"
|
||||||
roxmltree = "0.18"
|
roxmltree = "0.18"
|
||||||
rusqlite = { version = "0.29", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.29", features = ["bundled"], optional = true }
|
||||||
same-file = "1.0"
|
same-file = "1.0"
|
||||||
@ -86,11 +85,11 @@ terminal_size = "0.3"
|
|||||||
titlecase = "2.0"
|
titlecase = "2.0"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
ureq = { version = "2.7", default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
ureq = { version = "2.8", default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uu_cp = "0.0.21"
|
uu_cp = "0.0.22"
|
||||||
uuid = { version = "1.3", features = ["v4"] }
|
uuid = { version = "1.3", features = ["v4"] }
|
||||||
wax = { version = "0.5" }
|
wax = { version = "0.6" }
|
||||||
which = { version = "4.4", optional = true }
|
which = { version = "4.4", optional = true }
|
||||||
bracoxide = "0.1.2"
|
bracoxide = "0.1.2"
|
||||||
chardetng = "0.1.17"
|
chardetng = "0.1.17"
|
||||||
@ -105,7 +104,7 @@ nix = { version = "0.27", default-features = false, features = ["user"] }
|
|||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
optional = true
|
optional = true
|
||||||
version = "3.0"
|
version = "3.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
features = [
|
features = [
|
||||||
@ -124,8 +123,8 @@ trash-support = ["trash"]
|
|||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.85.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.86.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.85.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.86.0" }
|
||||||
|
|
||||||
dirs-next = "2.0"
|
dirs-next = "2.0"
|
||||||
mockito = { version = "1.2", default-features = false }
|
mockito = { version = "1.2", default-features = false }
|
||||||
|
@ -97,21 +97,21 @@ impl Command for BytesAdd {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Add bytes `0x[AA BB]` to `0x[1F FF AA AA]` at index 1",
|
description: "Add bytes `0x[AA BB]` to `0x[1F FF AA AA]` at index 1",
|
||||||
example: "0x[1F FF AA AA] | bytes add 0x[AA BB] -i 1",
|
example: "0x[1F FF AA AA] | bytes add 0x[AA BB] --index 1",
|
||||||
result: Some(Value::binary(vec![0x1F, 0xAA, 0xBB, 0xFF, 0xAA, 0xAA],
|
result: Some(Value::binary(vec![0x1F, 0xAA, 0xBB, 0xFF, 0xAA, 0xAA],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Add bytes `0x[11]` to `0x[FF AA AA]` at the end",
|
description: "Add bytes `0x[11]` to `0x[FF AA AA]` at the end",
|
||||||
example: "0x[FF AA AA] | bytes add 0x[11] -e",
|
example: "0x[FF AA AA] | bytes add 0x[11] --end",
|
||||||
result: Some(Value::binary(vec![0xFF, 0xAA, 0xAA, 0x11],
|
result: Some(Value::binary(vec![0xFF, 0xAA, 0xAA, 0x11],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Add bytes `0x[11 22 33]` to `0x[FF AA AA]` at the end, at index 1(the index is start from end)",
|
description: "Add bytes `0x[11 22 33]` to `0x[FF AA AA]` at the end, at index 1(the index is start from end)",
|
||||||
example: "0x[FF AA BB] | bytes add 0x[11 22 33] -e -i 1",
|
example: "0x[FF AA BB] | bytes add 0x[11 22 33] --end --index 1",
|
||||||
result: Some(Value::binary(vec![0xFF, 0xAA, 0x11, 0x22, 0x33, 0xBB],
|
result: Some(Value::binary(vec![0xFF, 0xAA, 0x11, 0x22, 0x33, 0xBB],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
|
@ -89,12 +89,12 @@ impl Command for BytesIndexOf {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns index of pattern, search from end",
|
description: "Returns index of pattern, search from end",
|
||||||
example: " 0x[33 44 55 10 01 13 44 55] | bytes index-of -e 0x[44 55]",
|
example: " 0x[33 44 55 10 01 13 44 55] | bytes index-of --end 0x[44 55]",
|
||||||
result: Some(Value::test_int(6)),
|
result: Some(Value::test_int(6)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns all matched index",
|
description: "Returns all matched index",
|
||||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a 0x[33 44]",
|
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all 0x[33 44]",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
|
vec![Value::test_int(0), Value::test_int(5), Value::test_int(7)],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
@ -102,7 +102,7 @@ impl Command for BytesIndexOf {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns all matched index, searching from end",
|
description: "Returns all matched index, searching from end",
|
||||||
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of -a -e 0x[33 44]",
|
example: " 0x[33 44 55 10 01 33 44 33 44] | bytes index-of --all --end 0x[33 44]",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
|
vec![Value::test_int(7), Value::test_int(5), Value::test_int(0)],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
|
@ -94,7 +94,7 @@ impl Command for BytesRemove {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Remove all occurrences of find binary in record field",
|
description: "Remove all occurrences of find binary in record field",
|
||||||
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove -a 0x[10] data",
|
example: "{ data: 0x[10 AA 10 BB 10] } | bytes remove --all 0x[10] data",
|
||||||
result: Some(Value::test_record(Record {
|
result: Some(Value::test_record(Record {
|
||||||
cols: vec!["data".to_string()],
|
cols: vec!["data".to_string()],
|
||||||
vals: vec![Value::test_binary(vec![0xAA, 0xBB])]
|
vals: vec![Value::test_binary(vec![0xAA, 0xBB])]
|
||||||
@ -102,7 +102,7 @@ impl Command for BytesRemove {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Remove occurrences of find binary from end",
|
description: "Remove occurrences of find binary from end",
|
||||||
example: "0x[10 AA 10 BB CC AA 10] | bytes remove -e 0x[10]",
|
example: "0x[10 AA 10 BB CC AA 10] | bytes remove --end 0x[10]",
|
||||||
result: Some(Value::binary (
|
result: Some(Value::binary (
|
||||||
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
|
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
|
@ -94,7 +94,7 @@ impl Command for BytesReplace {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Find and replace all occurrences of find binary",
|
description: "Find and replace all occurrences of find binary",
|
||||||
example: "0x[10 AA 10 BB 10] | bytes replace -a 0x[10] 0x[A0]",
|
example: "0x[10 AA 10 BB 10] | bytes replace --all 0x[10] 0x[A0]",
|
||||||
result: Some(Value::binary (
|
result: Some(Value::binary (
|
||||||
vec![0xA0, 0xAA, 0xA0, 0xBB, 0xA0],
|
vec![0xA0, 0xAA, 0xA0, 0xBB, 0xA0],
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
@ -102,7 +102,7 @@ impl Command for BytesReplace {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Find and replace all occurrences of find binary in table",
|
description: "Find and replace all occurrences of find binary in table",
|
||||||
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes replace -a 0x[11] 0x[13] ColA ColC",
|
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes replace --all 0x[11] 0x[13] ColA ColC",
|
||||||
result: Some(Value::list (
|
result: Some(Value::list (
|
||||||
vec![Value::test_record(Record {
|
vec![Value::test_record(Record {
|
||||||
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
|
||||||
|
@ -85,18 +85,18 @@ impl Command for Fill {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Fill a string on the left side to a width of 15 with the character '─'",
|
"Fill a string on the left side to a width of 15 with the character '─'",
|
||||||
example: "'nushell' | fill -a l -c '─' -w 15",
|
example: "'nushell' | fill --alignment l --character '─' --width 15",
|
||||||
result: Some(Value::string("nushell────────", Span::test_data())),
|
result: Some(Value::string("nushell────────", Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Fill a string on the right side to a width of 15 with the character '─'",
|
"Fill a string on the right side to a width of 15 with the character '─'",
|
||||||
example: "'nushell' | fill -a r -c '─' -w 15",
|
example: "'nushell' | fill --alignment r --character '─' --width 15",
|
||||||
result: Some(Value::string("────────nushell", Span::test_data())),
|
result: Some(Value::string("────────nushell", Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Fill a string on both sides to a width of 15 with the character '─'",
|
description: "Fill a string on both sides to a width of 15 with the character '─'",
|
||||||
example: "'nushell' | fill -a m -c '─' -w 15",
|
example: "'nushell' | fill --alignment m --character '─' --width 15",
|
||||||
result: Some(Value::string("────nushell────", Span::test_data())),
|
result: Some(Value::string("────nushell────", Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -115,8 +115,7 @@ impl Command for SubCommand {
|
|||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "convert an int to a nushell binary primitive with compact enabled",
|
||||||
"convert an integer to a nushell binary primitive with compact enabled",
|
|
||||||
example: "10 | into binary --compact",
|
example: "10 | into binary --compact",
|
||||||
result: Some(Value::binary(vec![10], Span::test_data())),
|
result: Some(Value::binary(vec![10], Span::test_data())),
|
||||||
},
|
},
|
||||||
@ -172,7 +171,7 @@ pub fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer, float, filesize, string, date, duration, binary or bool"
|
exp_input_type: "int, float, filesize, string, date, duration, binary, or bool"
|
||||||
.into(),
|
.into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
|
@ -90,7 +90,7 @@ impl Command for SubCommand {
|
|||||||
result: Some(Value::bool(true, span)),
|
result: Some(Value::bool(true, span)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert integer to boolean",
|
description: "convert int to boolean",
|
||||||
example: "1 | into bool",
|
example: "1 | into bool",
|
||||||
result: Some(Value::bool(true, span)),
|
result: Some(Value::bool(true, span)),
|
||||||
},
|
},
|
||||||
@ -159,7 +159,7 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "bool, integer, float or string".into(),
|
exp_input_type: "bool, int, float or string".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -175,7 +175,7 @@ impl Command for SubCommand {
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Convert non-standard timestamp string to datetime using a custom format",
|
"Convert non-standard timestamp string to datetime using a custom format",
|
||||||
example: "'20210227_135540+0000' | into datetime -f '%Y%m%d_%H%M%S%z'",
|
example: "'20210227_135540+0000' | into datetime --format '%Y%m%d_%H%M%S%z'",
|
||||||
#[allow(clippy::inconsistent_digit_grouping)]
|
#[allow(clippy::inconsistent_digit_grouping)]
|
||||||
result: example_result_1(1614434140_000000000),
|
result: example_result_1(1614434140_000000000),
|
||||||
},
|
},
|
||||||
@ -256,7 +256,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
other => {
|
other => {
|
||||||
return Value::error(
|
return Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "string and integer".into(),
|
exp_input_type: "string and int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -1,198 +0,0 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::{Call, CellPath},
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"into decimal"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("into decimal")
|
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Int, Type::Float),
|
|
||||||
(Type::String, Type::Float),
|
|
||||||
(Type::Bool, Type::Float),
|
|
||||||
(Type::Float, Type::Float),
|
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
|
||||||
(
|
|
||||||
Type::List(Box::new(Type::Any)),
|
|
||||||
Type::List(Box::new(Type::Float)),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.rest(
|
|
||||||
"rest",
|
|
||||||
SyntaxShape::CellPath,
|
|
||||||
"for a data structure input, convert data at the given cell paths",
|
|
||||||
)
|
|
||||||
.allow_variants_without_examples(true)
|
|
||||||
.category(Category::Conversions)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"deprecated: convert data into a floating point number."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"Use `into float` instead"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["convert", "number", "floating"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError(
|
|
||||||
"Deprecated command".into(),
|
|
||||||
"`into decimal` is deprecated and will be removed in 0.86.".into(),
|
|
||||||
Some(call.head),
|
|
||||||
Some("Use `into float` instead".into()),
|
|
||||||
vec![],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
let args = CellPathOnlyArgs::from(cell_paths);
|
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Convert string to float in table",
|
|
||||||
example: "[[num]; ['5.01']] | into decimal num",
|
|
||||||
result: Some(Value::list(
|
|
||||||
vec![Value::test_record(Record {
|
|
||||||
cols: vec!["num".to_string()],
|
|
||||||
vals: vec![Value::test_float(5.01)],
|
|
||||||
})],
|
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Convert string to float",
|
|
||||||
example: "'1.345' | into decimal",
|
|
||||||
result: Some(Value::test_float(1.345)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Coerce list of ints and floats to float",
|
|
||||||
example: "[4 -5.9] | into decimal",
|
|
||||||
result: Some(Value::test_list(vec![
|
|
||||||
Value::test_float(4.0),
|
|
||||||
Value::test_float(-5.9),
|
|
||||||
])),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Convert boolean to float",
|
|
||||||
example: "true | into decimal",
|
|
||||||
result: Some(Value::test_float(1.0)),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
|
|
||||||
let span = input.span();
|
|
||||||
match input {
|
|
||||||
Value::Float { .. } => input.clone(),
|
|
||||||
Value::String { val: s, .. } => {
|
|
||||||
let other = s.trim();
|
|
||||||
|
|
||||||
match other.parse::<f64>() {
|
|
||||||
Ok(x) => Value::float(x, head),
|
|
||||||
Err(reason) => Value::error(
|
|
||||||
ShellError::CantConvert {
|
|
||||||
to_type: "float".to_string(),
|
|
||||||
from_type: reason.to_string(),
|
|
||||||
span,
|
|
||||||
help: None,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Int { val: v, .. } => Value::float(*v as f64, span),
|
|
||||||
Value::Bool { val: b, .. } => Value::float(
|
|
||||||
match b {
|
|
||||||
true => 1.0,
|
|
||||||
false => 0.0,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => input.clone(),
|
|
||||||
other => Value::error(
|
|
||||||
ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "string, integer or bool".into(),
|
|
||||||
wrong_type: other.get_type().to_string(),
|
|
||||||
dst_span: head,
|
|
||||||
src_span: other.span(),
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use nu_protocol::Type::Error;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[allow(clippy::approx_constant)]
|
|
||||||
fn string_to_decimal() {
|
|
||||||
let word = Value::test_string("3.1415");
|
|
||||||
let expected = Value::test_float(3.1415);
|
|
||||||
|
|
||||||
let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
|
|
||||||
let decimal_str = Value::test_string("11.6anra");
|
|
||||||
|
|
||||||
let actual = action(
|
|
||||||
&decimal_str,
|
|
||||||
&CellPathOnlyArgs::from(vec![]),
|
|
||||||
Span::test_data(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn int_to_decimal() {
|
|
||||||
let decimal_str = Value::test_int(10);
|
|
||||||
let expected = Value::test_float(10.0);
|
|
||||||
let actual = action(
|
|
||||||
&decimal_str,
|
|
||||||
&CellPathOnlyArgs::from(vec![]),
|
|
||||||
Span::test_data(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
}
|
|
@ -136,7 +136,7 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
Value::Nothing { .. } => Value::filesize(0, value_span),
|
Value::Nothing { .. } => Value::filesize(0, value_span),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "string and integer".into(),
|
exp_input_type: "string and int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
src_span: value_span,
|
src_span: value_span,
|
||||||
|
@ -121,7 +121,7 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "string, integer or bool".into(),
|
exp_input_type: "string, int or bool".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -158,32 +158,32 @@ impl Command for SubCommand {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Convert string to integer in table",
|
description: "Convert string to int in table",
|
||||||
example: "[[num]; ['-5'] [4] [1.5]] | into int num",
|
example: "[[num]; ['-5'] [4] [1.5]] | into int num",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert string to integer",
|
description: "Convert string to int",
|
||||||
example: "'2' | into int",
|
example: "'2' | into int",
|
||||||
result: Some(Value::test_int(2)),
|
result: Some(Value::test_int(2)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert float to integer",
|
description: "Convert float to int",
|
||||||
example: "5.9 | into int",
|
example: "5.9 | into int",
|
||||||
result: Some(Value::test_int(5)),
|
result: Some(Value::test_int(5)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert decimal string to integer",
|
description: "Convert decimal string to int",
|
||||||
example: "'5.9' | into int",
|
example: "'5.9' | into int",
|
||||||
result: Some(Value::test_int(5)),
|
result: Some(Value::test_int(5)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert file size to integer",
|
description: "Convert file size to int",
|
||||||
example: "4KB | into int",
|
example: "4KB | into int",
|
||||||
result: Some(Value::test_int(4000)),
|
result: Some(Value::test_int(4000)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert bool to integer",
|
description: "Convert bool to int",
|
||||||
example: "[false, true] | into int",
|
example: "[false, true] | into int",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![Value::test_int(0), Value::test_int(1)],
|
vec![Value::test_int(0), Value::test_int(1)],
|
||||||
@ -191,33 +191,33 @@ impl Command for SubCommand {
|
|||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert date to integer (Unix nanosecond timestamp)",
|
description: "Convert date to int (Unix nanosecond timestamp)",
|
||||||
example: "1983-04-13T12:09:14.123456789-05:00 | into int",
|
example: "1983-04-13T12:09:14.123456789-05:00 | into int",
|
||||||
result: Some(Value::test_int(419101754123456789)),
|
result: Some(Value::test_int(419101754123456789)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert to integer from binary",
|
description: "Convert to int from binary data (radix: 2)",
|
||||||
example: "'1101' | into int -r 2",
|
example: "'1101' | into int --radix 2",
|
||||||
result: Some(Value::test_int(13)),
|
result: Some(Value::test_int(13)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert to integer from hex",
|
description: "Convert to int from hex",
|
||||||
example: "'FF' | into int -r 16",
|
example: "'FF' | into int --radix 16",
|
||||||
result: Some(Value::test_int(255)),
|
result: Some(Value::test_int(255)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert octal string to integer",
|
description: "Convert octal string to int",
|
||||||
example: "'0o10132' | into int",
|
example: "'0o10132' | into int",
|
||||||
result: Some(Value::test_int(4186)),
|
result: Some(Value::test_int(4186)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert 0 padded string to integer",
|
description: "Convert 0 padded string to int",
|
||||||
example: "'0010132' | into int",
|
example: "'0010132' | into int",
|
||||||
result: Some(Value::test_int(10132)),
|
result: Some(Value::test_int(10132)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert 0 padded string to integer with radix",
|
description: "Convert 0 padded string to int with radix 8",
|
||||||
example: "'0010132' | into int -r 8",
|
example: "'0010132' | into int --radix 8",
|
||||||
result: Some(Value::test_int(4186)),
|
result: Some(Value::test_int(4186)),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -248,7 +248,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|||||||
return Value::error(
|
return Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "float".to_string(),
|
to_type: "float".to_string(),
|
||||||
from_type: "integer".to_string(),
|
from_type: "int".to_string(),
|
||||||
span,
|
span,
|
||||||
help: None,
|
help: None,
|
||||||
},
|
},
|
||||||
@ -327,7 +327,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "integer, float, filesize, date, string, binary, duration or bool"
|
exp_input_type: "int, float, filesize, date, string, binary, duration, or bool"
|
||||||
.into(),
|
.into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: span,
|
dst_span: span,
|
||||||
@ -376,7 +376,7 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
|
|||||||
other => {
|
other => {
|
||||||
return Value::error(
|
return Value::error(
|
||||||
ShellError::OnlySupportsThisInputType {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "string and integer".into(),
|
exp_input_type: "string and int".into(),
|
||||||
wrong_type: other.get_type().to_string(),
|
wrong_type: other.get_type().to_string(),
|
||||||
dst_span: head,
|
dst_span: head,
|
||||||
src_span: other.span(),
|
src_span: other.span(),
|
||||||
|
@ -2,22 +2,22 @@ mod binary;
|
|||||||
mod bool;
|
mod bool;
|
||||||
mod command;
|
mod command;
|
||||||
mod datetime;
|
mod datetime;
|
||||||
mod decimal;
|
|
||||||
mod duration;
|
mod duration;
|
||||||
mod filesize;
|
mod filesize;
|
||||||
mod float;
|
mod float;
|
||||||
mod int;
|
mod int;
|
||||||
mod record;
|
mod record;
|
||||||
mod string;
|
mod string;
|
||||||
|
mod value;
|
||||||
|
|
||||||
pub use self::bool::SubCommand as IntoBool;
|
pub use self::bool::SubCommand as IntoBool;
|
||||||
pub use self::filesize::SubCommand as IntoFilesize;
|
pub use self::filesize::SubCommand as IntoFilesize;
|
||||||
pub use binary::SubCommand as IntoBinary;
|
pub use binary::SubCommand as IntoBinary;
|
||||||
pub use command::Into;
|
pub use command::Into;
|
||||||
pub use datetime::SubCommand as IntoDatetime;
|
pub use datetime::SubCommand as IntoDatetime;
|
||||||
pub use decimal::SubCommand as IntoDecimal;
|
|
||||||
pub use duration::SubCommand as IntoDuration;
|
pub use duration::SubCommand as IntoDuration;
|
||||||
pub use float::SubCommand as IntoFloat;
|
pub use float::SubCommand as IntoFloat;
|
||||||
pub use int::SubCommand as IntoInt;
|
pub use int::SubCommand as IntoInt;
|
||||||
pub use record::SubCommand as IntoRecord;
|
pub use record::SubCommand as IntoRecord;
|
||||||
pub use string::SubCommand as IntoString;
|
pub use string::SubCommand as IntoString;
|
||||||
|
pub use value::IntoValue;
|
||||||
|
@ -84,37 +84,25 @@ impl Command for SubCommand {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "convert integer to string and append three decimal places",
|
description: "convert int to string and append three decimal places",
|
||||||
example: "5 | into string -d 3",
|
example: "5 | into string --decimals 3",
|
||||||
result: Some(Value::test_string("5.000")),
|
result: Some(Value::test_string("5.000")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert float to string and round to nearest integer",
|
description: "convert float to string and round to nearest integer",
|
||||||
example: "1.7 | into string -d 0",
|
example: "1.7 | into string --decimals 0",
|
||||||
result: Some(Value::test_string("2")),
|
result: Some(Value::test_string("2")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert float to string",
|
description: "convert float to string",
|
||||||
example: "1.7 | into string -d 1",
|
example: "1.7 | into string --decimals 1",
|
||||||
result: Some(Value::test_string("1.7")),
|
result: Some(Value::test_string("1.7")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert float to string and limit to 2 decimals",
|
description: "convert float to string and limit to 2 decimals",
|
||||||
example: "1.734 | into string -d 2",
|
example: "1.734 | into string --decimals 2",
|
||||||
result: Some(Value::test_string("1.73")),
|
result: Some(Value::test_string("1.73")),
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
description: "try to convert float to string and provide negative decimal points",
|
|
||||||
example: "1.734 | into string -d -2",
|
|
||||||
result: None,
|
|
||||||
// FIXME
|
|
||||||
// result: Some(Value::Error {
|
|
||||||
// error: ShellError::UnsupportedInput(
|
|
||||||
// String::from("Cannot accept negative integers for decimals arguments"),
|
|
||||||
// Span::test_data(),
|
|
||||||
// ),
|
|
||||||
// }),
|
|
||||||
},
|
|
||||||
Example {
|
Example {
|
||||||
description: "convert float to string",
|
description: "convert float to string",
|
||||||
example: "4.3 | into string",
|
example: "4.3 | into string",
|
||||||
|
474
crates/nu-command/src/conversions/into/value.rs
Normal file
474
crates/nu-command/src/conversions/into/value.rs
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
use crate::parse_date_from_string;
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoInterruptiblePipelineData, PipelineData, PipelineIterator, ShellError,
|
||||||
|
Signature, Span, SyntaxShape, Type, Value,
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::{Regex, RegexBuilder};
|
||||||
|
use std::{collections::HashSet, iter::FromIterator};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IntoValue;
|
||||||
|
|
||||||
|
impl Command for IntoValue {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"into value"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("into value")
|
||||||
|
.input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))])
|
||||||
|
.named(
|
||||||
|
"columns",
|
||||||
|
SyntaxShape::Table(vec![]),
|
||||||
|
"list of columns to update",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
|
.category(Category::Filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Infer nushell datatype for each cell."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Infer Nushell values for each cell.",
|
||||||
|
example: "$table | into value",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Infer Nushell values for each cell in the given columns.",
|
||||||
|
example: "$table | into value -c [column1, column5]",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
let metadata = input.metadata();
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let span = call.head;
|
||||||
|
|
||||||
|
// the columns to update
|
||||||
|
let columns: Option<Value> = call.get_flag(&engine_state, stack, "columns")?;
|
||||||
|
let columns: Option<HashSet<String>> = match columns {
|
||||||
|
Some(val) => {
|
||||||
|
let cols = val
|
||||||
|
.as_list()?
|
||||||
|
.iter()
|
||||||
|
.map(|val| val.as_string())
|
||||||
|
.collect::<Result<Vec<String>, ShellError>>()?;
|
||||||
|
Some(HashSet::from_iter(cols))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(UpdateCellIterator {
|
||||||
|
input: input.into_iter(),
|
||||||
|
columns,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into_pipeline_data(ctrlc)
|
||||||
|
.set_metadata(metadata))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UpdateCellIterator {
|
||||||
|
input: PipelineIterator,
|
||||||
|
columns: Option<HashSet<String>>,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for UpdateCellIterator {
|
||||||
|
type Item = Value;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.input.next() {
|
||||||
|
Some(val) => {
|
||||||
|
if let Some(ref cols) = self.columns {
|
||||||
|
if !val.columns().iter().any(|c| cols.contains(c)) {
|
||||||
|
return Some(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let span = val.span();
|
||||||
|
match val {
|
||||||
|
Value::Record { val, .. } => Some(Value::record(
|
||||||
|
val.into_iter()
|
||||||
|
.map(|(col, val)| match &self.columns {
|
||||||
|
Some(cols) if !cols.contains(&col) => (col, val),
|
||||||
|
_ => (
|
||||||
|
col,
|
||||||
|
match process_cell(val, span) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => Value::error(err, span),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
span,
|
||||||
|
)),
|
||||||
|
val => match process_cell(val, self.span) {
|
||||||
|
Ok(val) => Some(val),
|
||||||
|
Err(err) => Some(Value::error(err, self.span)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function will check each cell to see if it matches a regular expression
|
||||||
|
// for a particular datatype. If it does, it will convert the cell to that datatype.
|
||||||
|
fn process_cell(val: Value, span: Span) -> Result<Value, ShellError> {
|
||||||
|
// step 1: convert value to string
|
||||||
|
let val_str = val.as_string().unwrap_or_default();
|
||||||
|
|
||||||
|
// step 2: bounce string up against regexes
|
||||||
|
if BOOLEAN_RE.is_match(&val_str) {
|
||||||
|
let bval = val_str
|
||||||
|
.parse::<bool>()
|
||||||
|
.map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "string".to_string(),
|
||||||
|
from_type: "bool".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid boolean value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::bool(bval, span))
|
||||||
|
} else if FLOAT_RE.is_match(&val_str) {
|
||||||
|
let fval = val_str
|
||||||
|
.parse::<f64>()
|
||||||
|
.map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "string".to_string(),
|
||||||
|
from_type: "float".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid floating point value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::float(fval, span))
|
||||||
|
} else if INTEGER_RE.is_match(&val_str) {
|
||||||
|
let ival = val_str
|
||||||
|
.parse::<i64>()
|
||||||
|
.map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "string".to_string(),
|
||||||
|
from_type: "int".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid integer value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::int(ival, span))
|
||||||
|
} else if INTEGER_WITH_DELIMS_RE.is_match(&val_str) {
|
||||||
|
let mut val_str = val_str;
|
||||||
|
val_str.retain(|x| !['_', ','].contains(&x));
|
||||||
|
|
||||||
|
let ival = val_str
|
||||||
|
.parse::<i64>()
|
||||||
|
.map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "string".to_string(),
|
||||||
|
from_type: "int".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid integer value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::int(ival, span))
|
||||||
|
} else if DATETIME_DMY_RE.is_match(&val_str) {
|
||||||
|
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "date".to_string(),
|
||||||
|
from_type: "string".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid DATETIME_MDY_RE value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::date(dt, span))
|
||||||
|
} else if DATETIME_YMD_RE.is_match(&val_str) {
|
||||||
|
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "date".to_string(),
|
||||||
|
from_type: "string".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid DATETIME_YMD_RE value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::date(dt, span))
|
||||||
|
} else if DATETIME_YMDZ_RE.is_match(&val_str) {
|
||||||
|
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
|
||||||
|
to_type: "date".to_string(),
|
||||||
|
from_type: "string".to_string(),
|
||||||
|
span,
|
||||||
|
help: Some(format!(
|
||||||
|
r#""{val_str}" does not represent a valid DATETIME_YMDZ_RE value"#
|
||||||
|
)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::date(dt, span))
|
||||||
|
} else {
|
||||||
|
// If we don't know what it is, just return whatever it was passed in as
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// region: datatype regexes
|
||||||
|
const DATETIME_DMY_PATTERN: &str = r#"(?x)
|
||||||
|
^
|
||||||
|
['"]? # optional quotes
|
||||||
|
(?:\d{1,2}) # day
|
||||||
|
[-/] # separator
|
||||||
|
(?P<month>[01]?\d{1}) # month
|
||||||
|
[-/] # separator
|
||||||
|
(?:\d{4,}) # year
|
||||||
|
(?:
|
||||||
|
[T\ ] # separator
|
||||||
|
(?:\d{2}) # hour
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # minute
|
||||||
|
(?:
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # second
|
||||||
|
(?:
|
||||||
|
\.(?:\d{1,9}) # subsecond
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
['"]? # optional quotes
|
||||||
|
$
|
||||||
|
"#;
|
||||||
|
|
||||||
|
static DATETIME_DMY_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(DATETIME_DMY_PATTERN).expect("datetime_dmy_pattern should be valid"));
|
||||||
|
const DATETIME_YMD_PATTERN: &str = r#"(?x)
|
||||||
|
^
|
||||||
|
['"]? # optional quotes
|
||||||
|
(?:\d{4,}) # year
|
||||||
|
[-/] # separator
|
||||||
|
(?P<month>[01]?\d{1}) # month
|
||||||
|
[-/] # separator
|
||||||
|
(?:\d{1,2}) # day
|
||||||
|
(?:
|
||||||
|
[T\ ] # separator
|
||||||
|
(?:\d{2}) # hour
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # minute
|
||||||
|
(?:
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # seconds
|
||||||
|
(?:
|
||||||
|
\.(?:\d{1,9}) # subsecond
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
['"]? # optional quotes
|
||||||
|
$
|
||||||
|
"#;
|
||||||
|
static DATETIME_YMD_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(DATETIME_YMD_PATTERN).expect("datetime_ymd_pattern should be valid"));
|
||||||
|
//2023-03-24 16:44:17.865147299 -05:00
|
||||||
|
const DATETIME_YMDZ_PATTERN: &str = r#"(?x)
|
||||||
|
^
|
||||||
|
['"]? # optional quotes
|
||||||
|
(?:\d{4,}) # year
|
||||||
|
[-/] # separator
|
||||||
|
(?P<month>[01]?\d{1}) # month
|
||||||
|
[-/] # separator
|
||||||
|
(?:\d{1,2}) # day
|
||||||
|
[T\ ] # separator
|
||||||
|
(?:\d{2}) # hour
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # minute
|
||||||
|
(?:
|
||||||
|
:? # separator
|
||||||
|
(?:\d{2}) # second
|
||||||
|
(?:
|
||||||
|
\.(?:\d{1,9}) # subsecond
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
\s? # optional space
|
||||||
|
(?:
|
||||||
|
# offset (e.g. +01:00)
|
||||||
|
[+-](?:\d{2})
|
||||||
|
:?
|
||||||
|
(?:\d{2})
|
||||||
|
# or Zulu suffix
|
||||||
|
|Z
|
||||||
|
)
|
||||||
|
['"]? # optional quotes
|
||||||
|
$
|
||||||
|
"#;
|
||||||
|
static DATETIME_YMDZ_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(DATETIME_YMDZ_PATTERN).expect("datetime_ymdz_pattern should be valid"));
|
||||||
|
|
||||||
|
static FLOAT_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
Regex::new(r"^\s*[-+]?((\d*\.\d+)([eE][-+]?\d+)?|inf|NaN|(\d+)[eE][-+]?\d+|\d+\.)$")
|
||||||
|
.expect("float pattern should be valid")
|
||||||
|
});
|
||||||
|
|
||||||
|
static INTEGER_RE: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"^\s*-?(\d+)$").expect("integer pattern should be valid"));
|
||||||
|
|
||||||
|
static INTEGER_WITH_DELIMS_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
Regex::new(r"^\s*-?(\d{1,3}([,_]\d{3})+)$")
|
||||||
|
.expect("integer with delimiters pattern should be valid")
|
||||||
|
});
|
||||||
|
|
||||||
|
static BOOLEAN_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
RegexBuilder::new(r"^\s*(true)$|^(false)$")
|
||||||
|
.case_insensitive(true)
|
||||||
|
.build()
|
||||||
|
.expect("boolean pattern should be valid")
|
||||||
|
});
|
||||||
|
// endregion:
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(IntoValue {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_float_parse() {
|
||||||
|
// The regex should work on all these but nushell's float parser is more strict
|
||||||
|
assert!(FLOAT_RE.is_match("0.1"));
|
||||||
|
assert!(FLOAT_RE.is_match("3.0"));
|
||||||
|
assert!(FLOAT_RE.is_match("3.00001"));
|
||||||
|
assert!(FLOAT_RE.is_match("-9.9990e-003"));
|
||||||
|
assert!(FLOAT_RE.is_match("9.9990e+003"));
|
||||||
|
assert!(FLOAT_RE.is_match("9.9990E+003"));
|
||||||
|
assert!(FLOAT_RE.is_match("9.9990E+003"));
|
||||||
|
assert!(FLOAT_RE.is_match(".5"));
|
||||||
|
assert!(FLOAT_RE.is_match("2.5E-10"));
|
||||||
|
assert!(FLOAT_RE.is_match("2.5e10"));
|
||||||
|
assert!(FLOAT_RE.is_match("NaN"));
|
||||||
|
assert!(FLOAT_RE.is_match("-NaN"));
|
||||||
|
assert!(FLOAT_RE.is_match("-inf"));
|
||||||
|
assert!(FLOAT_RE.is_match("inf"));
|
||||||
|
assert!(FLOAT_RE.is_match("-7e-05"));
|
||||||
|
assert!(FLOAT_RE.is_match("7e-05"));
|
||||||
|
assert!(FLOAT_RE.is_match("+7e+05"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int_parse() {
|
||||||
|
assert!(INTEGER_RE.is_match("0"));
|
||||||
|
assert!(INTEGER_RE.is_match("1"));
|
||||||
|
assert!(INTEGER_RE.is_match("10"));
|
||||||
|
assert!(INTEGER_RE.is_match("100"));
|
||||||
|
assert!(INTEGER_RE.is_match("1000"));
|
||||||
|
assert!(INTEGER_RE.is_match("10000"));
|
||||||
|
assert!(INTEGER_RE.is_match("100000"));
|
||||||
|
assert!(INTEGER_RE.is_match("1000000"));
|
||||||
|
assert!(INTEGER_RE.is_match("10000000"));
|
||||||
|
assert!(INTEGER_RE.is_match("100000000"));
|
||||||
|
assert!(INTEGER_RE.is_match("1000000000"));
|
||||||
|
assert!(INTEGER_RE.is_match("10000000000"));
|
||||||
|
assert!(INTEGER_RE.is_match("100000000000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("100_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("100_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1_000_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10_000_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("100_000_000_000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("100,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1,000,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10,000,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("100,000,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("1,000,000,000"));
|
||||||
|
assert!(INTEGER_WITH_DELIMS_RE.is_match("10,000,000,000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bool_parse() {
|
||||||
|
assert!(BOOLEAN_RE.is_match("true"));
|
||||||
|
assert!(BOOLEAN_RE.is_match("false"));
|
||||||
|
assert!(!BOOLEAN_RE.is_match("1"));
|
||||||
|
assert!(!BOOLEAN_RE.is_match("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_datetime_ymdz_pattern() {
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00Z"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789Z"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00+01:00"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789+01:00"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00-01:00"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789-01:00"));
|
||||||
|
assert!(DATETIME_YMDZ_RE.is_match("'2022-01-01T00:00:00Z'"));
|
||||||
|
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00."));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00+01"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00+01:0"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00+1:00"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789+01"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789+01:0"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789+1:00"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00-01"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00-01:0"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00-1:00"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789-01"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789-01:0"));
|
||||||
|
assert!(!DATETIME_YMDZ_RE.is_match("2022-01-01T00:00:00.123456789-1:00"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_datetime_ymd_pattern() {
|
||||||
|
assert!(DATETIME_YMD_RE.is_match("2022-01-01"));
|
||||||
|
assert!(DATETIME_YMD_RE.is_match("2022/01/01"));
|
||||||
|
assert!(DATETIME_YMD_RE.is_match("2022-01-01T00:00:00"));
|
||||||
|
assert!(DATETIME_YMD_RE.is_match("2022-01-01T00:00:00.000000000"));
|
||||||
|
assert!(DATETIME_YMD_RE.is_match("'2022-01-01'"));
|
||||||
|
|
||||||
|
// The regex isn't this specific, but it would be nice if it were
|
||||||
|
// assert!(!DATETIME_YMD_RE.is_match("2022-13-01"));
|
||||||
|
// assert!(!DATETIME_YMD_RE.is_match("2022-01-32"));
|
||||||
|
// assert!(!DATETIME_YMD_RE.is_match("2022-01-01T24:00:00"));
|
||||||
|
// assert!(!DATETIME_YMD_RE.is_match("2022-01-01T00:60:00"));
|
||||||
|
// assert!(!DATETIME_YMD_RE.is_match("2022-01-01T00:00:60"));
|
||||||
|
assert!(!DATETIME_YMD_RE.is_match("2022-01-01T00:00:00.0000000000"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_datetime_dmy_pattern() {
|
||||||
|
assert!(DATETIME_DMY_RE.is_match("31-12-2021"));
|
||||||
|
assert!(DATETIME_DMY_RE.is_match("01/01/2022"));
|
||||||
|
assert!(DATETIME_DMY_RE.is_match("15-06-2023 12:30"));
|
||||||
|
assert!(!DATETIME_DMY_RE.is_match("2022-13-01"));
|
||||||
|
assert!(!DATETIME_DMY_RE.is_match("2022-01-32"));
|
||||||
|
assert!(!DATETIME_DMY_RE.is_match("2022-01-01 24:00"));
|
||||||
|
}
|
||||||
|
}
|
@ -98,7 +98,7 @@ fn timezone_offset_internal(
|
|||||||
};
|
};
|
||||||
match s.len() {
|
match s.len() {
|
||||||
len if len >= 2 => &s[2..],
|
len if len >= 2 => &s[2..],
|
||||||
len if len == 0 => s,
|
0 => s,
|
||||||
_ => return Err(ParseErrorKind::TooShort),
|
_ => return Err(ParseErrorKind::TooShort),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ impl Command for Ast {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the ast of a pipeline with an error, as json, minified",
|
description: "Print the ast of a pipeline with an error, as json, minified",
|
||||||
example: "ast 'for x in 1..10 { echo $x ' -j -m",
|
example: "ast 'for x in 1..10 { echo $x ' --json --minify",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user