mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 23:22:10 +02:00
Compare commits
202 Commits
Author | SHA1 | Date | |
---|---|---|---|
777f746127 | |||
3caab5de36 | |||
fa97e819eb | |||
bdc4bf97a7 | |||
cfb0f3961b | |||
8fa965118c | |||
9c800bcb2c | |||
ea4d8c5f49 | |||
5d2abdd1c3 | |||
7d5333db3b | |||
2a8a628b72 | |||
c4d2b787aa | |||
a4e11726cf | |||
9850fbd77d | |||
2ccb91dc6a | |||
3e76ed9122 | |||
2223fd663a | |||
3f960012cd | |||
0b094e2bf2 | |||
2388e1e80b | |||
62e34b69b3 | |||
fd68767216 | |||
93202d4529 | |||
ed1f0eb231 | |||
04612809ab | |||
8cca447e8c | |||
651e86a3c0 | |||
c3c3481ef5 | |||
0732d8bbba | |||
bdca31cc2d | |||
ed7aea8dd3 | |||
e813e44501 | |||
f46c45343a | |||
b12ffb8888 | |||
cf96677c78 | |||
ce03d8eb12 | |||
21dedef7f6 | |||
da7f77867a | |||
3415594877 | |||
0c38729735 | |||
a0b3a48e8b | |||
b662c2eb96 | |||
8cda641350 | |||
efdfeac55e | |||
e0577e15f2 | |||
bb0b0870ea | |||
74a73f9838 | |||
c9f9078726 | |||
eb875ea949 | |||
b4a0e4c0dc | |||
88a0705df1 | |||
7bcd96fc65 | |||
833825ae9a | |||
899383c30c | |||
d9d6cea5a9 | |||
d01ccd5a54 | |||
a896892ac9 | |||
d89d1894d0 | |||
587536ddcc | |||
ced5e1065f | |||
7479173811 | |||
4b83a2d27a | |||
41f72b1236 | |||
c98a6705e6 | |||
6454bf69aa | |||
bd30ea723e | |||
2dd4cb9f7d | |||
1784b4bf50 | |||
8e4b85e29b | |||
7098e56ccf | |||
02ad491dea | |||
708fee535c | |||
7636cc7fe4 | |||
f856e64fb3 | |||
a783a084d4 | |||
81b12d02ec | |||
336df6c65e | |||
35f9299fc6 | |||
649c8319e6 | |||
99cf5871aa | |||
da04e9d801 | |||
2db2d98ef0 | |||
817eacccd8 | |||
ce6d3c6eb2 | |||
ef32e1ce1a | |||
c1105e945e | |||
69b089845c | |||
75556f6c5f | |||
b650d1ef79 | |||
099b571e8f | |||
cb926f7b49 | |||
13515c5eb0 | |||
58d960d914 | |||
4a83bb6c93 | |||
3e56e81d06 | |||
312e9bf5d6 | |||
18d7e64660 | |||
f1118020a1 | |||
63433f1bc8 | |||
921a66554e | |||
bb0d08a721 | |||
fe14e52e77 | |||
24d72ca43c | |||
457f7889df | |||
7b0c0692dc | |||
c4cb3a77cb | |||
aed8d3800b | |||
53a9264b67 | |||
e18fb13616 | |||
2201bd9b09 | |||
da8f6c5682 | |||
14d7ba5cc9 | |||
5ee096232c | |||
c259ef41bd | |||
2c238aea6a | |||
c600c1ebe7 | |||
df94052180 | |||
f878276de7 | |||
cd89304706 | |||
2b9f258126 | |||
85587c0c2a | |||
6cc4ef6c70 | |||
517173bb8c | |||
e415be6c0e | |||
59332562bb | |||
3d8d7787de | |||
611fe41788 | |||
a6118eed8d | |||
d4798d6ee1 | |||
5ea245badf | |||
5ee7847035 | |||
8a812cf03c | |||
9a1cedfd08 | |||
766d1ef374 | |||
beec658872 | |||
b90d701f89 | |||
be5d71ea47 | |||
f1bde69131 | |||
36ae384fb3 | |||
2c4048eb43 | |||
b9195c2668 | |||
bb968304da | |||
ca9bf19041 | |||
ecfee4c542 | |||
acb34561eb | |||
43aec8cdbe | |||
e46d610f77 | |||
1d95861a09 | |||
f48de73236 | |||
0b4daa66b0 | |||
412952182f | |||
014d36b17a | |||
f44f3a8af1 | |||
457514590d | |||
843d8c2242 | |||
ce4ae00a6f | |||
4f7f6a2932 | |||
7039602e4d | |||
acb7aff6fb | |||
8940ee6c3f | |||
3b26b4355e | |||
b56e603c58 | |||
66c2a36123 | |||
8838815737 | |||
834522d002 | |||
f281cd5aa3 | |||
5add5cbd12 | |||
902aad6016 | |||
13f87857cf | |||
e0cc2c9112 | |||
92ab8b831b | |||
6a7a60429f | |||
79fd7d54b2 | |||
ebca840d91 | |||
17b2bcc125 | |||
24a98f8999 | |||
e49b359848 | |||
c9fb381d69 | |||
8224ec49bc | |||
fe7e87ee02 | |||
a3dce8ff19 | |||
89f3cbf318 | |||
3f555a6836 | |||
4fdf5c663c | |||
1ec41a0ab4 | |||
ab0a6b6ca6 | |||
e3bf6fdfc0 | |||
46c0d29c08 | |||
76ccd5668a | |||
c6436eb32f | |||
b2c29117d9 | |||
ffb1dfb012 | |||
88c6fa9933 | |||
60df45a390 | |||
10aa86272b | |||
d37e6ba3b5 | |||
5eee33c7e4 | |||
5e748ae8fc | |||
50e53e788a | |||
7336e1df1a | |||
a724a8fe7d | |||
c731a4e275 |
@ -12,3 +12,17 @@ rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-stati
|
|||||||
# set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB)
|
# set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB)
|
||||||
# [target.x86_64-apple-darwin]
|
# [target.x86_64-apple-darwin]
|
||||||
# rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"]
|
# rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"]
|
||||||
|
|
||||||
|
# How to use mold in linux and mac
|
||||||
|
|
||||||
|
# [target.x86_64-unknown-linux-gnu]
|
||||||
|
# linker = "clang"
|
||||||
|
# rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]
|
||||||
|
|
||||||
|
# [target.x86_64-apple-darwin]
|
||||||
|
# linker = "clang"
|
||||||
|
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
||||||
|
# [target.aarch64-apple-darwin]
|
||||||
|
# linker = "clang"
|
||||||
|
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
25
.github/pull_request_template.md
vendored
25
.github/pull_request_template.md
vendored
@ -1,21 +1,24 @@
|
|||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
(description of your pull request here)
|
_(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_
|
||||||
|
|
||||||
# Tests
|
_(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
|
||||||
|
|
||||||
Make sure you've done the following:
|
# User-Facing Changes
|
||||||
|
|
||||||
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
|
_(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
|
||||||
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
|
|
||||||
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
|
# Tests + Formatting
|
||||||
|
|
||||||
|
Don't forget to add tests that cover your changes.
|
||||||
|
|
||||||
Make sure you've run and fixed any issues with these commands:
|
Make sure you've run and fixed any issues with these commands:
|
||||||
|
|
||||||
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||||
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||||
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass
|
- `cargo test --workspace` to check that all tests pass
|
||||||
|
|
||||||
# Documentation
|
# After Submitting
|
||||||
|
|
||||||
- [ ] If your PR touches a user-facing nushell feature then make sure that there is an entry in the documentation (https://github.com/nushell/nushell.github.io) for the feature, and update it if necessary.
|
If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
|
||||||
|
86
.github/workflows/ci.yml
vendored
86
.github/workflows/ci.yml
vendored
@ -20,32 +20,22 @@ jobs:
|
|||||||
NUSHELL_CARGO_TARGET: ci
|
NUSHELL_CARGO_TARGET: ci
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
# makes ci use rust-toolchain.toml
|
|
||||||
# with:
|
|
||||||
# profile: minimal
|
|
||||||
# toolchain: ${{ matrix.rust }}
|
|
||||||
# override: true
|
|
||||||
# components: rustfmt, clippy
|
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v1
|
|
||||||
with:
|
|
||||||
key: "v2" # increment this to bust the cache if needed
|
|
||||||
|
|
||||||
- name: Rustfmt
|
- name: Rustfmt
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
args: --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
nu-tests:
|
nu-tests:
|
||||||
env:
|
env:
|
||||||
@ -55,39 +45,31 @@ jobs:
|
|||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-latest]
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
style: [extra, default]
|
style: [default, dataframe]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
include:
|
include:
|
||||||
- style: extra
|
|
||||||
flags: "--features=extra"
|
|
||||||
- style: default
|
- style: default
|
||||||
flags: ""
|
flags: ""
|
||||||
|
- style: dataframe
|
||||||
|
flags: "--features=dataframe"
|
||||||
exclude:
|
exclude:
|
||||||
|
# only test dataframes on Ubuntu (the fastest platform)
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
style: default
|
style: dataframe
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
style: default
|
style: dataframe
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
# makes ci use rust-toolchain.toml
|
|
||||||
# with:
|
|
||||||
# profile: minimal
|
|
||||||
# toolchain: ${{ matrix.rust }}
|
|
||||||
# override: true
|
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v1
|
|
||||||
with:
|
|
||||||
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
|
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||||
@ -108,28 +90,19 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
# makes ci use rust-toolchain.toml
|
|
||||||
# with:
|
|
||||||
# profile: minimal
|
|
||||||
# toolchain: ${{ matrix.rust }}
|
|
||||||
# override: true
|
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v1
|
|
||||||
with:
|
|
||||||
key: "2" # increment this to bust the cache if needed
|
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: install
|
command: install
|
||||||
args: --path=. --profile ci --no-default-features
|
args: --locked --path=. --profile ci --no-default-features
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
@ -159,24 +132,19 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||||
# makes ci use rust-toolchain.toml
|
|
||||||
# with:
|
|
||||||
# profile: minimal
|
|
||||||
# toolchain: ${{ matrix.rust }}
|
|
||||||
# override: true
|
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1.0.1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --profile ci --package nu_plugin_*
|
args: --profile ci --package nu_plugin_*
|
||||||
|
43
.github/workflows/release-pkg.nu
vendored
43
.github/workflows/release-pkg.nu
vendored
@ -16,8 +16,13 @@ let flags = $env.TARGET_RUSTFLAGS
|
|||||||
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
||||||
let version = (open Cargo.toml | get package.version)
|
let version = (open Cargo.toml | get package.version)
|
||||||
|
|
||||||
|
$'Debugging info:'
|
||||||
|
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
||||||
|
|
||||||
# $env
|
# $env
|
||||||
|
|
||||||
|
let USE_UBUNTU = 'ubuntu-20.04'
|
||||||
|
|
||||||
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
$'(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 }
|
||||||
|
|
||||||
@ -26,8 +31,8 @@ $'Start building ($bin)...'; hr-line
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Ubuntu and macOS
|
# Build for Ubuntu and macOS
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in ['ubuntu-latest', 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
if $os == 'ubuntu-latest' {
|
if $os == $USE_UBUNTU {
|
||||||
sudo apt-get install libxcb-composite0-dev -y
|
sudo apt-get install libxcb-composite0-dev -y
|
||||||
}
|
}
|
||||||
if $target == 'aarch64-unknown-linux-gnu' {
|
if $target == 'aarch64-unknown-linux-gnu' {
|
||||||
@ -41,7 +46,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
|
|||||||
} else {
|
} else {
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
if $os == 'ubuntu-latest' { sudo apt install musl-tools -y }
|
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
||||||
cargo-build-nu $flags
|
cargo-build-nu $flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,9 +56,9 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in ['windows-latest'] {
|
if $os in ['windows-latest'] {
|
||||||
if ($flags | str trim | is-empty) {
|
if ($flags | str trim | is-empty) {
|
||||||
cargo build --release --all --target $target --features=extra
|
cargo build --release --all --target $target
|
||||||
} else {
|
} else {
|
||||||
cargo build --release --all --target $target --features=extra $flags
|
cargo build --release --all --target $target $flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,21 +93,28 @@ if ($ver | str trim | is-empty) {
|
|||||||
# Create a release archive and send it to output for the following steps
|
# Create a release archive and send it to output for the following steps
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
||||||
if $os in ['ubuntu-latest', 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
|
|
||||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
let files = (ls | get name)
|
||||||
|
let dest = $'($bin)-($version)-($target)'
|
||||||
|
let archive = $'($dist)/($dest).tar.gz'
|
||||||
|
|
||||||
let archive = $'($dist)/($bin)-($version)-($target).tar.gz'
|
mkdir $dest
|
||||||
tar czf $archive *
|
$files | each {|it| mv $it $dest } | ignore
|
||||||
|
|
||||||
|
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
||||||
|
|
||||||
|
tar -czf $archive $dest
|
||||||
print $'archive: ---> ($archive)'; ls $archive
|
print $'archive: ---> ($archive)'; ls $archive
|
||||||
echo $'::set-output name=archive::($archive)'
|
# REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
|
||||||
|
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
||||||
|
|
||||||
} else if $os == 'windows-latest' {
|
} else if $os == 'windows-latest' {
|
||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
$'(char nl)Download less related stuffs...'; hr-line
|
$'(char nl)Download less related stuffs...'; hr-line
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
||||||
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
|
||||||
# Create Windows msi release package
|
# Create Windows msi release package
|
||||||
@ -115,7 +127,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
|
|||||||
cp -r $'($dist)/*' target/release/
|
cp -r $'($dist)/*' target/release/
|
||||||
cargo install cargo-wix --version 0.3.3
|
cargo install cargo-wix --version 0.3.3
|
||||||
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
||||||
echo $'::set-output name=archive::($wixRelease)'
|
print $'archive: ---> ($wixRelease)';
|
||||||
|
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -125,16 +138,16 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
|
|||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)';
|
||||||
let pkg = (ls -f $archive | get name)
|
let pkg = (ls -f $archive | get name)
|
||||||
if not ($pkg | is-empty) {
|
if not ($pkg | is-empty) {
|
||||||
echo $'::set-output name=archive::($pkg | get 0)'
|
echo $"archive=($pkg | get 0)" | save --append $env.GITHUB_OUTPUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'cargo-build-nu' [ options: string ] {
|
def 'cargo-build-nu' [ options: string ] {
|
||||||
if ($options | str trim | is-empty) {
|
if ($options | str trim | is-empty) {
|
||||||
cargo build --release --all --target $target --features=extra,static-link-openssl
|
cargo build --release --all --target $target --features=static-link-openssl
|
||||||
} else {
|
} else {
|
||||||
cargo build --release --all --target $target --features=extra,static-link-openssl $options
|
cargo build --release --all --target $target --features=static-link-openssl $options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@ -44,22 +44,22 @@ jobs:
|
|||||||
os: windows-latest
|
os: windows-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-latest
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-latest
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-latest
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-latest
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.0.2
|
- uses: actions/checkout@v3.1.0
|
||||||
|
|
||||||
- name: Install Rust Toolchain Components
|
- name: Install Rust Toolchain Components
|
||||||
uses: actions-rs/toolchain@v1.0.6
|
uses: actions-rs/toolchain@v1.0.6
|
||||||
@ -70,9 +70,9 @@ jobs:
|
|||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v2.1
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.69.1
|
version: 0.71.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ jobs:
|
|||||||
|
|
||||||
# REF: https://github.com/marketplace/actions/gh-release
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
- name: Publish Archive
|
- name: Publish Archive
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v0.1.13
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -22,6 +22,10 @@ debian/nu/
|
|||||||
# VSCode's IDE items
|
# VSCode's IDE items
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
|
# Visual Studio Extension SourceGear Rust items
|
||||||
|
VSWorkspaceSettings.json
|
||||||
|
unstable_cargo_features.txt
|
||||||
|
|
||||||
# Helix configuration folder
|
# Helix configuration folder
|
||||||
.helix/*
|
.helix/*
|
||||||
.helix
|
.helix
|
||||||
|
@ -1,8 +1,23 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Welcome to Nushell!
|
Welcome to Nushell and thank you for considering contributing!
|
||||||
|
|
||||||
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)!
|
## Review Process
|
||||||
|
|
||||||
|
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
|
||||||
|
This saves both your and our time if we realize the change needs to go another direction before spending time on it.
|
||||||
|
So, please, reach out and tell us what you want to do.
|
||||||
|
This will significantly increase the chance of your PR being accepted.
|
||||||
|
|
||||||
|
The review process can be summarized as follows:
|
||||||
|
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
||||||
|
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
||||||
|
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
||||||
|
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
||||||
|
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
||||||
|
6. When we all agree, the PR will be merged.
|
||||||
|
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
||||||
|
8. Congratulate yourself, you just improved Nushell! :-)
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
@ -16,6 +31,18 @@ cd nushell
|
|||||||
cargo build
|
cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
Tests can be found in different places:
|
||||||
|
* `/tests`
|
||||||
|
* `src/tests`
|
||||||
|
* command examples
|
||||||
|
* crate-specific tests
|
||||||
|
|
||||||
|
The most comprehensive test suite we have is the `nu-test-support` crate. For testing specific features, such as running Nushell in a REPL mode, we have so called "testbins". For simple tests, you can find `run_test()` and `fail_test()` functions.
|
||||||
|
|
||||||
### Useful Commands
|
### Useful Commands
|
||||||
|
|
||||||
- Build and run Nushell:
|
- Build and run Nushell:
|
||||||
@ -24,21 +51,21 @@ cargo build
|
|||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
- Build and run with extra features. Currently extra features include dataframes and sqlite database support.
|
- Build and run with dataframe support.
|
||||||
```shell
|
```shell
|
||||||
cargo run --features=extra
|
cargo run --features=dataframe
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run Clippy on Nushell:
|
- Run Clippy on Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests:
|
- Run all tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo test --workspace --features=extra
|
cargo test --workspace
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests for a specific command
|
- Run all tests for a specific command
|
||||||
@ -64,11 +91,11 @@ cargo build
|
|||||||
- To view verbose logs when developing, enable the `trace` log level.
|
- To view verbose logs when developing, enable the `trace` log level.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo run --release --features=extra -- --log-level trace
|
cargo run --release -- --log-level trace
|
||||||
```
|
```
|
||||||
|
|
||||||
- To redirect trace logs to a file, enable the `--log-target file` switch.
|
- To redirect trace logs to a file, enable the `--log-target file` switch.
|
||||||
```shell
|
```shell
|
||||||
cargo run --release --features=extra -- --log-level trace --log-target file
|
cargo run --release -- --log-level trace --log-target file
|
||||||
open $"($nu.temp-path)/nu-($nu.pid).log"
|
open $"($nu.temp-path)/nu-($nu.pid).log"
|
||||||
```
|
```
|
||||||
|
656
Cargo.lock
generated
656
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
57
Cargo.toml
57
Cargo.toml
@ -10,10 +10,17 @@ 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.70.0"
|
version = "0.72.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[package.metadata.binstall]
|
||||||
|
pkg-url = "{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }"
|
||||||
|
pkg-fmt = "tgz"
|
||||||
|
|
||||||
|
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
|
||||||
|
pkg-fmt = "zip"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/nu-cli",
|
"crates/nu-cli",
|
||||||
@ -32,27 +39,27 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.21", features = ["serde"] }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = "5.1.0"
|
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-cli = { path="./crates/nu-cli", version = "0.70.0" }
|
nu-cli = { path="./crates/nu-cli", version = "0.72.1" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.70.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.72.1" }
|
||||||
nu-command = { path="./crates/nu-command", version = "0.70.0" }
|
nu-command = { path="./crates/nu-command", version = "0.72.1" }
|
||||||
nu-engine = { path="./crates/nu-engine", version = "0.70.0" }
|
nu-engine = { path="./crates/nu-engine", version = "0.72.1" }
|
||||||
nu-json = { path="./crates/nu-json", version = "0.70.0" }
|
nu-json = { path="./crates/nu-json", version = "0.72.1" }
|
||||||
nu-parser = { path="./crates/nu-parser", version = "0.70.0" }
|
nu-parser = { path="./crates/nu-parser", version = "0.72.1" }
|
||||||
nu-path = { path="./crates/nu-path", version = "0.70.0" }
|
nu-path = { path="./crates/nu-path", version = "0.72.1" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.70.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.72.1" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.70.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.72.1" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.70.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.72.1" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.70.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.72.1" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.70.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.72.1" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.70.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.72.1" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.70.0" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.72.1" }
|
||||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
|
|
||||||
rayon = "1.5.1"
|
rayon = "1.5.1"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
@ -69,11 +76,11 @@ signal-hook = { version = "0.3.14", default-features = false }
|
|||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
[target.'cfg(target_family = "unix")'.dependencies]
|
||||||
nix = "0.24"
|
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="./crates/nu-test-support", version = "0.70.0" }
|
nu-test-support = { path="./crates/nu-test-support", version = "0.72.1" }
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
assert_cmd = "2.0.2"
|
assert_cmd = "2.0.2"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
@ -84,10 +91,12 @@ itertools = "0.10.3"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
||||||
extra = ["default", "dataframe", "database"]
|
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
|
||||||
default = ["plugin", "which-support", "trash-support"]
|
extra = ["default"]
|
||||||
|
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
||||||
stable = ["default"]
|
stable = ["default"]
|
||||||
wasi = []
|
wasi = []
|
||||||
|
|
||||||
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||||
static-link-openssl = ["dep:openssl"]
|
static-link-openssl = ["dep:openssl"]
|
||||||
|
|
||||||
@ -100,8 +109,8 @@ trash-support = ["nu-command/trash-support"]
|
|||||||
# Dataframe feature for nushell
|
# Dataframe feature for nushell
|
||||||
dataframe = ["nu-command/dataframe"]
|
dataframe = ["nu-command/dataframe"]
|
||||||
|
|
||||||
# Database commands for nushell
|
# SQLite commands for nushell
|
||||||
database = ["nu-command/database"]
|
sqlite = ["nu-command/sqlite"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # Optimize for size
|
opt-level = "s" # Optimize for size
|
||||||
|
13
README.md
13
README.md
@ -126,12 +126,13 @@ For example, you can load a .toml file as structured data and explore it:
|
|||||||
> open Cargo.toml
|
> open Cargo.toml
|
||||||
╭──────────────────┬────────────────────╮
|
╭──────────────────┬────────────────────╮
|
||||||
│ bin │ [table 1 row] │
|
│ bin │ [table 1 row] │
|
||||||
│ dependencies │ {record 24 fields} │
|
│ dependencies │ {record 25 fields} │
|
||||||
│ dev-dependencies │ {record 8 fields} │
|
│ dev-dependencies │ {record 8 fields} │
|
||||||
│ features │ {record 10 fields} │
|
│ features │ {record 10 fields} │
|
||||||
│ package │ {record 13 fields} │
|
│ package │ {record 13 fields} │
|
||||||
|
│ patch │ {record 1 field} │
|
||||||
│ profile │ {record 3 fields} │
|
│ profile │ {record 3 fields} │
|
||||||
│ target │ {record 2 fields} │
|
│ target │ {record 3 fields} │
|
||||||
│ workspace │ {record 1 field} │
|
│ workspace │ {record 1 field} │
|
||||||
╰──────────────────┴────────────────────╯
|
╰──────────────────┴────────────────────╯
|
||||||
```
|
```
|
||||||
@ -149,11 +150,11 @@ We can pipe this into a command that gets the contents of one of the columns:
|
|||||||
│ exclude │ [list 1 item] │
|
│ exclude │ [list 1 item] │
|
||||||
│ homepage │ https://www.nushell.sh │
|
│ homepage │ https://www.nushell.sh │
|
||||||
│ license │ MIT │
|
│ license │ MIT │
|
||||||
|
│ metadata │ {record 1 field} │
|
||||||
│ name │ nu │
|
│ name │ nu │
|
||||||
│ readme │ README.md │
|
|
||||||
│ repository │ https://github.com/nushell/nushell │
|
│ repository │ https://github.com/nushell/nushell │
|
||||||
│ rust-version │ 1.60 │
|
│ rust-version │ 1.60 │
|
||||||
│ version │ 0.63.1 │
|
│ version │ 0.72.0 │
|
||||||
╰───────────────┴────────────────────────────────────╯
|
╰───────────────┴────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ And if needed we can drill down further:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml | get package.version
|
> open Cargo.toml | get package.version
|
||||||
0.63.1
|
0.72.0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Plugins
|
### Plugins
|
||||||
@ -206,7 +207,7 @@ Nu is under heavy development and will naturally change as it matures. The chart
|
|||||||
| Functions | | | | X | | Functions and aliases are supported |
|
| Functions | | | | X | | Functions and aliases are supported |
|
||||||
| Variables | | | | X | | Nu supports variables and environment variables |
|
| Variables | | | | X | | Nu supports variables and environment variables |
|
||||||
| Completions | | | | X | | Completions for filepaths |
|
| Completions | | | | X | | Completions for filepaths |
|
||||||
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked |
|
| Type-checking | | | | x | | Commands check basic types, and input/output types |
|
||||||
|
|
||||||
## Officially Supported By
|
## Officially Supported By
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
echo "Building nushell (nu) with --features=extra and all the plugins"
|
echo "Building nushell (nu) with dataframes and all the plugins"
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@ -14,10 +15,10 @@ NU_PLUGINS=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
echo "Building nushell"
|
echo "Building nushell"
|
||||||
cargo build --features=extra
|
cargo build --features=dataframe
|
||||||
for plugin in "${NU_PLUGINS[@]}"
|
for plugin in "${NU_PLUGINS[@]}"
|
||||||
do
|
do
|
||||||
echo '' && cd crates/$plugin
|
echo '' && cd crates/"$plugin"
|
||||||
echo "Building $plugin..."
|
echo "Building $plugin..."
|
||||||
echo "-----------------------------"
|
echo "-----------------------------"
|
||||||
cargo build && cd ../..
|
cargo build && cd ../..
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
@echo off
|
@echo off
|
||||||
@echo -------------------------------------------------------------------
|
@echo -------------------------------------------------------------------
|
||||||
@echo Building nushell (nu.exe) with --features=extra and all the plugins
|
@echo Building nushell (nu.exe) with dataframes and all the plugins
|
||||||
@echo -------------------------------------------------------------------
|
@echo -------------------------------------------------------------------
|
||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
echo Building nushell.exe
|
echo Building nushell.exe
|
||||||
cargo build --features=extra
|
cargo build cargo build --features=dataframe
|
||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
@cd crates\nu_plugin_example
|
@cd crates\nu_plugin_example
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
echo '-------------------------------------------------------------------'
|
echo '-------------------------------------------------------------------'
|
||||||
echo 'Building nushell (nu) with --features=extra and all the plugins'
|
echo 'Building nushell (nu) with dataframes and all the plugins'
|
||||||
echo '-------------------------------------------------------------------'
|
echo '-------------------------------------------------------------------'
|
||||||
|
|
||||||
echo $'(char nl)Building nushell'
|
echo $'(char nl)Building nushell'
|
||||||
echo '----------------------------'
|
echo '----------------------------'
|
||||||
cargo build --features=extra
|
cargo build --features=dataframe
|
||||||
|
|
||||||
let plugins = [
|
let plugins = [
|
||||||
nu_plugin_inc,
|
nu_plugin_inc,
|
||||||
|
@ -5,34 +5,33 @@ 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.70.0"
|
version = "0.72.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.70.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.72.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.70.0" }
|
nu-command = { path = "../nu-command", version = "0.72.1" }
|
||||||
rstest = {version = "0.15.0", default-features = false}
|
rstest = {version = "0.15.0", default-features = false}
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.70.0" }
|
nu-engine = { path = "../nu-engine", version = "0.72.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.70.0" }
|
nu-path = { path = "../nu-path", version = "0.72.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.70.0" }
|
nu-parser = { path = "../nu-parser", version = "0.72.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.70.0" }
|
nu-utils = { path = "../nu-utils", version = "0.72.1" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.70.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.72.1" }
|
||||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
|
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
chrono = "0.4.21"
|
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
fancy-regex = "0.10.0"
|
fancy-regex = "0.10.0"
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.1.0", features = ["fancy"] }
|
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
strip-ansi-escapes = "0.1.1"
|
|
||||||
sysinfo = "0.26.2"
|
sysinfo = "0.26.2"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ pub fn evaluate_commands(
|
|||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
is_perf_true: bool,
|
|
||||||
table_mode: Option<Value>,
|
table_mode: Option<Value>,
|
||||||
) -> Result<Option<i64>> {
|
) -> Result<Option<i64>> {
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
@ -68,9 +67,7 @@ pub fn evaluate_commands(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_perf_true {
|
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(exit_code)
|
Ok(exit_code)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ impl CommandCompletion {
|
|||||||
.matches_str(&x.to_string_lossy(), prefix)),
|
.matches_str(&x.to_string_lossy(), prefix)),
|
||||||
Some(true)
|
Some(true)
|
||||||
)
|
)
|
||||||
&& is_executable::is_executable(&item.path())
|
&& is_executable::is_executable(item.path())
|
||||||
{
|
{
|
||||||
if let Ok(name) = item.file_name().into_string() {
|
if let Ok(name) = item.file_name().into_string() {
|
||||||
executables.push(name);
|
executables.push(name);
|
||||||
|
@ -5,6 +5,7 @@ use crate::completions::{
|
|||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_expression, parse, FlatShape};
|
use nu_parser::{flatten_expression, parse, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
ast::PipelineElement,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
BlockId, PipelineData, Span, Value,
|
BlockId, PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
@ -131,139 +132,62 @@ impl NuCompleter {
|
|||||||
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
||||||
|
|
||||||
for pipeline in output.pipelines.into_iter() {
|
for pipeline in output.pipelines.into_iter() {
|
||||||
for expr in pipeline.expressions {
|
for pipeline_element in pipeline.elements {
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
match pipeline_element {
|
||||||
let span_offset: usize = alias_offset.iter().sum();
|
PipelineElement::Expression(_, expr)
|
||||||
let mut spans: Vec<String> = vec![];
|
| PipelineElement::Redirection(_, _, expr)
|
||||||
|
| PipelineElement::And(_, expr)
|
||||||
|
| PipelineElement::Or(_, expr) => {
|
||||||
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
|
let span_offset: usize = alias_offset.iter().sum();
|
||||||
|
let mut spans: Vec<String> = vec![];
|
||||||
|
|
||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
// 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();
|
||||||
let current_span_str = String::from_utf8_lossy(¤t_span);
|
let current_span_str = String::from_utf8_lossy(¤t_span);
|
||||||
|
|
||||||
// Skip the last 'a' as span item
|
// Skip the last 'a' as span item
|
||||||
if flat_idx == flattened.len() - 1 {
|
if flat_idx == flattened.len() - 1 {
|
||||||
let mut chars = current_span_str.chars();
|
let mut chars = current_span_str.chars();
|
||||||
chars.next_back();
|
chars.next_back();
|
||||||
let current_span_str = chars.as_str().to_owned();
|
let current_span_str = chars.as_str().to_owned();
|
||||||
spans.push(current_span_str.to_string());
|
spans.push(current_span_str.to_string());
|
||||||
} else {
|
} else {
|
||||||
spans.push(current_span_str.to_string());
|
spans.push(current_span_str.to_string());
|
||||||
}
|
|
||||||
|
|
||||||
// Complete based on the last span
|
|
||||||
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
|
||||||
// Context variables
|
|
||||||
let most_left_var =
|
|
||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
|
||||||
|
|
||||||
// Create a new span
|
|
||||||
let new_span = if flat_idx == 0 {
|
|
||||||
Span {
|
|
||||||
start: flat.0.start,
|
|
||||||
end: flat.0.end - 1 - span_offset,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Span {
|
|
||||||
start: flat.0.start - span_offset,
|
|
||||||
end: flat.0.end - 1 - span_offset,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
|
||||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
|
||||||
let index = pos - (flat.0.start - span_offset);
|
|
||||||
prefix.drain(index..);
|
|
||||||
|
|
||||||
// Variables completion
|
|
||||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
|
||||||
let mut completer = VariableCompletion::new(
|
|
||||||
self.engine_state.clone(),
|
|
||||||
self.stack.clone(),
|
|
||||||
most_left_var.unwrap_or((vec![], vec![])),
|
|
||||||
);
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flags completion
|
|
||||||
if prefix.starts_with(b"-") {
|
|
||||||
// Try to complete flag internally
|
|
||||||
let mut completer = FlagCompletion::new(expr.clone());
|
|
||||||
let result = self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix.clone(),
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !result.is_empty() {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We got no results for internal completion
|
// Complete based on the last span
|
||||||
// now we can check if external completer is set and use it
|
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
||||||
if let Some(block_id) = config.external_completer {
|
// Context variables
|
||||||
if let Some(external_result) =
|
let most_left_var =
|
||||||
self.external_completion(block_id, &spans, offset, new_span)
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
{
|
|
||||||
return external_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// specially check if it is currently empty - always complete commands
|
// Create a new span
|
||||||
if flat_idx == 0 && working_set.get_span_contents(new_span).is_empty() {
|
let new_span = if flat_idx == 0 {
|
||||||
let mut completer = CommandCompletion::new(
|
Span {
|
||||||
self.engine_state.clone(),
|
start: flat.0.start,
|
||||||
&working_set,
|
end: flat.0.end - 1 - span_offset,
|
||||||
flattened.clone(),
|
}
|
||||||
// flat_idx,
|
} else {
|
||||||
FlatShape::String,
|
Span {
|
||||||
true,
|
start: flat.0.start - span_offset,
|
||||||
);
|
end: flat.0.end - 1 - span_offset,
|
||||||
return self.process_completion(
|
}
|
||||||
&mut completer,
|
};
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Completions that depends on the previous expression (e.g: use, source-env)
|
// Parses the prefix. Completion should look up to the cursor position, not after.
|
||||||
if flat_idx > 0 {
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
let index = pos - (flat.0.start - span_offset);
|
||||||
// Read the content for the previous expression
|
prefix.drain(index..);
|
||||||
let prev_expr_str =
|
|
||||||
working_set.get_span_contents(previous_expr.0).to_vec();
|
|
||||||
|
|
||||||
// Completion for .nu files
|
// Variables completion
|
||||||
if prev_expr_str == b"use" || prev_expr_str == b"source-env" {
|
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||||
let mut completer =
|
let mut completer = VariableCompletion::new(
|
||||||
DotNuCompletion::new(self.engine_state.clone());
|
self.engine_state.clone(),
|
||||||
|
self.stack.clone(),
|
||||||
return self.process_completion(
|
most_left_var.unwrap_or((vec![], vec![])),
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
);
|
||||||
} else if prev_expr_str == b"ls" {
|
|
||||||
let mut completer =
|
|
||||||
FileCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
return self.process_completion(
|
||||||
&mut completer,
|
&mut completer,
|
||||||
@ -274,101 +198,190 @@ impl NuCompleter {
|
|||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match other types
|
// Flags completion
|
||||||
match &flat.1 {
|
if prefix.starts_with(b"-") {
|
||||||
FlatShape::Custom(decl_id) => {
|
// Try to complete flag internally
|
||||||
let mut completer = CustomCompletion::new(
|
let mut completer = FlagCompletion::new(expr.clone());
|
||||||
self.engine_state.clone(),
|
let result = self.process_completion(
|
||||||
self.stack.clone(),
|
&mut completer,
|
||||||
*decl_id,
|
&working_set,
|
||||||
initial_line,
|
prefix.clone(),
|
||||||
);
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
|
||||||
return self.process_completion(
|
if !result.is_empty() {
|
||||||
&mut completer,
|
return result;
|
||||||
&working_set,
|
}
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
FlatShape::Directory => {
|
|
||||||
let mut completer =
|
|
||||||
DirectoryCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
// We got no results for internal completion
|
||||||
&mut completer,
|
// now we can check if external completer is set and use it
|
||||||
&working_set,
|
if let Some(block_id) = config.external_completer {
|
||||||
prefix,
|
if let Some(external_result) = self
|
||||||
new_span,
|
.external_completion(block_id, &spans, offset, new_span)
|
||||||
offset,
|
{
|
||||||
pos,
|
return external_result;
|
||||||
);
|
}
|
||||||
}
|
|
||||||
FlatShape::Filepath | FlatShape::GlobPattern => {
|
|
||||||
let mut completer = FileCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
flat_shape => {
|
|
||||||
let mut completer = CommandCompletion::new(
|
|
||||||
self.engine_state.clone(),
|
|
||||||
&working_set,
|
|
||||||
flattened.clone(),
|
|
||||||
// flat_idx,
|
|
||||||
flat_shape.clone(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut out: Vec<_> = self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix.clone(),
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !out.is_empty() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to complete using an external completer (if set)
|
|
||||||
if let Some(block_id) = config.external_completer {
|
|
||||||
if let Some(external_result) =
|
|
||||||
self.external_completion(block_id, &spans, offset, new_span)
|
|
||||||
{
|
|
||||||
return external_result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for file completion
|
// specially check if it is currently empty - always complete commands
|
||||||
let mut completer = FileCompletion::new(self.engine_state.clone());
|
if flat_idx == 0
|
||||||
out = self.process_completion(
|
&& working_set.get_span_contents(new_span).is_empty()
|
||||||
&mut completer,
|
{
|
||||||
&working_set,
|
let mut completer = CommandCompletion::new(
|
||||||
prefix,
|
self.engine_state.clone(),
|
||||||
new_span,
|
&working_set,
|
||||||
offset,
|
flattened.clone(),
|
||||||
pos,
|
// flat_idx,
|
||||||
);
|
FlatShape::String,
|
||||||
|
true,
|
||||||
if !out.is_empty() {
|
);
|
||||||
return out;
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Completions that depends on the previous expression (e.g: use, source-env)
|
||||||
|
if flat_idx > 0 {
|
||||||
|
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
||||||
|
// Read the content for the previous expression
|
||||||
|
let prev_expr_str =
|
||||||
|
working_set.get_span_contents(previous_expr.0).to_vec();
|
||||||
|
|
||||||
|
// Completion for .nu files
|
||||||
|
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
|
||||||
|
{
|
||||||
|
let mut completer =
|
||||||
|
DotNuCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
} else if prev_expr_str == b"ls" {
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match other types
|
||||||
|
match &flat.1 {
|
||||||
|
FlatShape::Custom(decl_id) => {
|
||||||
|
let mut completer = CustomCompletion::new(
|
||||||
|
self.engine_state.clone(),
|
||||||
|
self.stack.clone(),
|
||||||
|
*decl_id,
|
||||||
|
initial_line,
|
||||||
|
);
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FlatShape::Directory => {
|
||||||
|
let mut completer =
|
||||||
|
DirectoryCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FlatShape::Filepath | FlatShape::GlobPattern => {
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
flat_shape => {
|
||||||
|
let mut completer = CommandCompletion::new(
|
||||||
|
self.engine_state.clone(),
|
||||||
|
&working_set,
|
||||||
|
flattened.clone(),
|
||||||
|
// flat_idx,
|
||||||
|
flat_shape.clone(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut out: Vec<_> = self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix.clone(),
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !out.is_empty() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to complete using an external completer (if set)
|
||||||
|
if let Some(block_id) = config.external_completer {
|
||||||
|
if let Some(external_result) = self.external_completion(
|
||||||
|
block_id, &spans, offset, new_span,
|
||||||
|
) {
|
||||||
|
return external_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for file completion
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
out = self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !out.is_empty() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ pub fn read_plugin_file(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
is_perf_true: bool,
|
|
||||||
) {
|
) {
|
||||||
// Reading signatures from signature file
|
// Reading signatures from signature file
|
||||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||||
@ -31,7 +30,7 @@ pub fn read_plugin_file(
|
|||||||
|
|
||||||
let plugin_path = engine_state.plugin_signatures.clone();
|
let plugin_path = engine_state.plugin_signatures.clone();
|
||||||
if let Some(plugin_path) = plugin_path {
|
if let Some(plugin_path) = plugin_path {
|
||||||
let plugin_filename = plugin_path.to_string_lossy().to_owned();
|
let plugin_filename = plugin_path.to_string_lossy();
|
||||||
|
|
||||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
@ -44,9 +43,7 @@ pub fn read_plugin_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_perf_true {
|
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@ -80,7 +77,7 @@ pub fn eval_config_contents(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) {
|
) {
|
||||||
if config_path.exists() & config_path.is_file() {
|
if config_path.exists() & config_path.is_file() {
|
||||||
let config_filename = config_path.to_string_lossy().to_owned();
|
let config_filename = config_path.to_string_lossy();
|
||||||
|
|
||||||
if let Ok(contents) = std::fs::read(&config_path) {
|
if let Ok(contents) = std::fs::read(&config_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
|
@ -19,7 +19,6 @@ pub fn evaluate_file(
|
|||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
is_perf_true: bool,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
@ -30,6 +29,8 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let file = std::fs::read(&path).into_diagnostic()?;
|
let file = std::fs::read(&path).into_diagnostic()?;
|
||||||
|
|
||||||
|
engine_state.start_in_file(Some(&path));
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
trace!("parsing file: {}", path);
|
trace!("parsing file: {}", path);
|
||||||
|
|
||||||
@ -54,9 +55,7 @@ pub fn evaluate_file(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_perf_true {
|
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -75,6 +74,14 @@ pub fn print_table_or_error(
|
|||||||
// Change the engine_state config to use the passed in configuration
|
// Change the engine_state config to use the passed in configuration
|
||||||
engine_state.set_config(config);
|
engine_state.set_config(config);
|
||||||
|
|
||||||
|
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
report_error(&working_set, error);
|
||||||
|
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||||
Some(decl_id) => {
|
Some(decl_id) => {
|
||||||
let command = engine_state.get_decl(decl_id);
|
let command = engine_state.get_decl(decl_id);
|
||||||
|
@ -372,7 +372,7 @@ impl DescriptionMenu {
|
|||||||
let description = self
|
let description = self
|
||||||
.get_value()
|
.get_value()
|
||||||
.and_then(|suggestion| suggestion.description)
|
.and_then(|suggestion| suggestion.description)
|
||||||
.unwrap_or_else(|| "".to_string())
|
.unwrap_or_default()
|
||||||
.lines()
|
.lines()
|
||||||
.skip(self.skipped_rows)
|
.skip(self.skipped_rows)
|
||||||
.take(self.working_details.description_rows)
|
.take(self.working_details.description_rows)
|
||||||
@ -610,7 +610,7 @@ impl Menu for DescriptionMenu {
|
|||||||
let description_rows = self
|
let description_rows = self
|
||||||
.get_value()
|
.get_value()
|
||||||
.and_then(|suggestion| suggestion.description)
|
.and_then(|suggestion| suggestion.description)
|
||||||
.unwrap_or_else(|| "".to_string())
|
.unwrap_or_default()
|
||||||
.lines()
|
.lines()
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ impl NuHelpCompleter {
|
|||||||
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||||
let mut commands = full_commands
|
let mut commands = full_commands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(sig, _, _, _)| {
|
.filter(|(sig, _, _, _, _)| {
|
||||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig
|
|| sig
|
||||||
@ -31,7 +31,7 @@ impl NuHelpCompleter {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
commands.sort_by(|(a, _, _, _), (b, _, _, _)| {
|
commands.sort_by(|(a, _, _, _, _), (b, _, _, _, _)| {
|
||||||
let a_distance = levenshtein_distance(line, &a.name);
|
let a_distance = levenshtein_distance(line, &a.name);
|
||||||
let b_distance = levenshtein_distance(line, &b.name);
|
let b_distance = levenshtein_distance(line, &b.name);
|
||||||
a_distance.cmp(&b_distance)
|
a_distance.cmp(&b_distance)
|
||||||
@ -39,7 +39,7 @@ impl NuHelpCompleter {
|
|||||||
|
|
||||||
commands
|
commands
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(sig, examples, _, _)| {
|
.map(|(sig, examples, _, _, _)| {
|
||||||
let mut long_desc = String::new();
|
let mut long_desc = String::new();
|
||||||
|
|
||||||
let usage = &sig.usage;
|
let usage = &sig.usage;
|
||||||
|
@ -17,6 +17,7 @@ pub struct NushellPrompt {
|
|||||||
default_vi_insert_prompt_indicator: Option<String>,
|
default_vi_insert_prompt_indicator: Option<String>,
|
||||||
default_vi_normal_prompt_indicator: Option<String>,
|
default_vi_normal_prompt_indicator: Option<String>,
|
||||||
default_multiline_indicator: Option<String>,
|
default_multiline_indicator: Option<String>,
|
||||||
|
render_right_prompt_on_last_line: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NushellPrompt {
|
impl Default for NushellPrompt {
|
||||||
@ -34,6 +35,7 @@ impl NushellPrompt {
|
|||||||
default_vi_insert_prompt_indicator: None,
|
default_vi_insert_prompt_indicator: None,
|
||||||
default_vi_normal_prompt_indicator: None,
|
default_vi_normal_prompt_indicator: None,
|
||||||
default_multiline_indicator: None,
|
default_multiline_indicator: None,
|
||||||
|
render_right_prompt_on_last_line: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +43,13 @@ impl NushellPrompt {
|
|||||||
self.left_prompt_string = prompt_string;
|
self.left_prompt_string = prompt_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_prompt_right(&mut self, prompt_string: Option<String>) {
|
pub fn update_prompt_right(
|
||||||
|
&mut self,
|
||||||
|
prompt_string: Option<String>,
|
||||||
|
render_right_prompt_on_last_line: bool,
|
||||||
|
) {
|
||||||
self.right_prompt_string = prompt_string;
|
self.right_prompt_string = prompt_string;
|
||||||
|
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
|
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
|
||||||
@ -68,6 +75,7 @@ impl NushellPrompt {
|
|||||||
prompt_indicator_string: Option<String>,
|
prompt_indicator_string: Option<String>,
|
||||||
prompt_multiline_indicator_string: Option<String>,
|
prompt_multiline_indicator_string: Option<String>,
|
||||||
prompt_vi: (Option<String>, Option<String>),
|
prompt_vi: (Option<String>, Option<String>),
|
||||||
|
render_right_prompt_on_last_line: bool,
|
||||||
) {
|
) {
|
||||||
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
|
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
|
||||||
|
|
||||||
@ -78,6 +86,8 @@ impl NushellPrompt {
|
|||||||
|
|
||||||
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
|
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
|
||||||
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
|
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
|
||||||
|
|
||||||
|
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_wrapped_custom_string(&self, str: String) -> String {
|
fn default_wrapped_custom_string(&self, str: String) -> String {
|
||||||
@ -162,4 +172,8 @@ impl Prompt for NushellPrompt {
|
|||||||
prefix, history_search.term
|
prefix, history_search.term
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn right_prompt_on_last_line(&self) -> bool {
|
||||||
|
self.render_right_prompt_on_last_line
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,11 @@ fn get_prompt_string(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
is_perf_true: bool,
|
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
stack
|
stack
|
||||||
.get_env_var(engine_state, prompt)
|
.get_env_var(engine_state, prompt)
|
||||||
.and_then(|v| match v {
|
.and_then(|v| match v {
|
||||||
Value::Block {
|
Value::Closure {
|
||||||
val: block_id,
|
val: block_id,
|
||||||
captures,
|
captures,
|
||||||
..
|
..
|
||||||
@ -44,14 +43,37 @@ fn get_prompt_string(
|
|||||||
block,
|
block,
|
||||||
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||||
);
|
);
|
||||||
if is_perf_true {
|
info!(
|
||||||
info!(
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
file!(),
|
||||||
file!(),
|
line!(),
|
||||||
line!(),
|
column!()
|
||||||
column!()
|
);
|
||||||
);
|
|
||||||
|
match ret_val {
|
||||||
|
Ok(ret_val) => Some(ret_val),
|
||||||
|
Err(err) => {
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
report_error(&working_set, &err);
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Value::Block { val: block_id, .. } => {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
||||||
|
let ret_val = eval_subexpression(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block,
|
||||||
|
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!()
|
||||||
|
);
|
||||||
|
|
||||||
match ret_val {
|
match ret_val {
|
||||||
Ok(ret_val) => Some(ret_val),
|
Ok(ret_val) => Some(ret_val),
|
||||||
@ -90,17 +112,10 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
nu_prompt: &'prompt mut NushellPrompt,
|
nu_prompt: &'prompt mut NushellPrompt,
|
||||||
is_perf_true: bool,
|
|
||||||
) -> &'prompt dyn Prompt {
|
) -> &'prompt dyn Prompt {
|
||||||
let mut stack = stack.clone();
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
let left_prompt_string = get_prompt_string(
|
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
|
||||||
PROMPT_COMMAND,
|
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now that we have the prompt string lets ansify it.
|
// Now that we have the prompt string lets ansify it.
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
@ -116,45 +131,20 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
left_prompt_string
|
left_prompt_string
|
||||||
};
|
};
|
||||||
|
|
||||||
let right_prompt_string = get_prompt_string(
|
let right_prompt_string =
|
||||||
PROMPT_COMMAND_RIGHT,
|
get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, &mut stack);
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let prompt_indicator_string = get_prompt_string(
|
let prompt_indicator_string =
|
||||||
PROMPT_INDICATOR,
|
get_prompt_string(PROMPT_INDICATOR, config, engine_state, &mut stack);
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let prompt_multiline_string = get_prompt_string(
|
let prompt_multiline_string =
|
||||||
PROMPT_MULTILINE_INDICATOR,
|
get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, &mut stack);
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let prompt_vi_insert_string = get_prompt_string(
|
let prompt_vi_insert_string =
|
||||||
PROMPT_INDICATOR_VI_INSERT,
|
get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, &mut stack);
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let prompt_vi_normal_string = get_prompt_string(
|
let prompt_vi_normal_string =
|
||||||
PROMPT_INDICATOR_VI_NORMAL,
|
get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, &mut stack);
|
||||||
config,
|
|
||||||
engine_state,
|
|
||||||
&mut stack,
|
|
||||||
is_perf_true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// apply the other indicators
|
// apply the other indicators
|
||||||
nu_prompt.update_all_prompt_strings(
|
nu_prompt.update_all_prompt_strings(
|
||||||
@ -163,12 +153,11 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
prompt_indicator_string,
|
prompt_indicator_string,
|
||||||
prompt_multiline_string,
|
prompt_multiline_string,
|
||||||
(prompt_vi_insert_string, prompt_vi_normal_string),
|
(prompt_vi_insert_string, prompt_vi_normal_string),
|
||||||
|
config.render_right_prompt_on_last_line,
|
||||||
);
|
);
|
||||||
|
|
||||||
let ret_val = nu_prompt as &dyn Prompt;
|
let ret_val = nu_prompt as &dyn Prompt;
|
||||||
if is_perf_true {
|
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ pub(crate) fn add_menus(
|
|||||||
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
||||||
|
|
||||||
if let PipelineData::Value(value, None) = res {
|
if let PipelineData::Value(value, None) = res {
|
||||||
for menu in create_menus(&value, config)? {
|
for menu in create_menus(&value)? {
|
||||||
line_editor =
|
line_editor =
|
||||||
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
||||||
}
|
}
|
||||||
@ -251,7 +251,7 @@ pub(crate) fn add_columnar_menu(
|
|||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
|
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
|
||||||
}
|
}
|
||||||
Value::Block {
|
Value::Closure {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@ -337,7 +337,7 @@ pub(crate) fn add_list_menu(
|
|||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
|
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
|
||||||
}
|
}
|
||||||
Value::Block {
|
Value::Closure {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@ -459,7 +459,7 @@ pub(crate) fn add_description_menu(
|
|||||||
completer,
|
completer,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Value::Block {
|
Value::Closure {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@ -477,7 +477,7 @@ pub(crate) fn add_description_menu(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::UnsupportedConfigValue(
|
_ => Err(ShellError::UnsupportedConfigValue(
|
||||||
"block or omitted value".to_string(),
|
"closure or omitted value".to_string(),
|
||||||
menu.source.into_abbreviated_string(config),
|
menu.source.into_abbreviated_string(config),
|
||||||
menu.source.span()?,
|
menu.source.span()?,
|
||||||
)),
|
)),
|
||||||
@ -491,7 +491,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
|||||||
KeyCode::Tab,
|
KeyCode::Tab,
|
||||||
ReedlineEvent::UntilFound(vec![
|
ReedlineEvent::UntilFound(vec![
|
||||||
ReedlineEvent::Menu("completion_menu".to_string()),
|
ReedlineEvent::Menu("completion_menu".to_string()),
|
||||||
ReedlineEvent::MenuNext,
|
ReedlineEvent::Edit(vec![EditCommand::Complete]),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -964,6 +964,7 @@ fn edit_from_record(
|
|||||||
let char = extract_char(value, config)?;
|
let char = extract_char(value, config)?;
|
||||||
EditCommand::MoveLeftBefore(char)
|
EditCommand::MoveLeftBefore(char)
|
||||||
}
|
}
|
||||||
|
"complete" => EditCommand::Complete,
|
||||||
e => {
|
e => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"reedline EditCommand".to_string(),
|
"reedline EditCommand".to_string(),
|
||||||
|
@ -10,7 +10,7 @@ use lazy_static::lazy_static;
|
|||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::get_color_config;
|
||||||
use nu_engine::{convert_env_values, eval_block};
|
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::PathMember,
|
ast::PathMember,
|
||||||
@ -24,7 +24,6 @@ use std::{
|
|||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
use strip_ansi_escapes::strip;
|
|
||||||
use sysinfo::SystemExt;
|
use sysinfo::SystemExt;
|
||||||
|
|
||||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||||
@ -41,7 +40,6 @@ pub fn evaluate_repl(
|
|||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
nushell_path: &str,
|
nushell_path: &str,
|
||||||
is_perf_true: bool,
|
|
||||||
prerun_command: Option<Spanned<String>>,
|
prerun_command: Option<Spanned<String>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||||
@ -51,7 +49,7 @@ pub fn evaluate_repl(
|
|||||||
if !atty::is(atty::Stream::Stdin) {
|
if !atty::is(atty::Stream::Stdin) {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::NotFound,
|
std::io::ErrorKind::NotFound,
|
||||||
"Nushell launched as interactive REPL but STDIN is not a TTY, either launch in a valid terminal or provide arguments to invoke a script!",
|
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
|
||||||
))
|
))
|
||||||
.into_diagnostic();
|
.into_diagnostic();
|
||||||
}
|
}
|
||||||
@ -60,14 +58,12 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let mut nu_prompt = NushellPrompt::new();
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
|
||||||
if is_perf_true {
|
info!(
|
||||||
info!(
|
"translate environment vars {}:{}:{}",
|
||||||
"translate environment vars {}:{}:{}",
|
file!(),
|
||||||
file!(),
|
line!(),
|
||||||
line!(),
|
column!()
|
||||||
column!()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
@ -92,18 +88,14 @@ pub fn evaluate_repl(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if is_perf_true {
|
info!(
|
||||||
info!(
|
"load config initially {}:{}:{}",
|
||||||
"load config initially {}:{}:{}",
|
file!(),
|
||||||
file!(),
|
line!(),
|
||||||
line!(),
|
column!()
|
||||||
column!()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_perf_true {
|
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut line_editor = Reedline::create();
|
let mut line_editor = Reedline::create();
|
||||||
|
|
||||||
@ -121,9 +113,7 @@ pub fn evaluate_repl(
|
|||||||
engine_state.config.history_file_format,
|
engine_state.config.history_file_format,
|
||||||
);
|
);
|
||||||
if let Some(history_path) = history_path.as_deref() {
|
if let Some(history_path) = history_path.as_deref() {
|
||||||
if is_perf_true {
|
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
||||||
HistoryFileFormat::PlainText => Box::new(
|
HistoryFileFormat::PlainText => Box::new(
|
||||||
@ -149,15 +139,7 @@ pub fn evaluate_repl(
|
|||||||
if use_ansi {
|
if use_ansi {
|
||||||
println!("{}", banner);
|
println!("{}", banner);
|
||||||
} else {
|
} else {
|
||||||
let stripped_string = {
|
println!("{}", nu_utils::strip_ansi_string_likely(banner));
|
||||||
if let Ok(bytes) = strip(&banner) {
|
|
||||||
String::from_utf8_lossy(&bytes).to_string()
|
|
||||||
} else {
|
|
||||||
banner
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{}", stripped_string);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,14 +155,12 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if is_perf_true {
|
info!(
|
||||||
info!(
|
"load config each loop {}:{}:{}",
|
||||||
"load config each loop {}:{}:{}",
|
file!(),
|
||||||
file!(),
|
line!(),
|
||||||
line!(),
|
column!()
|
||||||
column!()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||||
|
|
||||||
@ -201,15 +181,11 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
if is_perf_true {
|
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
let color_hm = get_color_config(config);
|
let color_hm = get_color_config(config);
|
||||||
|
|
||||||
if is_perf_true {
|
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
||||||
line_editor = line_editor
|
line_editor = line_editor
|
||||||
.with_highlighter(Box::new(NuHighlighter {
|
.with_highlighter(Box::new(NuHighlighter {
|
||||||
@ -266,18 +242,14 @@ pub fn evaluate_repl(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if config.sync_history_on_enter {
|
if config.sync_history_on_enter {
|
||||||
if is_perf_true {
|
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = line_editor.sync_history() {
|
if let Err(e) = line_editor.sync_history() {
|
||||||
warn!("Failed to sync history: {}", e);
|
warn!("Failed to sync history: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_perf_true {
|
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing the line editor based on the found keybindings
|
// Changing the line editor based on the found keybindings
|
||||||
line_editor = match create_keybindings(config) {
|
line_editor = match create_keybindings(config) {
|
||||||
@ -301,14 +273,12 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_perf_true {
|
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
||||||
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Right before we start our prompt and take input from the user,
|
// Right before we start our prompt and take input from the user,
|
||||||
// fire the "pre_prompt" hook
|
// fire the "pre_prompt" hook
|
||||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
||||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,19 +293,16 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let prompt =
|
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||||
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
|
|
||||||
|
|
||||||
entry_num += 1;
|
entry_num += 1;
|
||||||
|
|
||||||
if is_perf_true {
|
info!(
|
||||||
info!(
|
"finished setup, starting repl {}:{}:{}",
|
||||||
"finished setup, starting repl {}:{}:{}",
|
file!(),
|
||||||
file!(),
|
line!(),
|
||||||
line!(),
|
column!()
|
||||||
column!()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let input = line_editor.read_line(prompt);
|
let input = line_editor.read_line(prompt);
|
||||||
let shell_integration = config.shell_integration;
|
let shell_integration = config.shell_integration;
|
||||||
@ -367,7 +334,7 @@ pub fn evaluate_repl(
|
|||||||
// Right before we start running the code the user gave us,
|
// Right before we start running the code the user gave us,
|
||||||
// fire the "pre_execution" hook
|
// fire the "pre_execution" hook
|
||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,7 +528,7 @@ pub fn evaluate_repl(
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
let message = err.to_string();
|
let message = err.to_string();
|
||||||
if !message.contains("duration") {
|
if !message.contains("duration") {
|
||||||
println!("Error: {:?}", err);
|
eprintln!("Error: {:?}", err);
|
||||||
// TODO: Identify possible error cases where a hard failure is preferable
|
// TODO: Identify possible error cases where a hard failure is preferable
|
||||||
// Ignoring and reporting could hide bigger problems
|
// Ignoring and reporting could hide bigger problems
|
||||||
// e.g. https://github.com/nushell/nushell/issues/6452
|
// e.g. https://github.com/nushell/nushell/issues/6452
|
||||||
@ -719,6 +686,7 @@ pub fn eval_env_change_hook(
|
|||||||
eval_hook(
|
eval_hook(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
None,
|
||||||
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
||||||
hook_value,
|
hook_value,
|
||||||
)?;
|
)?;
|
||||||
@ -744,15 +712,17 @@ pub fn eval_env_change_hook(
|
|||||||
pub fn eval_hook(
|
pub fn eval_hook(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
input: Option<PipelineData>,
|
||||||
arguments: Vec<(String, Value)>,
|
arguments: Vec<(String, Value)>,
|
||||||
value: &Value,
|
value: &Value,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value_span = value.span()?;
|
let value_span = value.span()?;
|
||||||
|
|
||||||
let condition_path = PathMember::String {
|
let condition_path = PathMember::String {
|
||||||
val: "condition".to_string(),
|
val: "condition".to_string(),
|
||||||
span: value_span,
|
span: value_span,
|
||||||
};
|
};
|
||||||
|
let mut output = PipelineData::new(Span::new(0, 0));
|
||||||
|
|
||||||
let code_path = PathMember::String {
|
let code_path = PathMember::String {
|
||||||
val: "code".to_string(),
|
val: "code".to_string(),
|
||||||
@ -762,7 +732,7 @@ pub fn eval_hook(
|
|||||||
match value {
|
match value {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for val in vals {
|
for val in vals {
|
||||||
eval_hook(engine_state, stack, arguments.clone(), val)?
|
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@ -773,11 +743,17 @@ pub fn eval_hook(
|
|||||||
val: block_id,
|
val: block_id,
|
||||||
span: block_span,
|
span: block_span,
|
||||||
..
|
..
|
||||||
|
}
|
||||||
|
| Value::Closure {
|
||||||
|
val: block_id,
|
||||||
|
span: block_span,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
match run_hook_block(
|
match run_hook_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
block_id,
|
block_id,
|
||||||
|
None,
|
||||||
arguments.clone(),
|
arguments.clone(),
|
||||||
block_span,
|
block_span,
|
||||||
) {
|
) {
|
||||||
@ -825,6 +801,7 @@ pub fn eval_hook(
|
|||||||
name.as_bytes().to_vec(),
|
name.as_bytes().to_vec(),
|
||||||
val.span()?,
|
val.span()?,
|
||||||
Type::Any,
|
Type::Any,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
vars.push((var_id, val));
|
vars.push((var_id, val));
|
||||||
@ -857,7 +834,9 @@ pub fn eval_hook(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
Ok(_) => {}
|
Ok(pipeline_data) => {
|
||||||
|
output = pipeline_data;
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
@ -872,7 +851,28 @@ pub fn eval_hook(
|
|||||||
span: block_span,
|
span: block_span,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
run_hook_block(engine_state, stack, block_id, arguments, block_span)?;
|
run_hook_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block_id,
|
||||||
|
input,
|
||||||
|
arguments,
|
||||||
|
block_span,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Value::Closure {
|
||||||
|
val: block_id,
|
||||||
|
span: block_span,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
run_hook_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block_id,
|
||||||
|
input,
|
||||||
|
arguments,
|
||||||
|
block_span,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
@ -889,7 +889,34 @@ pub fn eval_hook(
|
|||||||
span: block_span,
|
span: block_span,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?;
|
output = PipelineData::Value(
|
||||||
|
run_hook_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
*block_id,
|
||||||
|
input,
|
||||||
|
arguments,
|
||||||
|
*block_span,
|
||||||
|
)?,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Value::Closure {
|
||||||
|
val: block_id,
|
||||||
|
span: block_span,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
output = PipelineData::Value(
|
||||||
|
run_hook_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
*block_id,
|
||||||
|
input,
|
||||||
|
arguments,
|
||||||
|
*block_span,
|
||||||
|
)?,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
@ -903,19 +930,20 @@ pub fn eval_hook(
|
|||||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||||
engine_state.merge_env(stack, cwd)?;
|
engine_state.merge_env(stack, cwd)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_hook_block(
|
pub fn run_hook_block(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
|
optional_input: Option<PipelineData>,
|
||||||
arguments: Vec<(String, Value)>,
|
arguments: Vec<(String, Value)>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let input = PipelineData::new(span);
|
let input = optional_input.unwrap_or_else(|| PipelineData::new(span));
|
||||||
|
|
||||||
let mut callee_stack = stack.gather_captures(&block.captures);
|
let mut callee_stack = stack.gather_captures(&block.captures);
|
||||||
|
|
||||||
@ -934,7 +962,8 @@ pub fn run_hook_block(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(engine_state, &mut callee_stack, block, input, false, false) {
|
match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)
|
||||||
|
{
|
||||||
Ok(pipeline_data) => match pipeline_data.into_value(span) {
|
Ok(pipeline_data) => match pipeline_data.into_value(span) {
|
||||||
Value::Error { error } => Err(error),
|
Value::Error { error } => Err(error),
|
||||||
val => {
|
val => {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::get_shape_color;
|
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
|
use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use nu_protocol::Config;
|
use nu_protocol::{Config, Span};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
@ -15,10 +16,12 @@ impl Highlighter for NuHighlighter {
|
|||||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
let (shapes, global_span_offset) = {
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let block = {
|
||||||
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||||
|
block
|
||||||
|
};
|
||||||
|
let (shapes, global_span_offset) = {
|
||||||
let shapes = flatten_block(&working_set, &block);
|
let shapes = flatten_block(&working_set, &block);
|
||||||
(shapes, self.engine_state.next_span_start())
|
(shapes, self.engine_state.next_span_start())
|
||||||
};
|
};
|
||||||
@ -26,6 +29,15 @@ impl Highlighter for NuHighlighter {
|
|||||||
let mut output = StyledText::default();
|
let mut output = StyledText::default();
|
||||||
let mut last_seen_span = global_span_offset;
|
let mut last_seen_span = global_span_offset;
|
||||||
|
|
||||||
|
let global_cursor_offset = _cursor + global_span_offset;
|
||||||
|
let matching_brackets_pos = find_matching_brackets(
|
||||||
|
line,
|
||||||
|
&working_set,
|
||||||
|
&block,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
);
|
||||||
|
|
||||||
for shape in &shapes {
|
for shape in &shapes {
|
||||||
if shape.0.end <= last_seen_span
|
if shape.0.end <= last_seen_span
|
||||||
|| last_seen_span < global_span_offset
|
|| last_seen_span < global_span_offset
|
||||||
@ -44,166 +56,75 @@ impl Highlighter for NuHighlighter {
|
|||||||
let next_token = line
|
let next_token = line
|
||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
|
macro_rules! add_colored_token_with_bracket_highlight {
|
||||||
|
($shape:expr, $span:expr, $text:expr) => {{
|
||||||
|
let spans = split_span_by_highlight_positions(
|
||||||
|
line,
|
||||||
|
&$span,
|
||||||
|
&matching_brackets_pos,
|
||||||
|
global_span_offset,
|
||||||
|
);
|
||||||
|
spans.iter().for_each(|(part, highlight)| {
|
||||||
|
let start = part.start - $span.start;
|
||||||
|
let end = part.end - $span.start;
|
||||||
|
let text = (&next_token[start..end]).to_string();
|
||||||
|
let mut style = get_shape_color($shape.to_string(), &self.config);
|
||||||
|
if *highlight {
|
||||||
|
style = get_matching_brackets_style(style, &self.config);
|
||||||
|
}
|
||||||
|
output.push((style, text));
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! add_colored_token {
|
||||||
|
($shape:expr, $text:expr) => {
|
||||||
|
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
FlatShape::Garbage => output.push((
|
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Garbage
|
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Nothing => output.push((
|
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Nothing
|
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::External => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Binary => {
|
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
||||||
// nushell ?
|
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
||||||
output.push((
|
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::String => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
||||||
))
|
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
||||||
}
|
|
||||||
FlatShape::Bool => {
|
|
||||||
// nushell ?
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Int => {
|
|
||||||
// nushell Int
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Float => {
|
|
||||||
// nushell Decimal
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Range => output.push((
|
|
||||||
// nushell DotDot ?
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::InternalCall => output.push((
|
|
||||||
// nushell InternalCommand
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::External => {
|
|
||||||
// nushell ExternalCommand
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::ExternalArg => {
|
|
||||||
// nushell ExternalWord
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Literal => {
|
|
||||||
// nushell ?
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Operator => output.push((
|
|
||||||
// nushell Operator
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Signature => output.push((
|
|
||||||
// nushell ?
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::String => {
|
|
||||||
// nushell String
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::StringInterpolation => {
|
|
||||||
// nushell ???
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::DateTime => {
|
|
||||||
// nushell ???
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::List => {
|
FlatShape::List => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Table => {
|
FlatShape::Table => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Record => {
|
FlatShape::Record => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Block => {
|
FlatShape::Block => {
|
||||||
// nushell ???
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
FlatShape::Filepath => output.push((
|
|
||||||
// nushell Path
|
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::Directory => output.push((
|
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
||||||
// nushell Directory
|
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
FlatShape::And => add_colored_token!(shape.1, next_token),
|
||||||
next_token,
|
FlatShape::Or => add_colored_token!(shape.1, next_token),
|
||||||
)),
|
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
|
||||||
FlatShape::GlobPattern => output.push((
|
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
||||||
// nushell GlobPattern
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Variable => output.push((
|
|
||||||
// nushell Variable
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
FlatShape::Flag => {
|
|
||||||
// nushell Flag
|
|
||||||
output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
FlatShape::Custom(..) => output.push((
|
|
||||||
get_shape_color(shape.1.to_string(), &self.config),
|
|
||||||
next_token,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
last_seen_span = shape.0.end;
|
||||||
}
|
}
|
||||||
@ -216,3 +137,305 @@ impl Highlighter for NuHighlighter {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_span_by_highlight_positions(
|
||||||
|
line: &str,
|
||||||
|
span: &Span,
|
||||||
|
highlight_positions: &Vec<usize>,
|
||||||
|
global_span_offset: usize,
|
||||||
|
) -> Vec<(Span, bool)> {
|
||||||
|
let mut start = span.start;
|
||||||
|
let mut result: Vec<(Span, bool)> = Vec::new();
|
||||||
|
for pos in highlight_positions {
|
||||||
|
if start <= *pos && pos < &span.end {
|
||||||
|
if start < *pos {
|
||||||
|
result.push((Span { start, end: *pos }, false));
|
||||||
|
}
|
||||||
|
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
||||||
|
let end = span_str
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.map(|c| pos + get_char_length(c))
|
||||||
|
.unwrap_or(pos + 1);
|
||||||
|
result.push((Span { start: *pos, end }, true));
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start < span.end {
|
||||||
|
result.push((
|
||||||
|
Span {
|
||||||
|
start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_brackets(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
block: &Block,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Vec<usize> {
|
||||||
|
const BRACKETS: &str = "{}[]()";
|
||||||
|
|
||||||
|
// calculate first bracket position
|
||||||
|
let global_end_offset = line.len() + global_span_offset;
|
||||||
|
let global_bracket_pos =
|
||||||
|
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
|
||||||
|
// cursor is at the end of a non-empty string -- find block end at the previous position
|
||||||
|
if let Some(last_char) = line.chars().last() {
|
||||||
|
global_cursor_offset - get_char_length(last_char)
|
||||||
|
} else {
|
||||||
|
global_cursor_offset
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cursor is in the middle of a string -- find block end at the current position
|
||||||
|
global_cursor_offset
|
||||||
|
};
|
||||||
|
|
||||||
|
// check that position contains bracket
|
||||||
|
let match_idx = global_bracket_pos - global_span_offset;
|
||||||
|
if match_idx >= line.len()
|
||||||
|
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
|
||||||
|
{
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find matching bracket by finding matching block end
|
||||||
|
let matching_block_end = find_matching_block_end_in_block(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
block,
|
||||||
|
global_span_offset,
|
||||||
|
global_bracket_pos,
|
||||||
|
);
|
||||||
|
if let Some(pos) = matching_block_end {
|
||||||
|
let matching_idx = pos - global_span_offset;
|
||||||
|
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
|
||||||
|
return if global_bracket_pos < pos {
|
||||||
|
vec![global_bracket_pos, pos]
|
||||||
|
} else {
|
||||||
|
vec![pos, global_bracket_pos]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_block_end_in_block(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
block: &Block,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
for p in &block.pipelines {
|
||||||
|
for e in &p.elements {
|
||||||
|
match e {
|
||||||
|
PipelineElement::Expression(_, e)
|
||||||
|
| PipelineElement::Redirection(_, _, e)
|
||||||
|
| PipelineElement::And(_, e)
|
||||||
|
| PipelineElement::Or(_, e) => {
|
||||||
|
if e.span.contains(global_cursor_offset) {
|
||||||
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
e,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
) {
|
||||||
|
return Some(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_matching_block_end_in_expr(
|
||||||
|
line: &str,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
expression: &Expression,
|
||||||
|
global_span_offset: usize,
|
||||||
|
global_cursor_offset: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
macro_rules! find_in_expr_or_continue {
|
||||||
|
($inner_expr:ident) => {
|
||||||
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
$inner_expr,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
) {
|
||||||
|
return Some(pos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
|
||||||
|
{
|
||||||
|
let expr_first = expression.span.start;
|
||||||
|
let span_str = &line
|
||||||
|
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
|
||||||
|
let expr_last = span_str
|
||||||
|
.chars()
|
||||||
|
.last()
|
||||||
|
.map(|c| expression.span.end - get_char_length(c))
|
||||||
|
.unwrap_or(expression.span.start);
|
||||||
|
|
||||||
|
return match &expression.expr {
|
||||||
|
Expr::Bool(_) => None,
|
||||||
|
Expr::Int(_) => None,
|
||||||
|
Expr::Float(_) => None,
|
||||||
|
Expr::Binary(_) => None,
|
||||||
|
Expr::Range(..) => None,
|
||||||
|
Expr::Var(_) => None,
|
||||||
|
Expr::VarDecl(_) => None,
|
||||||
|
Expr::ExternalCall(..) => None,
|
||||||
|
Expr::Operator(_) => None,
|
||||||
|
Expr::UnaryNot(_) => None,
|
||||||
|
Expr::Keyword(..) => None,
|
||||||
|
Expr::ValueWithUnit(..) => None,
|
||||||
|
Expr::DateTime(_) => None,
|
||||||
|
Expr::Filepath(_) => None,
|
||||||
|
Expr::Directory(_) => None,
|
||||||
|
Expr::GlobPattern(_) => None,
|
||||||
|
Expr::String(_) => None,
|
||||||
|
Expr::CellPath(_) => None,
|
||||||
|
Expr::ImportPattern(_) => None,
|
||||||
|
Expr::Overlay(_) => None,
|
||||||
|
Expr::Signature(_) => None,
|
||||||
|
Expr::Nothing => None,
|
||||||
|
Expr::Garbage => None,
|
||||||
|
|
||||||
|
Expr::Table(hdr, rows) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at table end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at table start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside table
|
||||||
|
for inner_expr in hdr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
for row in rows {
|
||||||
|
for inner_expr in row {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Record(exprs) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at record end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at record start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside record
|
||||||
|
for (k, v) in exprs {
|
||||||
|
find_in_expr_or_continue!(k);
|
||||||
|
find_in_expr_or_continue!(v);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Call(call) => {
|
||||||
|
for arg in &call.arguments {
|
||||||
|
let opt_expr = match arg {
|
||||||
|
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
||||||
|
Argument::Positional(inner_expr) => Some(inner_expr),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(inner_expr) = opt_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
&b.head,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
),
|
||||||
|
|
||||||
|
Expr::BinaryOp(lhs, op, rhs) => {
|
||||||
|
find_in_expr_or_continue!(lhs);
|
||||||
|
find_in_expr_or_continue!(op);
|
||||||
|
find_in_expr_or_continue!(rhs);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Block(block_id)
|
||||||
|
| Expr::Closure(block_id)
|
||||||
|
| Expr::RowCondition(block_id)
|
||||||
|
| Expr::Subexpression(block_id) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at block end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at block start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside block
|
||||||
|
let nested_block = working_set.get_block(*block_id);
|
||||||
|
find_matching_block_end_in_block(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
nested_block,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::StringInterpolation(inner_expr) => {
|
||||||
|
for inner_expr in inner_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::List(inner_expr) => {
|
||||||
|
if expr_last == global_cursor_offset {
|
||||||
|
// cursor is at list end
|
||||||
|
Some(expr_first)
|
||||||
|
} else if expr_first == global_cursor_offset {
|
||||||
|
// cursor is at list start
|
||||||
|
Some(expr_last)
|
||||||
|
} else {
|
||||||
|
// cursor is inside list
|
||||||
|
for inner_expr in inner_expr {
|
||||||
|
find_in_expr_or_continue!(inner_expr);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
|
||||||
|
s[index..].chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_length(c: char) -> usize {
|
||||||
|
c.to_string().len()
|
||||||
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use log::trace;
|
use crate::repl::eval_hook;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use nu_protocol::CliError;
|
use nu_protocol::CliError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
PipelineData, ShellError, Span, Value,
|
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
@ -204,8 +204,6 @@ pub fn eval_source(
|
|||||||
fname: &str,
|
fname: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
trace!("eval_source");
|
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let (output, err) = parse(
|
let (output, err) = parse(
|
||||||
@ -232,7 +230,30 @@ pub fn eval_source(
|
|||||||
|
|
||||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
Ok(pipeline_data) => {
|
Ok(pipeline_data) => {
|
||||||
match pipeline_data.print(engine_state, stack, false, false) {
|
let config = engine_state.get_config();
|
||||||
|
let result;
|
||||||
|
if let PipelineData::ExternalStream {
|
||||||
|
stdout: stream,
|
||||||
|
stderr: stderr_stream,
|
||||||
|
exit_code,
|
||||||
|
..
|
||||||
|
} = pipeline_data
|
||||||
|
{
|
||||||
|
result = print_if_stream(stream, stderr_stream, false, exit_code);
|
||||||
|
} else if let Some(hook) = config.hooks.display_output.clone() {
|
||||||
|
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
|
||||||
|
Err(err) => {
|
||||||
|
result = Err(err);
|
||||||
|
}
|
||||||
|
Ok(val) => {
|
||||||
|
result = val.print(engine_state, stack, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = pipeline_data.print(engine_state, stack, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
match result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@ 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.70.0"
|
version = "0.72.1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.1" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-json = { path = "../nu-json", version = "0.70.0" }
|
nu-json = { path = "../nu-json", version = "0.72.1" }
|
||||||
nu-table = { path = "../nu-table", version = "0.70.0" }
|
nu-table = { path = "../nu-table", version = "0.72.1" }
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
|
@ -217,15 +217,10 @@ pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
|
|||||||
hm.insert("hints".to_string(), Color::DarkGray.normal());
|
hm.insert("hints".to_string(), Color::DarkGray.normal());
|
||||||
|
|
||||||
for (key, value) in &config.color_config {
|
for (key, value) in &config.color_config {
|
||||||
let value = value
|
match value.as_string() {
|
||||||
.as_string()
|
Ok(value) => update_hashmap(key, &value, &mut hm),
|
||||||
.expect("the only values for config color must be strings");
|
Err(_) => continue,
|
||||||
update_hashmap(key, &value, &mut hm);
|
}
|
||||||
|
|
||||||
// eprintln!(
|
|
||||||
// "config: {}:{}\t\t\thashmap: {}:{:?}",
|
|
||||||
// &key, &value, &key, &hm[key]
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hm
|
hm
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
mod color_config;
|
mod color_config;
|
||||||
|
mod matching_brackets_style;
|
||||||
mod nu_style;
|
mod nu_style;
|
||||||
mod shape_color;
|
mod shape_color;
|
||||||
|
|
||||||
pub use color_config::*;
|
pub use color_config::*;
|
||||||
|
pub use matching_brackets_style::*;
|
||||||
pub use nu_style::*;
|
pub use nu_style::*;
|
||||||
pub use shape_color::*;
|
pub use shape_color::*;
|
||||||
|
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::color_config::lookup_ansi_color_style;
|
||||||
|
use nu_ansi_term::Style;
|
||||||
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
|
||||||
|
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
|
||||||
|
|
||||||
|
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
|
||||||
|
Some(int_color) => match int_color.as_string() {
|
||||||
|
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
|
||||||
|
Err(_) => default_style,
|
||||||
|
},
|
||||||
|
None => default_style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_styles(base: Style, extra: Style) -> Style {
|
||||||
|
Style {
|
||||||
|
foreground: extra.foreground.or(base.foreground),
|
||||||
|
background: extra.background.or(base.background),
|
||||||
|
is_bold: extra.is_bold || base.is_bold,
|
||||||
|
is_dimmed: extra.is_dimmed || base.is_dimmed,
|
||||||
|
is_italic: extra.is_italic || base.is_italic,
|
||||||
|
is_underline: extra.is_underline || base.is_underline,
|
||||||
|
is_blink: extra.is_blink || base.is_blink,
|
||||||
|
is_reverse: extra.is_reverse || base.is_reverse,
|
||||||
|
is_hidden: extra.is_hidden || base.is_hidden,
|
||||||
|
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,10 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style {
|
|||||||
"shape_variable" => Style::new().fg(Color::Purple),
|
"shape_variable" => Style::new().fg(Color::Purple),
|
||||||
"shape_flag" => Style::new().fg(Color::Blue).bold(),
|
"shape_flag" => Style::new().fg(Color::Blue).bold(),
|
||||||
"shape_custom" => Style::new().fg(Color::Green),
|
"shape_custom" => Style::new().fg(Color::Green),
|
||||||
|
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
|
||||||
|
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
|
||||||
|
"shape_and" => Style::new().fg(Color::Purple).bold(),
|
||||||
|
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
||||||
_ => Style::default(),
|
_ => Style::default(),
|
||||||
},
|
},
|
||||||
|
@ -5,25 +5,24 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.70.0"
|
version = "0.72.1"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
# 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-color-config = { path = "../nu-color-config", version = "0.70.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.72.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.70.0" }
|
nu-engine = { path = "../nu-engine", version = "0.72.1" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.70.0" }
|
nu-glob = { path = "../nu-glob", version = "0.72.1" }
|
||||||
nu-json = { path = "../nu-json", version = "0.70.0" }
|
nu-json = { path = "../nu-json", version = "0.72.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.70.0" }
|
nu-parser = { path = "../nu-parser", version = "0.72.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.70.0" }
|
nu-path = { path = "../nu-path", version = "0.72.1" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.70.0" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.72.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.1" }
|
||||||
nu-system = { path = "../nu-system", version = "0.70.0" }
|
nu-system = { path = "../nu-system", version = "0.72.1" }
|
||||||
nu-table = { path = "../nu-table", version = "0.70.0" }
|
nu-table = { path = "../nu-table", version = "0.72.1" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.70.0" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.72.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.70.0" }
|
nu-utils = { path = "../nu-utils", version = "0.72.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.70.0" }
|
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
num-format = { version = "0.4.3" }
|
num-format = { version = "0.4.3" }
|
||||||
|
|
||||||
@ -33,13 +32,13 @@ base64 = "0.13.0"
|
|||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
calamine = "0.18.0"
|
calamine = "0.18.0"
|
||||||
chrono = { version = "0.4.21", features = ["serde", "unstable-locales"] }
|
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
|
||||||
chrono-humanize = "0.2.1"
|
chrono-humanize = "0.2.1"
|
||||||
chrono-tz = "0.6.3"
|
chrono-tz = "0.6.3"
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
dialoguer = "0.9.0"
|
dialoguer = { default-features = false, version = "0.9.0" }
|
||||||
digest = "0.10.0"
|
digest = { default-features = false, version = "0.10.0" }
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
encoding_rs = "0.8.30"
|
encoding_rs = "0.8.30"
|
||||||
@ -55,13 +54,14 @@ is-root = "0.1.2"
|
|||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
lscolors = { version = "0.12.0", features = ["crossterm"]}
|
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||||
md5 = { package = "md-5", version = "0.10.0" }
|
md5 = { package = "md-5", version = "0.10.0" }
|
||||||
meval = "0.2.0"
|
meval = "0.2.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
num = { version = "0.4.0", optional = true }
|
num = { version = "0.4.0", optional = true }
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
|
once_cell = "1.0"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
powierza-coefficient = "1.0.1"
|
powierza-coefficient = "1.0.1"
|
||||||
quick-xml = "0.23.0"
|
quick-xml = "0.23.0"
|
||||||
@ -78,7 +78,6 @@ serde_yaml = "0.9.4"
|
|||||||
sha2 = "0.10.0"
|
sha2 = "0.10.0"
|
||||||
# Disable default features b/c the default features build Git (very slow to compile)
|
# Disable default features b/c the default features build Git (very slow to compile)
|
||||||
shadow-rs = { version = "0.16.1", default-features = false }
|
shadow-rs = { version = "0.16.1", default-features = false }
|
||||||
strip-ansi-escapes = "0.1.1"
|
|
||||||
sysinfo = "0.26.2"
|
sysinfo = "0.26.2"
|
||||||
terminal_size = "0.2.1"
|
terminal_size = "0.2.1"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
@ -88,8 +87,8 @@ unicode-segmentation = "1.8.0"
|
|||||||
url = "2.2.1"
|
url = "2.2.1"
|
||||||
uuid = { version = "1.1.2", features = ["v4"] }
|
uuid = { version = "1.1.2", features = ["v4"] }
|
||||||
which = { version = "4.3.0", optional = true }
|
which = { version = "4.3.0", optional = true }
|
||||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
wax = { version = "0.5.0", features = ["diagnostics"] }
|
wax = { version = "0.5.0" }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
||||||
|
|
||||||
@ -99,13 +98,14 @@ winreg = "0.10.1"
|
|||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
umask = "2.0.0"
|
umask = "2.0.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
version = "2.1.3"
|
version = "3.0.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
version = "0.23.2"
|
version = "0.25.0"
|
||||||
optional = true
|
optional = true
|
||||||
features = [
|
features = [
|
||||||
"arg_where",
|
"arg_where",
|
||||||
@ -136,9 +136,8 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
version = "0.37.0"
|
version = "0.43.0"
|
||||||
features = [
|
features = [
|
||||||
"alloc",
|
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
@ -148,13 +147,15 @@ features = [
|
|||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
plugin = ["nu-parser/plugin"]
|
plugin = ["nu-parser/plugin"]
|
||||||
dataframe = ["polars", "num"]
|
dataframe = ["polars", "num", "sqlparser"]
|
||||||
database = ["sqlparser", "rusqlite"]
|
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.16.1", default-features = false }
|
shadow-rs = { version = "0.16.1", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
nu-test-support = { path = "../nu-test-support", version = "0.72.1" }
|
||||||
|
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
proptest = "1.0.0"
|
proptest = "1.0.0"
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -15,6 +15,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits and")
|
Signature::build("bits and")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required(
|
.required(
|
||||||
"target",
|
"target",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Bits {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Bits.signature(), &Bits.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Bits.signature(),
|
||||||
|
&Bits.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -16,6 +16,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits not")
|
Signature::build("bits not")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.switch(
|
.switch(
|
||||||
"signed",
|
"signed",
|
||||||
"always treat input number as a signed number",
|
"always treat input number as a signed number",
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -15,6 +15,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits or")
|
Signature::build("bits or")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required(
|
.required(
|
||||||
"target",
|
"target",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use num_traits::int::PrimInt;
|
use num_traits::int::PrimInt;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -18,6 +18,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits rol")
|
Signature::build("bits rol")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
|
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
|
||||||
.switch(
|
.switch(
|
||||||
"signed",
|
"signed",
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use num_traits::int::PrimInt;
|
use num_traits::int::PrimInt;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -18,6 +18,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits ror")
|
Signature::build("bits ror")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
|
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
|
||||||
.switch(
|
.switch(
|
||||||
"signed",
|
"signed",
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use num_traits::CheckedShl;
|
use num_traits::CheckedShl;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -18,6 +18,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits shl")
|
Signature::build("bits shl")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to shift left")
|
.required("bits", SyntaxShape::Int, "number of bits to shift left")
|
||||||
.switch(
|
.switch(
|
||||||
"signed",
|
"signed",
|
||||||
|
@ -3,7 +3,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use num_traits::CheckedShr;
|
use num_traits::CheckedShr;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -18,6 +18,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits shr")
|
Signature::build("bits shr")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to shift right")
|
.required("bits", SyntaxShape::Int, "number of bits to shift right")
|
||||||
.switch(
|
.switch(
|
||||||
"signed",
|
"signed",
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -15,6 +15,8 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bits xor")
|
Signature::build("bits xor")
|
||||||
|
.input_output_types(vec![(Type::Int, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required(
|
.required(
|
||||||
"target",
|
"target",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
added_data: Vec<u8>,
|
added_data: Vec<u8>,
|
||||||
index: Option<usize>,
|
index: Option<usize>,
|
||||||
end: bool,
|
end: bool,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ impl Command for BytesAdd {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes add")
|
Signature::build("bytes add")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("data", SyntaxShape::Binary, "the binary to add")
|
.required("data", SyntaxShape::Binary, "the binary to add")
|
||||||
.named(
|
.named(
|
||||||
"index",
|
"index",
|
||||||
@ -41,7 +43,7 @@ impl Command for BytesAdd {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally matches prefix of text by column paths",
|
"for a data structure input, add bytes to the data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@ -62,12 +64,8 @@ impl Command for BytesAdd {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
||||||
let end = call.has_flag("end");
|
let end = call.has_flag("end");
|
||||||
|
|
||||||
@ -75,7 +73,7 @@ impl Command for BytesAdd {
|
|||||||
added_data,
|
added_data,
|
||||||
index,
|
index,
|
||||||
end,
|
end,
|
||||||
column_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@ -118,7 +116,25 @@ impl Command for BytesAdd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(input: &[u8], args: &Arguments, span: Span) -> Value {
|
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => add_impl(val, args, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
|
||||||
match args.index {
|
match args.index {
|
||||||
None => {
|
None => {
|
||||||
if args.end {
|
if args.end {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@ -15,12 +15,12 @@ struct Arguments {
|
|||||||
start: isize,
|
start: isize,
|
||||||
end: isize,
|
end: isize,
|
||||||
arg_span: Span,
|
arg_span: Span,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,11 +115,13 @@ impl Command for BytesAt {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes at")
|
Signature::build("bytes at")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.required("range", SyntaxShape::Any, "the indexes to get bytes")
|
.required("range", SyntaxShape::Any, "the indexes to get bytes")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally get bytes by column paths",
|
"for a data structure input, get bytes from data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@ -141,17 +143,13 @@ impl Command for BytesAt {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let range: Value = call.req(engine_state, stack, 0)?;
|
let range: Value = call.req(engine_state, stack, 0)?;
|
||||||
let (start, end, arg_span) = parse_range(range, call.head)?;
|
let (start, end, arg_span) = parse_range(range, call.head)?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
arg_span,
|
arg_span,
|
||||||
column_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@ -228,7 +226,25 @@ impl Command for BytesAt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
fn at(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => at_impl(val, args, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
let len: isize = input.len() as isize;
|
let len: isize = input.len() as isize;
|
||||||
|
|
||||||
let start: isize = if arg.start < 0 {
|
let start: isize = if arg.start < 0 {
|
||||||
|
@ -3,7 +3,7 @@ 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::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -24,6 +24,7 @@ impl Command for BytesBuild {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("bytes build")
|
Signature::build("bytes build")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Binary)])
|
||||||
.rest("rest", SyntaxShape::Any, "list of bytes")
|
.rest("rest", SyntaxShape::Any, "list of bytes")
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Bytes {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Bytes.signature(), &Bytes.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Bytes.signature(),
|
||||||
|
&Bytes.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -3,7 +3,7 @@ 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::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@ -16,6 +16,7 @@ impl Command for BytesCollect {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes collect")
|
Signature::build("bytes collect")
|
||||||
|
.input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)])
|
||||||
.optional(
|
.optional(
|
||||||
"separator",
|
"separator",
|
||||||
SyntaxShape::Binary,
|
SyntaxShape::Binary,
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +28,12 @@ impl Command for BytesEndsWith {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes ends-with")
|
Signature::build("bytes ends-with")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Bool)])
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally matches prefix of text by column paths",
|
"for a data structure input, check if bytes at the given cell paths end with the pattern",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@ -53,15 +54,11 @@ impl Command for BytesEndsWith {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
column_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@ -96,10 +93,24 @@ impl Command for BytesEndsWith {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
Value::Bool {
|
match val {
|
||||||
val: input.ends_with(pattern),
|
Value::Binary {
|
||||||
span,
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => Value::Bool {
|
||||||
|
val: val.ends_with(&args.pattern),
|
||||||
|
span: *val_span,
|
||||||
|
},
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::{Call, CellPath};
|
use nu_protocol::ast::{Call, CellPath};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
end: bool,
|
end: bool,
|
||||||
all: bool,
|
all: bool,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +29,10 @@ impl Command for BytesIndexOf {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes index-of")
|
Signature::build("bytes index-of")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Binary, Type::Int),
|
||||||
|
(Type::Binary, Type::List(Box::new(Type::Int))),
|
||||||
|
])
|
||||||
.required(
|
.required(
|
||||||
"pattern",
|
"pattern",
|
||||||
SyntaxShape::Binary,
|
SyntaxShape::Binary,
|
||||||
@ -37,7 +41,7 @@ impl Command for BytesIndexOf {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally returns index of pattern in string by column paths",
|
"for a data structure input, find the indexes at the given cell paths",
|
||||||
)
|
)
|
||||||
.switch("all", "returns all matched index", Some('a'))
|
.switch("all", "returns all matched index", Some('a'))
|
||||||
.switch("end", "search from the end of the binary", Some('e'))
|
.switch("end", "search from the end of the binary", Some('e'))
|
||||||
@ -60,17 +64,13 @@ impl Command for BytesIndexOf {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
end: call.has_flag("end"),
|
end: call.has_flag("end"),
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
column_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@ -126,7 +126,25 @@ impl Command for BytesIndexOf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => index_of_impl(val, args, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
if arg.all {
|
if arg.all {
|
||||||
search_all_index(input, &arg.pattern, arg.end, span)
|
search_all_index(input, &arg.pattern, arg.end, span)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,24 +1,14 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BytesLen;
|
pub struct BytesLen;
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
column_paths: Option<Vec<CellPath>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
||||||
self.column_paths.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for BytesLen {
|
impl Command for BytesLen {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"bytes length"
|
"bytes length"
|
||||||
@ -26,10 +16,12 @@ impl Command for BytesLen {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes length")
|
Signature::build("bytes length")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Int)])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally find length of binary by column paths",
|
"for a data structure input, find the length of data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@ -49,13 +41,8 @@ impl Command for BytesLen {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments { column_paths };
|
|
||||||
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +65,24 @@ impl Command for BytesLen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
|
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
Value::Int {
|
match val {
|
||||||
val: input.len() as i64,
|
Value::Binary {
|
||||||
span,
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => Value::Int {
|
||||||
|
val: val.len() as i64,
|
||||||
|
span: *val_span,
|
||||||
|
},
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,6 @@ mod replace;
|
|||||||
mod reverse;
|
mod reverse;
|
||||||
mod starts_with;
|
mod starts_with;
|
||||||
|
|
||||||
use nu_protocol::ast::CellPath;
|
|
||||||
use nu_protocol::{PipelineData, ShellError, Span, Value};
|
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub use add::BytesAdd;
|
pub use add::BytesAdd;
|
||||||
pub use at::BytesAt;
|
pub use at::BytesAt;
|
||||||
pub use build_::BytesBuild;
|
pub use build_::BytesBuild;
|
||||||
@ -28,71 +23,3 @@ pub use remove::BytesRemove;
|
|||||||
pub use replace::BytesReplace;
|
pub use replace::BytesReplace;
|
||||||
pub use reverse::BytesReverse;
|
pub use reverse::BytesReverse;
|
||||||
pub use starts_with::BytesStartsWith;
|
pub use starts_with::BytesStartsWith;
|
||||||
|
|
||||||
trait BytesArgument {
|
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
|
|
||||||
fn operate<C, A>(
|
|
||||||
cmd: C,
|
|
||||||
mut arg: A,
|
|
||||||
input: PipelineData,
|
|
||||||
span: Span,
|
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
|
||||||
) -> Result<PipelineData, ShellError>
|
|
||||||
where
|
|
||||||
A: BytesArgument + Send + Sync + 'static,
|
|
||||||
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
|
|
||||||
{
|
|
||||||
match arg.take_column_paths() {
|
|
||||||
None => input.map(
|
|
||||||
move |v| match v {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => cmd(&val, &arg, val_span),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::UnsupportedInput(
|
|
||||||
format!(
|
|
||||||
"Input's type is {}. This command only works with bytes.",
|
|
||||||
other.get_type()
|
|
||||||
),
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ctrlc,
|
|
||||||
),
|
|
||||||
Some(column_paths) => {
|
|
||||||
let arg = Arc::new(arg);
|
|
||||||
input.map(
|
|
||||||
move |mut v| {
|
|
||||||
for path in &column_paths {
|
|
||||||
let opt = arg.clone();
|
|
||||||
let r = v.update_cell_path(
|
|
||||||
&path.members,
|
|
||||||
Box::new(move |old| {
|
|
||||||
match old {
|
|
||||||
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::UnsupportedInput(
|
|
||||||
format!(
|
|
||||||
"Input's type is {}. This command only works with bytes.",
|
|
||||||
other.get_type()
|
|
||||||
),
|
|
||||||
span,
|
|
||||||
),
|
|
||||||
}}}),
|
|
||||||
);
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v
|
|
||||||
},
|
|
||||||
ctrlc,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
end: bool,
|
end: bool,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
all: bool,
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +30,12 @@ impl Command for BytesRemove {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes remove")
|
Signature::build("bytes remove")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to find")
|
.required("pattern", SyntaxShape::Binary, "the pattern to find")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally remove bytes by column paths",
|
"for a data structure input, remove bytes from data at the given cell paths",
|
||||||
)
|
)
|
||||||
.switch("end", "remove from end of binary", Some('e'))
|
.switch("end", "remove from end of binary", Some('e'))
|
||||||
.switch("all", "remove occurrences of finding binary", Some('a'))
|
.switch("all", "remove occurrences of finding binary", Some('a'))
|
||||||
@ -55,12 +57,8 @@ impl Command for BytesRemove {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||||
if pattern_to_remove.item.is_empty() {
|
if pattern_to_remove.item.is_empty() {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
@ -73,7 +71,7 @@ impl Command for BytesRemove {
|
|||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern: pattern_to_remove,
|
pattern: pattern_to_remove,
|
||||||
end: call.has_flag("end"),
|
end: call.has_flag("end"),
|
||||||
column_paths,
|
cell_paths,
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,7 +133,25 @@ impl Command for BytesRemove {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => remove_impl(val, args, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
let remove_all = arg.all;
|
let remove_all = arg.all;
|
||||||
let input_len = input.len();
|
let input_len = input.len();
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
find: Vec<u8>,
|
find: Vec<u8>,
|
||||||
replace: Vec<u8>,
|
replace: Vec<u8>,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
all: bool,
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,12 +30,13 @@ impl Command for BytesReplace {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes replace")
|
Signature::build("bytes replace")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
.required("find", SyntaxShape::Binary, "the pattern to find")
|
.required("find", SyntaxShape::Binary, "the pattern to find")
|
||||||
.required("replace", SyntaxShape::Binary, "the replacement pattern")
|
.required("replace", SyntaxShape::Binary, "the replacement pattern")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally find and replace text by column paths",
|
"for a data structure input, replace bytes in data at the given cell paths",
|
||||||
)
|
)
|
||||||
.switch("all", "replace all occurrences of find binary", Some('a'))
|
.switch("all", "replace all occurrences of find binary", Some('a'))
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
@ -55,12 +57,8 @@ impl Command for BytesReplace {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||||
if find.item.is_empty() {
|
if find.item.is_empty() {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
@ -72,7 +70,7 @@ impl Command for BytesReplace {
|
|||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
find: find.item,
|
find: find.item,
|
||||||
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
||||||
column_paths,
|
cell_paths,
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,7 +124,25 @@ impl Command for BytesReplace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => replace_impl(val, args, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
let mut replaced = vec![];
|
let mut replaced = vec![];
|
||||||
let replace_all = arg.all;
|
let replace_all = arg.all;
|
||||||
|
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
column_paths: Option<Vec<CellPath>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
||||||
self.column_paths.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
||||||
@ -27,16 +17,17 @@ impl Command for BytesReverse {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes reverse")
|
Signature::build("bytes reverse")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally matches prefix of text by column paths",
|
"for a data structure input, reverse data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Reverse every bytes in the pipeline"
|
"Reverse the bytes in the pipeline"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -50,13 +41,8 @@ impl Command for BytesReverse {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments { column_paths };
|
|
||||||
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,12 +68,28 @@ impl Command for BytesReverse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value {
|
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
let mut reversed_input = input.to_vec();
|
match val {
|
||||||
reversed_input.reverse();
|
Value::Binary {
|
||||||
Value::Binary {
|
val,
|
||||||
val: reversed_input,
|
span: val_span,
|
||||||
span,
|
} => {
|
||||||
|
let mut reversed_input = val.to_vec();
|
||||||
|
reversed_input.reverse();
|
||||||
|
Value::Binary {
|
||||||
|
val: reversed_input,
|
||||||
|
span: *val_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
use super::{operate, BytesArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
column_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BytesArgument for Arguments {
|
impl CmdArgument for Arguments {
|
||||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.column_paths.take()
|
self.cell_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,11 +28,12 @@ impl Command for BytesStartsWith {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes starts-with")
|
Signature::build("bytes starts-with")
|
||||||
|
.input_output_types(vec![(Type::Binary, Type::Bool)])
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally matches prefix of text by column paths",
|
"for a data structure input, check if bytes at the given cell paths start with the pattern",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@ -53,15 +54,11 @@ impl Command for BytesStartsWith {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let column_paths = if column_paths.is_empty() {
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(column_paths)
|
|
||||||
};
|
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
column_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
operate(
|
operate(
|
||||||
starts_with,
|
starts_with,
|
||||||
@ -102,10 +99,24 @@ impl Command for BytesStartsWith {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
Value::Bool {
|
match val {
|
||||||
val: input.starts_with(pattern),
|
Value::Binary {
|
||||||
span,
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => Value::Bool {
|
||||||
|
val: val.starts_with(&args.pattern),
|
||||||
|
span: *val_span,
|
||||||
|
},
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
/// ```text
|
/// ```text
|
||||||
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
|
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Eq, Debug)]
|
#[derive(Eq, Debug, Ord, PartialOrd)]
|
||||||
pub enum HashableValue {
|
pub enum HashableValue {
|
||||||
Bool {
|
Bool {
|
||||||
val: bool,
|
val: bool,
|
||||||
@ -228,7 +228,7 @@ mod test {
|
|||||||
vals: vec![Value::Bool { val: true, span }],
|
vals: vec![Value::Bool { val: true, span }],
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Block {
|
Value::Closure {
|
||||||
val: 0,
|
val: 0,
|
||||||
captures: HashMap::new(),
|
captures: HashMap::new(),
|
||||||
span,
|
span,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use super::hashable_value::HashableValue;
|
use super::hashable_value::HashableValue;
|
||||||
|
use itertools::Itertools;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
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::{
|
||||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -24,6 +25,7 @@ impl Command for Histogram {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("histogram")
|
Signature::build("histogram")
|
||||||
|
.input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Table(vec![])),])
|
||||||
.optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list")
|
.optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list")
|
||||||
.optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output")
|
.optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output")
|
||||||
.named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t'))
|
.named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t'))
|
||||||
@ -36,23 +38,48 @@ impl Command for Histogram {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Get a histogram for the types of files",
|
description: "Compute a histogram of file types",
|
||||||
example: "ls | histogram type",
|
example: "ls | histogram type",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Get a histogram for the types of files, with frequency column named freq",
|
"Compute a histogram for the types of files, with frequency column named freq",
|
||||||
example: "ls | histogram type freq",
|
example: "ls | histogram type freq",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Get a histogram for a list of numbers",
|
description: "Compute a histogram for a list of numbers",
|
||||||
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram",
|
example: "echo [1 2 1] | histogram",
|
||||||
result: None,
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::Record {
|
||||||
|
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
||||||
|
vals: vec![
|
||||||
|
Value::test_int(1),
|
||||||
|
Value::test_int(2),
|
||||||
|
Value::test_float(0.6666666666666666),
|
||||||
|
Value::test_string("66.67%"),
|
||||||
|
Value::test_string("******************************************************************"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
||||||
|
vals: vec![
|
||||||
|
Value::test_int(2),
|
||||||
|
Value::test_int(1),
|
||||||
|
Value::test_float(0.3333333333333333),
|
||||||
|
Value::test_string("33.33%"),
|
||||||
|
Value::test_string("*********************************"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}
|
||||||
|
),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Get a histogram for a list of numbers, and percentage is based on the maximum value",
|
description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value",
|
||||||
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
|
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
|
||||||
result: None,
|
result: None,
|
||||||
}
|
}
|
||||||
@ -213,7 +240,7 @@ fn histogram_impl(
|
|||||||
freq_column.to_string(),
|
freq_column.to_string(),
|
||||||
];
|
];
|
||||||
const MAX_FREQ_COUNT: f64 = 100.0;
|
const MAX_FREQ_COUNT: f64 = 100.0;
|
||||||
for (val, count) in counter.into_iter() {
|
for (val, count) in counter.into_iter().sorted() {
|
||||||
let quantile = match calc_method {
|
let quantile = match calc_method {
|
||||||
PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64,
|
PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64,
|
||||||
PercentageCalcMethod::Relative => count as f64 / max_cnt as f64,
|
PercentageCalcMethod::Relative => count as f64 / max_cnt as f64,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -18,7 +19,9 @@ impl Command for Fmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("fmt").category(Category::Conversions)
|
Signature::build("fmt")
|
||||||
|
.input_output_types(vec![(Type::Number, Type::Record(vec![]))])
|
||||||
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -96,31 +99,12 @@ fn fmt(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let column_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())
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
action(&v, head)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let r =
|
|
||||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: &Value, span: Span) -> Value {
|
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val, .. } => fmt_it(*val, span),
|
Value::Int { val, .. } => fmt_it(*val, span),
|
||||||
Value::Filesize { val, .. } => fmt_it(*val, span),
|
Value::Filesize { val, .. } => fmt_it(*val, span),
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -16,10 +17,20 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into binary")
|
Signature::build("into binary")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Binary, Type::Binary),
|
||||||
|
(Type::Int, Type::Binary),
|
||||||
|
(Type::Number, Type::Binary),
|
||||||
|
(Type::String, Type::Binary),
|
||||||
|
(Type::Bool, Type::Binary),
|
||||||
|
(Type::Filesize, Type::Binary),
|
||||||
|
(Type::Date, Type::Binary),
|
||||||
|
])
|
||||||
|
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to binary (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -100,7 +111,7 @@ fn into_binary(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
||||||
@ -120,27 +131,10 @@ fn into_binary(
|
|||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
_ => input.map(
|
_ => {
|
||||||
move |v| {
|
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||||
if column_paths.is_empty() {
|
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
action(&v, head)
|
}
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let r = ret.update_cell_path(
|
|
||||||
&path.members,
|
|
||||||
Box::new(move |old| action(old, head)),
|
|
||||||
);
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +154,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: &Value, span: Span) -> Value {
|
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Binary { .. } => input.clone(),
|
Value::Binary { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::Binary {
|
Value::Int { val, .. } => Value::Binary {
|
||||||
@ -180,7 +174,7 @@ pub fn action(input: &Value, span: Span) -> Value {
|
|||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Bool { val, .. } => Value::Binary {
|
Value::Bool { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(if *val { 1i64 } else { 0 }),
|
val: int_to_endian(i64::from(*val)),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Date { val, .. } => Value::Binary {
|
Value::Date { val, .. } => Value::Binary {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -15,10 +16,17 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into bool")
|
Signature::build("into bool")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Int, Type::Bool),
|
||||||
|
(Type::Number, Type::Bool),
|
||||||
|
(Type::String, Type::Bool),
|
||||||
|
(Type::Bool, Type::Bool),
|
||||||
|
(Type::List(Box::new(Type::Any)), Type::Table(vec![])),
|
||||||
|
])
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to boolean (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -88,6 +96,11 @@ impl Command for SubCommand {
|
|||||||
example: "1 | into bool",
|
example: "1 | into bool",
|
||||||
result: Some(Value::boolean(true, span)),
|
result: Some(Value::boolean(true, span)),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "convert decimal to boolean",
|
||||||
|
example: "0.3 | into bool",
|
||||||
|
result: Some(Value::boolean(true, span)),
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert decimal string to boolean",
|
description: "convert decimal string to boolean",
|
||||||
example: "'0.0' | into bool",
|
example: "'0.0' | into bool",
|
||||||
@ -108,28 +121,9 @@ fn into_bool(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let column_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())
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
action(&v, head)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let r =
|
|
||||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
||||||
@ -154,7 +148,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, span: Span) -> Value {
|
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Bool { .. } => input.clone(),
|
Value::Bool { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::Bool {
|
Value::Int { val, .. } => Value::Bool {
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Into {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Into.signature(), &[], engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Into.signature(),
|
||||||
|
&[],
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use crate::{generate_strftime_list, parse_date_from_string};
|
use crate::{generate_strftime_list, parse_date_from_string};
|
||||||
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
|
use chrono::{DateTime, FixedOffset, Local, LocalResult, TimeZone, Utc};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||||
|
SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
timezone: Option<Spanned<String>>,
|
zone_options: Option<Spanned<Zone>>,
|
||||||
offset: Option<Spanned<i64>>,
|
format_options: Option<DatetimeFormat>,
|
||||||
format: Option<String>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
column_paths: Vec<CellPath>,
|
}
|
||||||
|
|
||||||
|
impl CmdArgument for Arguments {
|
||||||
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.cell_paths.take()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case it may be confused with chrono::TimeZone
|
// In case it may be confused with chrono::TimeZone
|
||||||
@ -57,7 +64,11 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into datetime")
|
Signature::build("into datetime")
|
||||||
.named(
|
.input_output_types(vec![
|
||||||
|
(Type::Int, Type::Date),
|
||||||
|
(Type::String, Type::Date),
|
||||||
|
])
|
||||||
|
.named(
|
||||||
"timezone",
|
"timezone",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
"Specify timezone if the input is a Unix timestamp. Valid options: 'UTC' ('u') or 'LOCAL' ('l')",
|
"Specify timezone if the input is a Unix timestamp. Valid options: 'UTC' ('u') or 'LOCAL' ('l')",
|
||||||
@ -83,7 +94,7 @@ impl Command for SubCommand {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"optionally convert text into datetime by column paths",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -95,7 +106,36 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
if call.has_flag("list") {
|
||||||
|
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
|
||||||
|
// if zone-offset is specified, then zone will be neglected
|
||||||
|
let timezone = call.get_flag::<Spanned<String>>(engine_state, stack, "timezone")?;
|
||||||
|
let zone_options =
|
||||||
|
match &call.get_flag::<Spanned<i64>>(engine_state, stack, "offset")? {
|
||||||
|
Some(zone_offset) => Some(Spanned {
|
||||||
|
item: Zone::new(zone_offset.item),
|
||||||
|
span: zone_offset.span,
|
||||||
|
}),
|
||||||
|
None => timezone.as_ref().map(|zone| Spanned {
|
||||||
|
item: Zone::from_string(zone.item.clone()),
|
||||||
|
span: zone.span,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let format_options = call
|
||||||
|
.get_flag::<String>(engine_state, stack, "format")?
|
||||||
|
.as_ref()
|
||||||
|
.map(|fmt| DatetimeFormat(fmt.to_string()));
|
||||||
|
let args = Arguments {
|
||||||
|
format_options,
|
||||||
|
zone_options,
|
||||||
|
cell_paths,
|
||||||
|
};
|
||||||
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -107,38 +147,63 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
let example_result_1 = |secs: i64, nsecs: u32| {
|
||||||
|
let dt = match Utc.timestamp_opt(secs, nsecs) {
|
||||||
|
LocalResult::Single(dt) => Some(dt),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
match dt {
|
||||||
|
Some(dt) => Some(Value::Date {
|
||||||
|
val: dt.into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
None => Some(Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given datetime representation is unsupported.".to_string(),
|
||||||
|
Span::test_data(),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let example_result_2 = |millis: i64| {
|
||||||
|
let dt = match Utc.timestamp_millis_opt(millis) {
|
||||||
|
LocalResult::Single(dt) => Some(dt),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
match dt {
|
||||||
|
Some(dt) => Some(Value::Date {
|
||||||
|
val: dt.into(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
None => Some(Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given datetime representation is unsupported.".to_string(),
|
||||||
|
Span::test_data(),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
};
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Convert to datetime",
|
description: "Convert to datetime",
|
||||||
example: "'27.02.2021 1:55 pm +0000' | into datetime",
|
example: "'27.02.2021 1:55 pm +0000' | into datetime",
|
||||||
result: Some(Value::Date {
|
result: example_result_1(1614434100,0)
|
||||||
val: Utc.timestamp(1614434100, 0).into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert to datetime",
|
description: "Convert to datetime",
|
||||||
example: "'2021-02-27T13:55:40+00:00' | into datetime",
|
example: "'2021-02-27T13:55:40+00:00' | into datetime",
|
||||||
result: Some(Value::Date {
|
result: example_result_1(1614434140, 0)
|
||||||
val: Utc.timestamp(1614434140, 0).into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert to datetime using a custom format",
|
description: "Convert 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 -f '%Y%m%d_%H%M%S%z'",
|
||||||
result: Some(Value::Date {
|
result: example_result_1(1614434140, 0)
|
||||||
val: Utc.timestamp(1614434140, 0).into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert timestamp (no larger than 8e+12) to a UTC datetime",
|
description: "Convert timestamp (no larger than 8e+12) to a UTC datetime",
|
||||||
example: "1614434140 | into datetime",
|
example: "1614434140 | into datetime",
|
||||||
result: Some(Value::Date {
|
result: example_result_1(1614434140, 0)
|
||||||
val: Utc.timestamp(1614434140, 0).into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
@ -150,10 +215,7 @@ impl Command for SubCommand {
|
|||||||
description:
|
description:
|
||||||
"Convert timestamps like the sqlite history t",
|
"Convert timestamps like the sqlite history t",
|
||||||
example: "1656165681720 | into datetime",
|
example: "1656165681720 | into datetime",
|
||||||
result: Some(Value::Date {
|
result: example_result_2(1656165681720)
|
||||||
val: Utc.timestamp_millis(1656165681720).into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -162,72 +224,9 @@ impl Command for SubCommand {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DatetimeFormat(String);
|
struct DatetimeFormat(String);
|
||||||
|
|
||||||
fn operate(
|
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||||
engine_state: &EngineState,
|
let timezone = &args.zone_options;
|
||||||
stack: &mut Stack,
|
let dateformat = &args.format_options;
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
|
|
||||||
let options = Arguments {
|
|
||||||
timezone: call.get_flag(engine_state, stack, "timezone")?,
|
|
||||||
offset: call.get_flag(engine_state, stack, "offset")?,
|
|
||||||
format: call.get_flag(engine_state, stack, "format")?,
|
|
||||||
column_paths: call.rest(engine_state, stack, 0)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
// if zone-offset is specified, then zone will be neglected
|
|
||||||
let zone_options = match &options.offset {
|
|
||||||
Some(zone_offset) => Some(Spanned {
|
|
||||||
item: Zone::new(zone_offset.item),
|
|
||||||
span: zone_offset.span,
|
|
||||||
}),
|
|
||||||
None => options.timezone.as_ref().map(|zone| Spanned {
|
|
||||||
item: Zone::from_string(zone.item.clone()),
|
|
||||||
span: zone.span,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
let list_flag = call.has_flag("list");
|
|
||||||
|
|
||||||
let format_options = options
|
|
||||||
.format
|
|
||||||
.as_ref()
|
|
||||||
.map(|fmt| DatetimeFormat(fmt.to_string()));
|
|
||||||
|
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if options.column_paths.is_empty() && !list_flag {
|
|
||||||
action(&v, &zone_options, &format_options, head)
|
|
||||||
} else if list_flag {
|
|
||||||
generate_strftime_list(head, true)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &options.column_paths {
|
|
||||||
let zone_options = zone_options.clone();
|
|
||||||
let format_options = format_options.clone();
|
|
||||||
let r = ret.update_cell_path(
|
|
||||||
&path.members,
|
|
||||||
Box::new(move |old| action(old, &zone_options, &format_options, head)),
|
|
||||||
);
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(
|
|
||||||
input: &Value,
|
|
||||||
timezone: &Option<Spanned<Zone>>,
|
|
||||||
dateformat: &Option<DatetimeFormat>,
|
|
||||||
head: Span,
|
|
||||||
) -> Value {
|
|
||||||
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
|
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
|
||||||
let timestamp = match input {
|
let timestamp = match input {
|
||||||
Value::Int { val, .. } => Ok(*val),
|
Value::Int { val, .. } => Ok(*val),
|
||||||
@ -262,8 +261,31 @@ fn action(
|
|||||||
// be able to convert chrono::Utc::now()
|
// be able to convert chrono::Utc::now()
|
||||||
let dt = match ts.to_string().len() {
|
let dt = match ts.to_string().len() {
|
||||||
x if x > 13 => Utc.timestamp_nanos(ts).into(),
|
x if x > 13 => Utc.timestamp_nanos(ts).into(),
|
||||||
x if x > 10 => Utc.timestamp_millis(ts).into(),
|
x if x > 10 => match Utc.timestamp_millis_opt(ts) {
|
||||||
_ => Utc.timestamp(ts, 0).into(),
|
LocalResult::Single(dt) => dt.into(),
|
||||||
|
_ => {
|
||||||
|
return Value::Error {
|
||||||
|
// This error message is from chrono
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid."
|
||||||
|
.to_string(),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => match Utc.timestamp_opt(ts, 0) {
|
||||||
|
LocalResult::Single(dt) => dt.into(),
|
||||||
|
_ => {
|
||||||
|
return Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid."
|
||||||
|
.to_string(),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Value::Date {
|
Value::Date {
|
||||||
@ -272,28 +294,64 @@ fn action(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Spanned { item, span }) => match item {
|
Some(Spanned { item, span }) => match item {
|
||||||
Zone::Utc => Value::Date {
|
Zone::Utc => match Utc.timestamp_opt(ts, 0) {
|
||||||
val: Utc.timestamp(ts, 0).into(),
|
LocalResult::Single(val) => Value::Date {
|
||||||
span: head,
|
val: val.into(),
|
||||||
},
|
|
||||||
Zone::Local => Value::Date {
|
|
||||||
val: Local.timestamp(ts, 0).into(),
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Zone::East(i) => {
|
|
||||||
let eastoffset = FixedOffset::east((*i as i32) * HOUR);
|
|
||||||
Value::Date {
|
|
||||||
val: eastoffset.timestamp(ts, 0),
|
|
||||||
span: head,
|
span: head,
|
||||||
}
|
},
|
||||||
}
|
_ => Value::Error {
|
||||||
Zone::West(i) => {
|
error: ShellError::UnsupportedInput(
|
||||||
let westoffset = FixedOffset::west((*i as i32) * HOUR);
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
Value::Date {
|
*span,
|
||||||
val: westoffset.timestamp(ts, 0),
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Zone::Local => match Local.timestamp_opt(ts, 0) {
|
||||||
|
LocalResult::Single(val) => Value::Date {
|
||||||
|
val: val.into(),
|
||||||
span: head,
|
span: head,
|
||||||
}
|
},
|
||||||
}
|
_ => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
|
*span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) {
|
||||||
|
Some(eastoffset) => match eastoffset.timestamp_opt(ts, 0) {
|
||||||
|
LocalResult::Single(val) => Value::Date { val, span: head },
|
||||||
|
_ => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
|
*span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
|
*span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) {
|
||||||
|
Some(westoffset) => match westoffset.timestamp_opt(ts, 0) {
|
||||||
|
LocalResult::Single(val) => Value::Date { val, span: head },
|
||||||
|
_ => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
|
*span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"The given local datetime representation is invalid.".to_string(),
|
||||||
|
*span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
Zone::Error => Value::Error {
|
Zone::Error => Value::Error {
|
||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"Cannot convert given timezone or offset to timestamp".to_string(),
|
"Cannot convert given timezone or offset to timestamp".to_string(),
|
||||||
@ -359,7 +417,12 @@ mod tests {
|
|||||||
fn takes_a_date_format() {
|
fn takes_a_date_format() {
|
||||||
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
|
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
|
||||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||||
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: None,
|
||||||
|
format_options: fmt_options,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
|
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -371,7 +434,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn takes_iso8601_date_format() {
|
fn takes_iso8601_date_format() {
|
||||||
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
|
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
|
||||||
let actual = action(&date_str, &None, &None, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: None,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
|
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -387,7 +455,12 @@ mod tests {
|
|||||||
item: Zone::East(8),
|
item: Zone::East(8),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
});
|
});
|
||||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: timezone_option,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -404,7 +477,12 @@ mod tests {
|
|||||||
item: Zone::East(8),
|
item: Zone::East(8),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
});
|
});
|
||||||
let actual = action(&date_int, &timezone_option, &None, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: timezone_option,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_int, &args, Span::test_data());
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -421,9 +499,14 @@ mod tests {
|
|||||||
item: Zone::Local,
|
item: Zone::Local,
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
});
|
});
|
||||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: timezone_option,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: Local.timestamp(1614434140, 0).into(),
|
val: Local.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -433,11 +516,15 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn takes_timestamp_without_timezone() {
|
fn takes_timestamp_without_timezone() {
|
||||||
let date_str = Value::test_string("1614434140");
|
let date_str = Value::test_string("1614434140");
|
||||||
let timezone_option = None;
|
let args = Arguments {
|
||||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
zone_options: None,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
|
|
||||||
let expected = Value::Date {
|
let expected = Value::Date {
|
||||||
val: Utc.timestamp(1614434140, 0).into(),
|
val: Utc.timestamp_opt(1614434140, 0).unwrap().into(),
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -451,7 +538,12 @@ mod tests {
|
|||||||
item: Zone::Utc,
|
item: Zone::Utc,
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
});
|
});
|
||||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: timezone_option,
|
||||||
|
format_options: None,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error);
|
assert_eq!(actual.get_type(), Error);
|
||||||
}
|
}
|
||||||
@ -460,7 +552,12 @@ mod tests {
|
|||||||
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
|
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
|
||||||
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
|
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
|
||||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||||
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
|
let args = Arguments {
|
||||||
|
zone_options: None,
|
||||||
|
format_options: fmt_options,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_str, &args, Span::test_data());
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error);
|
assert_eq!(actual.get_type(), Error);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -14,11 +15,16 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into decimal").rest(
|
Signature::build("into decimal")
|
||||||
"rest",
|
.input_output_types(vec![
|
||||||
SyntaxShape::CellPath,
|
(Type::String, Type::Number),
|
||||||
"optionally convert text into decimal by column paths",
|
(Type::Bool, Type::Number),
|
||||||
)
|
])
|
||||||
|
.rest(
|
||||||
|
"rest",
|
||||||
|
SyntaxShape::CellPath,
|
||||||
|
"for a data structure input, convert data at the given cell paths",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -36,7 +42,9 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
operate(engine_state, stack, call, input)
|
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> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -72,37 +80,7 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
action(&v, head)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let r =
|
|
||||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, head: Span) -> Value {
|
|
||||||
match input {
|
match input {
|
||||||
Value::String { val: s, span } => {
|
Value::String { val: s, span } => {
|
||||||
let other = s.trim();
|
let other = s.trim();
|
||||||
@ -163,7 +141,7 @@ mod tests {
|
|||||||
let word = Value::test_string("3.1415");
|
let word = Value::test_string("3.1415");
|
||||||
let expected = Value::test_float(3.1415);
|
let expected = Value::test_float(3.1415);
|
||||||
|
|
||||||
let actual = action(&word, Span::test_data());
|
let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +149,11 @@ mod tests {
|
|||||||
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
|
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
|
||||||
let decimal_str = Value::test_string("11.6anra");
|
let decimal_str = Value::test_string("11.6anra");
|
||||||
|
|
||||||
let actual = action(&decimal_str, Span::test_data());
|
let actual = action(
|
||||||
|
&decimal_str,
|
||||||
|
&CellPathOnlyArgs::from(vec![]),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error);
|
assert_eq!(actual.get_type(), Error);
|
||||||
}
|
}
|
||||||
@ -180,7 +162,11 @@ mod tests {
|
|||||||
fn int_to_decimal() {
|
fn int_to_decimal() {
|
||||||
let decimal_str = Value::test_int(10);
|
let decimal_str = Value::test_int(10);
|
||||||
let expected = Value::test_float(10.0);
|
let expected = Value::test_float(10.0);
|
||||||
let actual = action(&decimal_str, Span::test_data());
|
let actual = action(
|
||||||
|
&decimal_str,
|
||||||
|
&CellPathOnlyArgs::from(vec![]),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use nu_parser::parse_duration_bytes;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath, Expr},
|
ast::{Call, CellPath, Expr},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Unit,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Unit,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,6 +17,13 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into duration")
|
Signature::build("into duration")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::String, Type::Duration),
|
||||||
|
(Type::Duration, Type::Duration),
|
||||||
|
// TODO: --convert option should be implemented as `format duration`
|
||||||
|
(Type::String, Type::String),
|
||||||
|
(Type::Duration, Type::String),
|
||||||
|
])
|
||||||
.named(
|
.named(
|
||||||
"convert",
|
"convert",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
@ -26,7 +33,7 @@ impl Command for SubCommand {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to duration (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -36,7 +43,7 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
"into duration does not take leap years into account and every month is calculated with 30 days"
|
"This command does not take leap years into account, and every month is assumed to have 30 days."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -121,11 +128,19 @@ impl Command for SubCommand {
|
|||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert duration to duration",
|
||||||
|
example: "420sec | into duration",
|
||||||
|
result: Some(Value::Duration {
|
||||||
|
val: 7 * 60 * 1000 * 1000 * 1000,
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Convert duration to the requested duration as a string",
|
description: "Convert duration to the requested duration as a string",
|
||||||
example: "420sec | into duration --convert min",
|
example: "420sec | into duration --convert ms",
|
||||||
result: Some(Value::String {
|
result: Some(Value::String {
|
||||||
val: "7 min".to_string(),
|
val: "420000 ms".to_string(),
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -15,10 +16,16 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into filesize")
|
Signature::build("into filesize")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Int, Type::Filesize),
|
||||||
|
(Type::Number, Type::Filesize),
|
||||||
|
(Type::String, Type::Filesize),
|
||||||
|
(Type::Filesize, Type::Filesize),
|
||||||
|
])
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to filesize (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -38,7 +45,9 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_filesize(engine_state, stack, call, input)
|
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> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -84,37 +93,7 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_filesize(
|
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
action(&v, head)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let r =
|
|
||||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn action(input: &Value, span: Span) -> Value {
|
|
||||||
if let Ok(value_span) = input.span() {
|
if let Ok(value_span) = input.span() {
|
||||||
match input {
|
match input {
|
||||||
Value::Filesize { .. } => input.clone(),
|
Value::Filesize { .. } => input.clone(),
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
radix: Option<Value>,
|
radix: u32,
|
||||||
column_paths: Vec<CellPath>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
little_endian: bool,
|
little_endian: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CmdArgument for Arguments {
|
||||||
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.cell_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -21,12 +28,22 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into int")
|
Signature::build("into int")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::String, Type::Int),
|
||||||
|
(Type::Number, Type::Int),
|
||||||
|
(Type::Bool, Type::Int),
|
||||||
|
// Unix timestamp in seconds
|
||||||
|
(Type::Date, Type::Int),
|
||||||
|
// TODO: Users should do this by dividing a Filesize by a Filesize explicitly
|
||||||
|
(Type::Filesize, Type::Int),
|
||||||
|
])
|
||||||
|
.vectorizes_over_list(true)
|
||||||
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
|
.named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
|
||||||
.switch("little-endian", "use little-endian byte decoding", None)
|
.switch("little-endian", "use little-endian byte decoding", None)
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to int (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@ -46,7 +63,29 @@ impl Command for SubCommand {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_int(engine_state, stack, call, input)
|
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
|
||||||
|
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
|
||||||
|
let radix: u32 = match radix {
|
||||||
|
Some(Value::Int { val, span }) => {
|
||||||
|
if !(2..=36).contains(&val) {
|
||||||
|
return Err(ShellError::UnsupportedInput(
|
||||||
|
"Radix must lie in the range [2, 36]".to_string(),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
val as u32
|
||||||
|
}
|
||||||
|
Some(_) => 10,
|
||||||
|
None => 10,
|
||||||
|
};
|
||||||
|
let args = Arguments {
|
||||||
|
radix,
|
||||||
|
little_endian: call.has_flag("little-endian"),
|
||||||
|
cell_paths,
|
||||||
|
};
|
||||||
|
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -121,59 +160,9 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_int(
|
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
engine_state: &EngineState,
|
let radix = args.radix;
|
||||||
stack: &mut Stack,
|
let little_endian = args.little_endian;
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
|
|
||||||
let options = Arguments {
|
|
||||||
radix: call.get_flag(engine_state, stack, "radix")?,
|
|
||||||
little_endian: call.has_flag("little-endian"),
|
|
||||||
column_paths: call.rest(engine_state, stack, 0)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let radix: u32 = match options.radix {
|
|
||||||
Some(Value::Int { val, .. }) => val as u32,
|
|
||||||
Some(_) => 10,
|
|
||||||
None => 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(val) = &options.radix {
|
|
||||||
if !(2..=36).contains(&radix) {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Radix must lie in the range [2, 36]".to_string(),
|
|
||||||
val.span()?,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.map(
|
|
||||||
move |v| {
|
|
||||||
if options.column_paths.is_empty() {
|
|
||||||
action(&v, head, radix, options.little_endian)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &options.column_paths {
|
|
||||||
let r = ret.update_cell_path(
|
|
||||||
&path.members,
|
|
||||||
Box::new(move |old| action(old, head, radix, options.little_endian)),
|
|
||||||
);
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
|
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val: _, .. } => {
|
Value::Int { val: _, .. } => {
|
||||||
if radix == 10 {
|
if radix == 10 {
|
||||||
@ -401,21 +390,45 @@ mod test {
|
|||||||
let word = Value::test_string("10");
|
let word = Value::test_string("10");
|
||||||
let expected = Value::test_int(10);
|
let expected = Value::test_int(10);
|
||||||
|
|
||||||
let actual = action(&word, Span::test_data(), 10, false);
|
let actual = action(
|
||||||
|
&word,
|
||||||
|
&Arguments {
|
||||||
|
radix: 10,
|
||||||
|
cell_paths: None,
|
||||||
|
little_endian: false,
|
||||||
|
},
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turns_binary_to_integer() {
|
fn turns_binary_to_integer() {
|
||||||
let s = Value::test_string("0b101");
|
let s = Value::test_string("0b101");
|
||||||
let actual = action(&s, Span::test_data(), 10, false);
|
let actual = action(
|
||||||
|
&s,
|
||||||
|
&Arguments {
|
||||||
|
radix: 10,
|
||||||
|
cell_paths: None,
|
||||||
|
little_endian: false,
|
||||||
|
},
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
assert_eq!(actual, Value::test_int(5));
|
assert_eq!(actual, Value::test_int(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turns_hex_to_integer() {
|
fn turns_hex_to_integer() {
|
||||||
let s = Value::test_string("0xFF");
|
let s = Value::test_string("0xFF");
|
||||||
let actual = action(&s, Span::test_data(), 16, false);
|
let actual = action(
|
||||||
|
&s,
|
||||||
|
&Arguments {
|
||||||
|
radix: 16,
|
||||||
|
cell_paths: None,
|
||||||
|
little_endian: false,
|
||||||
|
},
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
assert_eq!(actual, Value::test_int(255));
|
assert_eq!(actual, Value::test_int(255));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +436,15 @@ mod test {
|
|||||||
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
||||||
let integer_str = Value::test_string("36anra");
|
let integer_str = Value::test_string("36anra");
|
||||||
|
|
||||||
let actual = action(&integer_str, Span::test_data(), 10, false);
|
let actual = action(
|
||||||
|
&integer_str,
|
||||||
|
&Arguments {
|
||||||
|
radix: 10,
|
||||||
|
cell_paths: None,
|
||||||
|
little_endian: false,
|
||||||
|
},
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual.get_type(), Error)
|
assert_eq!(actual.get_type(), Error)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ mod decimal;
|
|||||||
mod duration;
|
mod duration;
|
||||||
mod filesize;
|
mod filesize;
|
||||||
mod int;
|
mod int;
|
||||||
|
mod record;
|
||||||
mod string;
|
mod string;
|
||||||
|
|
||||||
pub use self::bool::SubCommand as IntoBool;
|
pub use self::bool::SubCommand as IntoBool;
|
||||||
@ -16,4 +17,5 @@ pub use datetime::SubCommand as IntoDatetime;
|
|||||||
pub use decimal::SubCommand as IntoDecimal;
|
pub use decimal::SubCommand as IntoDecimal;
|
||||||
pub use duration::SubCommand as IntoDuration;
|
pub use duration::SubCommand as IntoDuration;
|
||||||
pub use int::SubCommand as IntoInt;
|
pub use int::SubCommand as IntoInt;
|
||||||
|
pub use record::SubCommand as IntoRecord;
|
||||||
pub use string::SubCommand as IntoString;
|
pub use string::SubCommand as IntoString;
|
||||||
|
291
crates/nu-command/src/conversions/into/record.rs
Normal file
291
crates/nu-command/src/conversions/into/record.rs
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
|
||||||
|
use nu_protocol::format_duration_as_timeperiod;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
|
};
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
impl Command for SubCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"into record"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("into record")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Date, Type::Record(vec![])),
|
||||||
|
(Type::Duration, Type::Record(vec![])),
|
||||||
|
(Type::List(Box::new(Type::Any)), Type::Record(vec![])),
|
||||||
|
(Type::Range, Type::Record(vec![])),
|
||||||
|
(Type::Record(vec![]), Type::Record(vec![])),
|
||||||
|
(Type::Table(vec![]), Type::Record(vec![])),
|
||||||
|
])
|
||||||
|
.category(Category::Conversions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Convert value to record"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["convert"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
into_record(engine_state, call, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
let span = Span::test_data();
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Convert from one row table to record",
|
||||||
|
example: "echo [[value]; [false]] | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["value".to_string()],
|
||||||
|
vals: vec![Value::boolean(false, span)],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert from list to record",
|
||||||
|
example: "[1 2 3] | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int { val: 1, span },
|
||||||
|
Value::Int { val: 2, span },
|
||||||
|
Value::Int { val: 3, span },
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Convert from range to record",
|
||||||
|
example: "0..2 | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int { val: 0, span },
|
||||||
|
Value::Int { val: 1, span },
|
||||||
|
Value::Int { val: 2, span },
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "convert duration to record",
|
||||||
|
example: "-500day | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec![
|
||||||
|
"year".into(),
|
||||||
|
"month".into(),
|
||||||
|
"week".into(),
|
||||||
|
"day".into(),
|
||||||
|
"sign".into(),
|
||||||
|
],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int { val: 1, span },
|
||||||
|
Value::Int { val: 4, span },
|
||||||
|
Value::Int { val: 2, span },
|
||||||
|
Value::Int { val: 1, span },
|
||||||
|
Value::String {
|
||||||
|
val: "-".into(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "convert record to record",
|
||||||
|
example: "{a: 1, b: 2} | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec!["a".to_string(), "b".to_string()],
|
||||||
|
vals: vec![Value::Int { val: 1, span }, Value::Int { val: 2, span }],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "convert date to record",
|
||||||
|
example: "2020-04-12T22:10:57+02:00 | into record",
|
||||||
|
result: Some(Value::Record {
|
||||||
|
cols: vec![
|
||||||
|
"year".into(),
|
||||||
|
"month".into(),
|
||||||
|
"day".into(),
|
||||||
|
"hour".into(),
|
||||||
|
"minute".into(),
|
||||||
|
"second".into(),
|
||||||
|
"timezone".into(),
|
||||||
|
],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int { val: 2020, span },
|
||||||
|
Value::Int { val: 4, span },
|
||||||
|
Value::Int { val: 12, span },
|
||||||
|
Value::Int { val: 22, span },
|
||||||
|
Value::Int { val: 10, span },
|
||||||
|
Value::Int { val: 57, span },
|
||||||
|
Value::String {
|
||||||
|
val: "+02:00".to_string(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_record(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let input = input.into_value(call.head);
|
||||||
|
let input_type = input.get_type();
|
||||||
|
let res = match input {
|
||||||
|
Value::Date { val, span } => parse_date_into_record(Ok(val), span),
|
||||||
|
Value::Duration { val, span } => parse_duration_into_record(val, span),
|
||||||
|
Value::List { mut vals, span } => match input_type {
|
||||||
|
Type::Table(..) if vals.len() == 1 => vals.pop().expect("already checked 1 item"),
|
||||||
|
_ => {
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut values = vec![];
|
||||||
|
for (idx, val) in vals.into_iter().enumerate() {
|
||||||
|
cols.push(format!("{idx}"));
|
||||||
|
values.push(val);
|
||||||
|
}
|
||||||
|
Value::Record {
|
||||||
|
cols,
|
||||||
|
vals: values,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Value::Range { val, span } => {
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut vals = vec![];
|
||||||
|
for (idx, val) in val.into_range_iter(engine_state.ctrlc.clone())?.enumerate() {
|
||||||
|
cols.push(format!("{idx}"));
|
||||||
|
vals.push(val);
|
||||||
|
}
|
||||||
|
Value::Record { cols, vals, span }
|
||||||
|
}
|
||||||
|
Value::Record { cols, vals, span } => Value::Record { cols, vals, span },
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"'into record' does not support this input".into(),
|
||||||
|
other.span().unwrap_or(call.head),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(res.into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_date_into_record(date: Result<DateTime<FixedOffset>, Value>, span: Span) -> Value {
|
||||||
|
let cols = vec![
|
||||||
|
"year".into(),
|
||||||
|
"month".into(),
|
||||||
|
"day".into(),
|
||||||
|
"hour".into(),
|
||||||
|
"minute".into(),
|
||||||
|
"second".into(),
|
||||||
|
"timezone".into(),
|
||||||
|
];
|
||||||
|
match date {
|
||||||
|
Ok(x) => {
|
||||||
|
let vals = vec![
|
||||||
|
Value::Int {
|
||||||
|
val: x.year() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: x.month() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: x.day() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: x.hour() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: x.minute() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Int {
|
||||||
|
val: x.second() as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: x.offset().to_string(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Value::Record { cols, vals, span }
|
||||||
|
}
|
||||||
|
Err(e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_duration_into_record(duration: i64, span: Span) -> Value {
|
||||||
|
let (sign, periods) = format_duration_as_timeperiod(duration);
|
||||||
|
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut vals = vec![];
|
||||||
|
for p in periods {
|
||||||
|
let num_with_unit = p.to_text().to_string();
|
||||||
|
let split = num_with_unit.split(' ').collect::<Vec<&str>>();
|
||||||
|
cols.push(match split[1] {
|
||||||
|
"ns" => "nanosecond".into(),
|
||||||
|
"µs" => "microsecond".into(),
|
||||||
|
"ms" => "millisecond".into(),
|
||||||
|
"sec" => "second".into(),
|
||||||
|
"min" => "minute".into(),
|
||||||
|
"hr" => "hour".into(),
|
||||||
|
"day" => "day".into(),
|
||||||
|
"wk" => "week".into(),
|
||||||
|
"month" => "month".into(),
|
||||||
|
"yr" => "year".into(),
|
||||||
|
_ => "unknown".into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
vals.push(Value::Int {
|
||||||
|
val: split[0].parse::<i64>().unwrap_or(0),
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push("sign".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: if sign == -1 { "-".into() } else { "+".into() },
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
|
Value::Record { cols, vals, span }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(SubCommand {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,27 @@
|
|||||||
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||||
Span, SyntaxShape, Value,
|
Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::get_system_locale;
|
use nu_utils::get_system_locale;
|
||||||
use num_format::ToFormattedString;
|
use num_format::ToFormattedString;
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
decimals_value: Option<i64>,
|
||||||
|
decimals: bool,
|
||||||
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdArgument for Arguments {
|
||||||
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.cell_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -18,11 +32,20 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into string")
|
Signature::build("into string")
|
||||||
// FIXME - need to support column paths
|
.input_output_types(vec![
|
||||||
|
(Type::Binary, Type::String),
|
||||||
|
(Type::Int, Type::String),
|
||||||
|
(Type::Number, Type::String),
|
||||||
|
(Type::String, Type::String),
|
||||||
|
(Type::Bool, Type::String),
|
||||||
|
(Type::Filesize, Type::String),
|
||||||
|
(Type::Date, Type::String),
|
||||||
|
])
|
||||||
|
.allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"column paths to convert to string (for table input)",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
.named(
|
.named(
|
||||||
"decimals",
|
"decimals",
|
||||||
@ -121,11 +144,12 @@ impl Command for SubCommand {
|
|||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
// TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032
|
||||||
description: "convert date to string",
|
// Example {
|
||||||
example: "date now | into string",
|
// description: "convert date to string",
|
||||||
result: None,
|
// example: "'2020-10-10 10:00:00 +02:00' | into datetime | into string",
|
||||||
},
|
// result: Some(Value::test_string("Sat Oct 10 10:00:00 2020")),
|
||||||
|
// },
|
||||||
Example {
|
Example {
|
||||||
description: "convert filepath to string",
|
description: "convert filepath to string",
|
||||||
example: "ls Cargo.toml | get name | into string",
|
example: "ls Cargo.toml | get name | into string",
|
||||||
@ -133,8 +157,8 @@ impl Command for SubCommand {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert filesize to string",
|
description: "convert filesize to string",
|
||||||
example: "ls Cargo.toml | get size | into string",
|
example: "1KiB | into string",
|
||||||
result: None,
|
result: Some(Value::test_string("1,024 B")),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -149,9 +173,6 @@ fn string_helper(
|
|||||||
let decimals = call.has_flag("decimals");
|
let decimals = call.has_flag("decimals");
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
|
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
|
||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
let config = engine_state.get_config().clone();
|
|
||||||
|
|
||||||
if let Some(decimal_val) = decimals_value {
|
if let Some(decimal_val) = decimals_value {
|
||||||
if decimals && decimal_val.is_negative() {
|
if decimals && decimal_val.is_negative() {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
@ -160,6 +181,15 @@ fn string_helper(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||||
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
let config = engine_state.get_config().clone();
|
||||||
|
let args = Arguments {
|
||||||
|
decimals_value,
|
||||||
|
decimals,
|
||||||
|
cell_paths,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
|
||||||
@ -179,45 +209,18 @@ fn string_helper(
|
|||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
_ => input.map(
|
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
|
||||||
move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
action(&v, head, decimals, decimals_value, false, &config)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
for path in &column_paths {
|
|
||||||
let config = config.clone();
|
|
||||||
let r = ret.update_cell_path(
|
|
||||||
&path.members,
|
|
||||||
Box::new(move |old| {
|
|
||||||
action(old, head, decimals, decimals_value, false, &config)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if let Err(error) = r {
|
|
||||||
return Value::Error { error };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
},
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(
|
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||||
input: &Value,
|
let decimals = args.decimals;
|
||||||
span: Span,
|
let digits = args.decimals_value;
|
||||||
decimals: bool,
|
let config = &args.config;
|
||||||
digits: Option<i64>,
|
|
||||||
group_digits: bool,
|
|
||||||
config: &Config,
|
|
||||||
) -> Value {
|
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val, .. } => {
|
Value::Int { val, .. } => {
|
||||||
let decimal_value = digits.unwrap_or(0) as usize;
|
let decimal_value = digits.unwrap_or(0) as usize;
|
||||||
let res = format_int(*val, group_digits, decimal_value);
|
let res = format_int(*val, false, decimal_value);
|
||||||
Value::String { val: res, span }
|
Value::String { val: res, span }
|
||||||
}
|
}
|
||||||
Value::Float { val, .. } => {
|
Value::Float { val, .. } => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Alias;
|
pub struct Alias;
|
||||||
@ -16,6 +16,7 @@ impl Command for Alias {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("alias")
|
Signature::build("alias")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("name", SyntaxShape::String, "name of the alias")
|
.required("name", SyntaxShape::String, "name of the alias")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
@ -49,10 +50,17 @@ impl Command for Alias {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Alias ll to ls -l",
|
Example {
|
||||||
example: "alias ll = ls -l",
|
description: "Alias ll to ls -l",
|
||||||
result: None,
|
example: "alias ll = ls -l",
|
||||||
}]
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Make an alias that makes a list of all custom commands",
|
||||||
|
example: "alias customs = ($nu.scope.commands | where is_custom | get command)",
|
||||||
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ use nu_parser::parse;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -20,6 +21,7 @@ impl Command for Ast {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("ast")
|
Signature::build("ast")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required(
|
.required(
|
||||||
"pipeline",
|
"pipeline",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
@ -50,17 +52,17 @@ impl Command for Ast {
|
|||||||
Example {
|
Example {
|
||||||
description: "Print the ast of a string",
|
description: "Print the ast of a string",
|
||||||
example: "ast 'hello'",
|
example: "ast 'hello'",
|
||||||
result: None,
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the ast of a pipeline",
|
description: "Print the ast of a pipeline",
|
||||||
example: "ast 'ls | where name =~ README'",
|
example: "ast 'ls | where name =~ README'",
|
||||||
result: None,
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the ast of a pipeline with an error",
|
description: "Print the ast of a pipeline with an error",
|
||||||
example: "ast 'for x in 1..10 { echo $x '",
|
example: "ast 'for x in 1..10 { echo $x '",
|
||||||
result: None,
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
49
crates/nu-command/src/core_commands/break_.rs
Normal file
49
crates/nu-command/src/core_commands/break_.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Break;
|
||||||
|
|
||||||
|
impl Command for Break {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"break"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Break a loop"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("break")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Err(ShellError::Break(call.head))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Break out of a loop",
|
||||||
|
example: r#"loop { break }"#,
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ use nu_protocol::engine::ReplOperation;
|
|||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::IntoPipelineData;
|
use nu_protocol::IntoPipelineData;
|
||||||
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Commandline;
|
pub struct Commandline;
|
||||||
@ -16,6 +16,7 @@ impl Command for Commandline {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("commandline")
|
Signature::build("commandline")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.switch(
|
.switch(
|
||||||
"append",
|
"append",
|
||||||
"appends the string to the end of the buffer",
|
"appends the string to the end of the buffer",
|
||||||
|
49
crates/nu-command/src/core_commands/continue_.rs
Normal file
49
crates/nu-command/src/core_commands/continue_.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Continue;
|
||||||
|
|
||||||
|
impl Command for Continue {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Continue a loop from the next iteration"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("continue")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Err(ShellError::Continue(call.head))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Continue a loop from the next iteration",
|
||||||
|
example: r#"for i in 1..10 { if $i == 5 { continue }; print $i }"#,
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, ShellError, Signature, Span, Value};
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Debug;
|
pub struct Debug;
|
||||||
@ -15,11 +15,17 @@ impl Command for Debug {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("debug").category(Category::Core).switch(
|
Signature::build("debug")
|
||||||
"raw",
|
.input_output_types(vec![
|
||||||
"Prints the raw value representation",
|
(
|
||||||
Some('r'),
|
Type::List(Box::new(Type::Any)),
|
||||||
)
|
Type::List(Box::new(Type::String)),
|
||||||
|
),
|
||||||
|
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
|
||||||
|
(Type::Any, Type::String),
|
||||||
|
])
|
||||||
|
.category(Category::Core)
|
||||||
|
.switch("raw", "Prints the raw value representation", Some('r'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -54,12 +60,20 @@ impl Command for Debug {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Print the value of a string",
|
description: "Debug print a string",
|
||||||
example: "'hello' | debug",
|
example: "'hello' | debug",
|
||||||
result: Some(Value::test_string("hello")),
|
result: Some(Value::test_string("hello")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the value of a table",
|
description: "Debug print a list",
|
||||||
|
example: "['hello'] | debug",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::test_string("hello")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Debug print a table",
|
||||||
example: "echo [[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug",
|
example: "echo [[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Def;
|
pub struct Def;
|
||||||
@ -16,13 +16,10 @@ 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)])
|
||||||
.required("def_name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required(
|
.required("body", SyntaxShape::Closure(None), "body of the definition")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"body of the definition",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DefEnv;
|
pub struct DefEnv;
|
||||||
@ -16,13 +16,10 @@ impl Command for DefEnv {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("def-env")
|
Signature::build("def-env")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("def_name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required(
|
.required("block", SyntaxShape::Block, "body of the definition")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"body of the definition",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -17,7 +17,9 @@ impl Command for Describe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("describe").category(Category::Core)
|
Signature::build("describe")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::String)])
|
||||||
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{eval_block_with_early_return, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
|
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -18,14 +17,25 @@ impl Command for Do {
|
|||||||
"Run a block"
|
"Run a block"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("do")
|
Signature::build("do")
|
||||||
.required("block", SyntaxShape::Any, "the block to run")
|
.required("closure", SyntaxShape::Any, "the closure to run")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
"ignore errors as the block runs",
|
"ignore errors as the block runs",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"ignore-shell-errors",
|
||||||
|
"ignore shell errors as the block runs",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"ignore-program-errors",
|
||||||
|
"ignore program errors as the block runs",
|
||||||
|
Some('p'),
|
||||||
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"capture-errors",
|
"capture-errors",
|
||||||
"capture errors as the block runs and return it",
|
"capture errors as the block runs and return it",
|
||||||
@ -41,10 +51,12 @@ impl Command for Do {
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
let block: Closure = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||||
let ignore_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_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 mut stack = stack.captures_to_stack(&block.captures);
|
let mut stack = stack.captures_to_stack(&block.captures);
|
||||||
@ -88,93 +100,79 @@ impl Command for Do {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = eval_block(
|
let result = eval_block_with_early_return(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
ignore_errors || capture_errors,
|
capture_errors || ignore_shell_errors || ignore_program_errors,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ignore_errors {
|
match result {
|
||||||
match result {
|
Ok(PipelineData::ExternalStream {
|
||||||
Ok(x) => Ok(x),
|
stdout,
|
||||||
Err(_) => Ok(PipelineData::new(call.head)),
|
stderr,
|
||||||
}
|
exit_code,
|
||||||
} else if capture_errors {
|
span,
|
||||||
// collect stdout and stderr and check exit code.
|
metadata,
|
||||||
// if exit code is not 0, return back ShellError.
|
trim_end_newline,
|
||||||
match result {
|
}) if capture_errors => {
|
||||||
|
let mut exit_code_ctrlc = None;
|
||||||
|
let exit_code: Vec<Value> = match exit_code {
|
||||||
|
None => vec![],
|
||||||
|
Some(exit_code_stream) => {
|
||||||
|
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
|
||||||
|
exit_code_stream.into_iter().collect()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
|
||||||
|
if *code != 0 {
|
||||||
|
let stderr_msg = match stderr {
|
||||||
|
None => "".to_string(),
|
||||||
|
Some(stderr_stream) => stderr_stream.into_string().map(|s| s.item)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(ShellError::ExternalCommand(
|
||||||
|
"External command failed".to_string(),
|
||||||
|
stderr_msg,
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout,
|
stdout,
|
||||||
stderr,
|
stderr,
|
||||||
exit_code,
|
exit_code: Some(ListStream::from_stream(
|
||||||
|
exit_code.into_iter(),
|
||||||
|
exit_code_ctrlc,
|
||||||
|
)),
|
||||||
span,
|
span,
|
||||||
metadata,
|
metadata,
|
||||||
}) => {
|
trim_end_newline,
|
||||||
// collect all output first.
|
})
|
||||||
let mut stderr_ctrlc = None;
|
|
||||||
let stderr_msg = match stderr {
|
|
||||||
None => "".to_string(),
|
|
||||||
Some(stderr_stream) => {
|
|
||||||
stderr_ctrlc = stderr_stream.ctrlc.clone();
|
|
||||||
stderr_stream.into_string().map(|s| s.item)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut stdout_ctrlc = None;
|
|
||||||
let stdout_msg = match stdout {
|
|
||||||
None => "".to_string(),
|
|
||||||
Some(stdout_stream) => {
|
|
||||||
stdout_ctrlc = stdout_stream.ctrlc.clone();
|
|
||||||
stdout_stream.into_string().map(|s| s.item)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut exit_code_ctrlc = None;
|
|
||||||
let exit_code: Vec<Value> = match exit_code {
|
|
||||||
None => vec![],
|
|
||||||
Some(exit_code_stream) => {
|
|
||||||
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
|
|
||||||
exit_code_stream.into_iter().collect()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
|
|
||||||
// if exit_code is not 0, it indicates error occured, return back Err.
|
|
||||||
if *code != 0 {
|
|
||||||
return Err(ShellError::ExternalCommand(
|
|
||||||
"External command runs to failed".to_string(),
|
|
||||||
stderr_msg,
|
|
||||||
span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// construct pipeline data to our caller
|
|
||||||
Ok(PipelineData::ExternalStream {
|
|
||||||
stdout: Some(RawStream::new(
|
|
||||||
Box::new(vec![Ok(stdout_msg.into_bytes())].into_iter()),
|
|
||||||
stdout_ctrlc,
|
|
||||||
span,
|
|
||||||
)),
|
|
||||||
stderr: Some(RawStream::new(
|
|
||||||
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
|
|
||||||
stderr_ctrlc,
|
|
||||||
span,
|
|
||||||
)),
|
|
||||||
exit_code: Some(ListStream::from_stream(
|
|
||||||
exit_code.into_iter(),
|
|
||||||
exit_code_ctrlc,
|
|
||||||
)),
|
|
||||||
span,
|
|
||||||
metadata,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(other) => Ok(other),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
}
|
||||||
} else {
|
Ok(PipelineData::ExternalStream {
|
||||||
result
|
stdout,
|
||||||
|
stderr,
|
||||||
|
exit_code: _,
|
||||||
|
span,
|
||||||
|
metadata,
|
||||||
|
trim_end_newline,
|
||||||
|
}) if ignore_program_errors => Ok(PipelineData::ExternalStream {
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
exit_code: None,
|
||||||
|
span,
|
||||||
|
metadata,
|
||||||
|
trim_end_newline,
|
||||||
|
}),
|
||||||
|
Ok(PipelineData::Value(Value::Error { .. }, ..)) if ignore_shell_errors => {
|
||||||
|
Ok(PipelineData::new(call.head))
|
||||||
|
}
|
||||||
|
Err(_) if ignore_shell_errors => Ok(PipelineData::new(call.head)),
|
||||||
|
r => r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,14 +184,34 @@ impl Command for Do {
|
|||||||
result: Some(Value::test_string("hello")),
|
result: Some(Value::test_string("hello")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the block and ignore errors",
|
description: "Run the block and ignore both shell and program errors",
|
||||||
example: r#"do -i { thisisnotarealcommand }"#,
|
example: r#"do -i { thisisnotarealcommand }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the block and ignore shell errors",
|
||||||
|
example: r#"do -s { thisisnotarealcommand }"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the block and ignore program errors",
|
||||||
|
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Abort the pipeline if a program returns a non-zero exit code",
|
||||||
|
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the block, with a positional parameter",
|
description: "Run the block, with a positional parameter",
|
||||||
example: r#"do {|x| 100 + $x } 50"#,
|
example: r#"do {|x| 100 + $x } 77"#,
|
||||||
result: Some(Value::test_int(150)),
|
result: Some(Value::test_int(177)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the block, with input",
|
||||||
|
example: r#"77 | do {|x| 100 + $in }"#,
|
||||||
|
result: None, // TODO: returns 177
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
|||||||
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::{
|
||||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -14,17 +15,20 @@ impl Command for Echo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Echo the arguments back to the user."
|
"Returns its arguments, ignoring the piped-in value."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("echo")
|
Signature::build("echo")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Any)])
|
||||||
.rest("rest", SyntaxShape::Any, "the values to echo")
|
.rest("rest", SyntaxShape::Any, "the values to echo")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline."
|
r#"When given no arguments, it returns an empty string. When given one argument,
|
||||||
|
it returns it. Otherwise, it returns a list of the arguments. There is usually
|
||||||
|
little reason to use this over just writing the values as-is."#
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -61,13 +65,17 @@ impl Command for Echo {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Put a hello message in the pipeline",
|
description: "Put a list of numbers in the pipeline. This is the same as [1 2 3].",
|
||||||
example: "echo 'hello'",
|
example: "echo 1 2 3",
|
||||||
result: Some(Value::test_string("hello")),
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Print the value of the special '$nu' variable",
|
description:
|
||||||
example: "echo $nu",
|
"Returns the piped-in value, by using the special $in variable to obtain it.",
|
||||||
|
example: "echo $in",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::get_full_help;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Value,
|
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -14,7 +14,9 @@ impl Command for ExportCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("export").category(Category::Core)
|
Signature::build("export")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -43,6 +45,7 @@ impl Command for ExportCommand {
|
|||||||
&ExportCommand.examples(),
|
&ExportCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape};
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExportAlias;
|
pub struct ExportAlias;
|
||||||
@ -16,6 +16,7 @@ impl Command for ExportAlias {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("export alias")
|
Signature::build("export alias")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("name", SyntaxShape::String, "name of the alias")
|
.required("name", SyntaxShape::String, "name of the alias")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExportDef;
|
pub struct ExportDef;
|
||||||
@ -16,13 +16,10 @@ 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)])
|
||||||
.required("name", SyntaxShape::String, "definition name")
|
.required("name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required(
|
.required("block", SyntaxShape::Block, "body of the definition")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"body of the definition",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExportDefEnv;
|
pub struct ExportDefEnv;
|
||||||
@ -16,13 +16,10 @@ impl Command for ExportDefEnv {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("export def-env")
|
Signature::build("export def-env")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("name", SyntaxShape::String, "definition name")
|
.required("name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.required(
|
.required("block", SyntaxShape::Block, "body of the definition")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"body of the definition",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape};
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExportExtern;
|
pub struct ExportExtern;
|
||||||
@ -16,6 +16,7 @@ impl Command for ExportExtern {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("export extern")
|
Signature::build("export extern")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("def_name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExportUse;
|
pub struct ExportUse;
|
||||||
@ -16,6 +16,7 @@ impl Command for ExportUse {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("export use")
|
Signature::build("export use")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
|
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape};
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Extern;
|
pub struct Extern;
|
||||||
@ -16,6 +16,7 @@ impl Command for Extern {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("extern")
|
Signature::build("extern")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("def_name", SyntaxShape::String, "definition name")
|
.required("def_name", SyntaxShape::String, "definition name")
|
||||||
.required("params", SyntaxShape::Signature, "parameters")
|
.required("params", SyntaxShape::Signature, "parameters")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use nu_engine::{eval_block, eval_expression, CallExt};
|
use nu_engine::{eval_block, eval_expression, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
use nu_protocol::engine::{Block, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, Signature, Span,
|
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||||
SyntaxShape, Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -30,11 +29,7 @@ impl Command for For {
|
|||||||
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)),
|
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)),
|
||||||
"range of the loop",
|
"range of the loop",
|
||||||
)
|
)
|
||||||
.required(
|
.required("block", SyntaxShape::Block, "the block to run")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"the block to run",
|
|
||||||
)
|
|
||||||
.switch(
|
.switch(
|
||||||
"numbered",
|
"numbered",
|
||||||
"returned a numbered item ($it.index and $it.item)",
|
"returned a numbered item ($it.index and $it.item)",
|
||||||
@ -74,66 +69,22 @@ impl Command for For {
|
|||||||
.expect("internal error: missing keyword");
|
.expect("internal error: missing keyword");
|
||||||
let values = eval_expression(engine_state, stack, keyword_expr)?;
|
let values = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
|
|
||||||
let capture_block: CaptureBlock = call.req(engine_state, stack, 2)?;
|
let block: Block = call.req(engine_state, stack, 2)?;
|
||||||
|
|
||||||
let numbered = call.has_flag("numbered");
|
let numbered = call.has_flag("numbered");
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = engine_state.clone();
|
let engine_state = engine_state.clone();
|
||||||
let block = engine_state.get_block(capture_block.block_id).clone();
|
let block = engine_state.get_block(block.block_id).clone();
|
||||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
|
||||||
let orig_env_vars = stack.env_vars.clone();
|
|
||||||
let orig_env_hidden = stack.env_hidden.clone();
|
|
||||||
let redirect_stdout = call.redirect_stdout;
|
let redirect_stdout = call.redirect_stdout;
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
match values {
|
match values {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
Ok(ListStream::from_stream(vals.into_iter(), ctrlc.clone())
|
for (idx, x) in ListStream::from_stream(vals.into_iter(), ctrlc).enumerate() {
|
||||||
.enumerate()
|
// with_env() is used here to ensure that each iteration uses
|
||||||
.map(move |(idx, x)| {
|
// a different set of environment variables.
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
// Hence, a 'cd' in the first loop won't affect the next loop.
|
||||||
|
|
||||||
stack.add_var(
|
|
||||||
var_id,
|
|
||||||
if numbered {
|
|
||||||
Value::Record {
|
|
||||||
cols: vec!["index".into(), "item".into()],
|
|
||||||
vals: vec![
|
|
||||||
Value::Int {
|
|
||||||
val: idx as i64,
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
x,
|
|
||||||
],
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
//let block = engine_state.get_block(block_id);
|
|
||||||
match eval_block(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
PipelineData::new(head),
|
|
||||||
redirect_stdout,
|
|
||||||
redirect_stderr,
|
|
||||||
) {
|
|
||||||
Ok(pipeline_data) => pipeline_data.into_value(head),
|
|
||||||
Err(error) => Value::Error { error },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(|x| !x.is_nothing())
|
|
||||||
.into_pipeline_data(ctrlc))
|
|
||||||
}
|
|
||||||
Value::Range { val, .. } => Ok(val
|
|
||||||
.into_range_iter(ctrlc.clone())?
|
|
||||||
.enumerate()
|
|
||||||
.map(move |(idx, x)| {
|
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
|
||||||
|
|
||||||
stack.add_var(
|
stack.add_var(
|
||||||
var_id,
|
var_id,
|
||||||
@ -157,76 +108,106 @@ impl Command for For {
|
|||||||
//let block = engine_state.get_block(block_id);
|
//let block = engine_state.get_block(block_id);
|
||||||
match eval_block(
|
match eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::new(head),
|
PipelineData::new(head),
|
||||||
redirect_stdout,
|
redirect_stdout,
|
||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
) {
|
) {
|
||||||
Ok(pipeline_data) => pipeline_data.into_value(head),
|
Err(ShellError::Break(_)) => {
|
||||||
Err(error) => Value::Error { error },
|
break;
|
||||||
|
}
|
||||||
|
Err(ShellError::Continue(_)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(pipeline) => {
|
||||||
|
pipeline.into_value(head);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.filter(|x| !x.is_nothing())
|
}
|
||||||
.into_pipeline_data(ctrlc)),
|
Value::Range { val, .. } => {
|
||||||
|
for (idx, x) in val.into_range_iter(ctrlc)?.enumerate() {
|
||||||
|
stack.add_var(
|
||||||
|
var_id,
|
||||||
|
if numbered {
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
//let block = engine_state.get_block(block_id);
|
||||||
|
match eval_block(
|
||||||
|
&engine_state,
|
||||||
|
stack,
|
||||||
|
&block,
|
||||||
|
PipelineData::new(head),
|
||||||
|
redirect_stdout,
|
||||||
|
redirect_stderr,
|
||||||
|
) {
|
||||||
|
Err(ShellError::Break(_)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ShellError::Continue(_)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(pipeline) => {
|
||||||
|
pipeline.into_value(head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
stack.add_var(var_id, x);
|
stack.add_var(var_id, x);
|
||||||
|
|
||||||
eval_block(
|
eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::new(head),
|
PipelineData::new(head),
|
||||||
redirect_stdout,
|
redirect_stdout,
|
||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
)
|
)?
|
||||||
|
.into_value(head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(PipelineData::new(head))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
let span = Span::test_data();
|
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Echo the square of each integer",
|
description: "Echo the square of each integer",
|
||||||
example: "for x in [1 2 3] { $x * $x }",
|
example: "for x in [1 2 3] { print ($x * $x) }",
|
||||||
result: Some(Value::List {
|
result: None,
|
||||||
vals: vec![
|
|
||||||
Value::Int { val: 1, span },
|
|
||||||
Value::Int { val: 4, span },
|
|
||||||
Value::Int { val: 9, span },
|
|
||||||
],
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Work with elements of a range",
|
description: "Work with elements of a range",
|
||||||
example: "for $x in 1..3 { $x }",
|
example: "for $x in 1..3 { print $x }",
|
||||||
result: Some(Value::List {
|
result: None,
|
||||||
vals: vec![
|
|
||||||
Value::Int { val: 1, span },
|
|
||||||
Value::Int { val: 2, span },
|
|
||||||
Value::Int { val: 3, span },
|
|
||||||
],
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Number each item and echo a message",
|
description: "Number each item and echo a message",
|
||||||
example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }",
|
example:
|
||||||
result: Some(Value::List {
|
"for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
|
||||||
vals: vec![
|
result: None,
|
||||||
Value::String {
|
|
||||||
val: "0 is bob".into(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
Value::String {
|
|
||||||
val: "1 is fred".into(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use nu_protocol::{
|
|||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||||
ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -22,6 +22,7 @@ impl Command for Help {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("help")
|
Signature::build("help")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
@ -103,6 +104,7 @@ fn help(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
let decl = engine_state.get_decl(decl_id);
|
let decl = engine_state.get_decl(decl_id);
|
||||||
let sig = decl.signature().update_from_command(decl.borrow());
|
let sig = decl.signature().update_from_command(decl.borrow());
|
||||||
|
let signatures = sig.to_string();
|
||||||
let key = sig.name;
|
let key = sig.name;
|
||||||
let usage = sig.usage;
|
let usage = sig.usage;
|
||||||
let search_terms = sig.search_terms;
|
let search_terms = sig.search_terms;
|
||||||
@ -134,21 +136,9 @@ fn help(
|
|||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("is_plugin".into());
|
cols.push("command_type".into());
|
||||||
vals.push(Value::Bool {
|
vals.push(Value::String {
|
||||||
val: decl.is_plugin().is_some(),
|
val: format!("{:?}", decl.command_type()).to_lowercase(),
|
||||||
span: head,
|
|
||||||
});
|
|
||||||
|
|
||||||
cols.push("is_custom".into());
|
|
||||||
vals.push(Value::Bool {
|
|
||||||
val: decl.is_custom_command(),
|
|
||||||
span: head,
|
|
||||||
});
|
|
||||||
|
|
||||||
cols.push("is_keyword".into());
|
|
||||||
vals.push(Value::Bool {
|
|
||||||
val: decl.is_parser_keyword(),
|
|
||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -162,6 +152,16 @@ fn help(
|
|||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cols.push("signatures".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: if decl.is_parser_keyword() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
signatures
|
||||||
|
},
|
||||||
|
span: head,
|
||||||
|
});
|
||||||
|
|
||||||
cols.push("search_terms".into());
|
cols.push("search_terms".into());
|
||||||
vals.push(if search_terms.is_empty() {
|
vals.push(if search_terms.is_empty() {
|
||||||
Value::nothing(head)
|
Value::nothing(head)
|
||||||
@ -219,6 +219,7 @@ fn help(
|
|||||||
let decl = engine_state.get_decl(decl_id);
|
let decl = engine_state.get_decl(decl_id);
|
||||||
let sig = decl.signature().update_from_command(decl.borrow());
|
let sig = decl.signature().update_from_command(decl.borrow());
|
||||||
|
|
||||||
|
let signatures = sig.to_string();
|
||||||
let key = sig.name;
|
let key = sig.name;
|
||||||
let usage = sig.usage;
|
let usage = sig.usage;
|
||||||
let search_terms = sig.search_terms;
|
let search_terms = sig.search_terms;
|
||||||
@ -235,21 +236,9 @@ fn help(
|
|||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
cols.push("is_plugin".into());
|
cols.push("command_type".into());
|
||||||
vals.push(Value::Bool {
|
vals.push(Value::String {
|
||||||
val: decl.is_plugin().is_some(),
|
val: format!("{:?}", decl.command_type()).to_lowercase(),
|
||||||
span: head,
|
|
||||||
});
|
|
||||||
|
|
||||||
cols.push("is_custom".into());
|
|
||||||
vals.push(Value::Bool {
|
|
||||||
val: decl.is_custom_command(),
|
|
||||||
span: head,
|
|
||||||
});
|
|
||||||
|
|
||||||
cols.push("is_keyword".into());
|
|
||||||
vals.push(Value::Bool {
|
|
||||||
val: decl.is_parser_keyword(),
|
|
||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -259,6 +248,16 @@ fn help(
|
|||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cols.push("signatures".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: if decl.is_parser_keyword() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
signatures
|
||||||
|
},
|
||||||
|
span: head,
|
||||||
|
});
|
||||||
|
|
||||||
cols.push("search_terms".into());
|
cols.push("search_terms".into());
|
||||||
vals.push(if search_terms.is_empty() {
|
vals.push(if search_terms.is_empty() {
|
||||||
Value::nothing(head)
|
Value::nothing(head)
|
||||||
@ -292,9 +291,9 @@ fn help(
|
|||||||
let output = engine_state
|
let output = engine_state
|
||||||
.get_signatures_with_examples(false)
|
.get_signatures_with_examples(false)
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(signature, _, _, _)| signature.name == name)
|
.filter(|(signature, _, _, _, _)| signature.name == name)
|
||||||
.map(|(signature, examples, _, _)| {
|
.map(|(signature, examples, _, _, is_parser_keyword)| {
|
||||||
get_full_help(signature, examples, engine_state, stack)
|
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
@ -363,15 +362,12 @@ pub fn highlight_search_string(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// strip haystack to remove existing ansi style
|
// strip haystack to remove existing ansi style
|
||||||
let stripped_haystack: String = match strip_ansi_escapes::strip(haystack) {
|
let stripped_haystack = nu_utils::strip_ansi_likely(haystack);
|
||||||
Ok(i) => String::from_utf8(i).unwrap_or_else(|_| String::from(haystack)),
|
|
||||||
Err(_) => String::from(haystack),
|
|
||||||
};
|
|
||||||
let mut last_match_end = 0;
|
let mut last_match_end = 0;
|
||||||
let style = Style::new().fg(White).on(Red);
|
let style = Style::new().fg(White).on(Red);
|
||||||
let mut highlighted = String::new();
|
let mut highlighted = String::new();
|
||||||
|
|
||||||
for cap in regex.captures_iter(stripped_haystack.as_str()) {
|
for cap in regex.captures_iter(stripped_haystack.as_ref()) {
|
||||||
match cap {
|
match cap {
|
||||||
Ok(capture) => {
|
Ok(capture) => {
|
||||||
let start = match capture.get(0) {
|
let start = match capture.get(0) {
|
||||||
|
333
crates/nu-command/src/core_commands/help_operators.rs
Normal file
333
crates/nu-command/src/core_commands/help_operators.rs
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HelpOperators;
|
||||||
|
|
||||||
|
impl Command for HelpOperators {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"help operators"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Show help on nushell operators."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("help operators").category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
let op_info = generate_operator_info();
|
||||||
|
let mut recs = vec![];
|
||||||
|
|
||||||
|
for op in op_info {
|
||||||
|
let mut cols = vec![];
|
||||||
|
let mut vals = vec![];
|
||||||
|
cols.push("type".into());
|
||||||
|
vals.push(Value::string(op.op_type, head));
|
||||||
|
cols.push("operator".into());
|
||||||
|
vals.push(Value::string(op.operator, head));
|
||||||
|
cols.push("name".into());
|
||||||
|
vals.push(Value::string(op.name, head));
|
||||||
|
cols.push("description".into());
|
||||||
|
vals.push(Value::string(op.description, head));
|
||||||
|
cols.push("precedence".into());
|
||||||
|
vals.push(Value::int(op.precedence, head));
|
||||||
|
recs.push(Value::Record {
|
||||||
|
cols,
|
||||||
|
vals,
|
||||||
|
span: head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(recs
|
||||||
|
.into_iter()
|
||||||
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperatorInfo {
|
||||||
|
op_type: String,
|
||||||
|
operator: String,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
precedence: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_operator_info() -> Vec<OperatorInfo> {
|
||||||
|
vec![
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Assignment".into(),
|
||||||
|
operator: "=".into(),
|
||||||
|
name: "Assign".into(),
|
||||||
|
description: "Assigns a value to a variable.".into(),
|
||||||
|
precedence: 10,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Assignment".into(),
|
||||||
|
operator: "+=".into(),
|
||||||
|
name: "PlusAssign".into(),
|
||||||
|
description: "Adds a value to a variable.".into(),
|
||||||
|
precedence: 10,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Assignment".into(),
|
||||||
|
operator: "-=".into(),
|
||||||
|
name: "MinusAssign".into(),
|
||||||
|
description: "Subtracts a value from a variable.".into(),
|
||||||
|
precedence: 10,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Assignment".into(),
|
||||||
|
operator: "*=".into(),
|
||||||
|
name: "MultiplyAssign".into(),
|
||||||
|
description: "Multiplies a variable by a value.".into(),
|
||||||
|
precedence: 10,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Assignment".into(),
|
||||||
|
operator: "/=".into(),
|
||||||
|
name: "DivideAssign".into(),
|
||||||
|
description: "Divides a variable by a value.".into(),
|
||||||
|
precedence: 10,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "==".into(),
|
||||||
|
name: "Equal".into(),
|
||||||
|
description: "Checks if two values are equal.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "!=".into(),
|
||||||
|
name: "NotEqual".into(),
|
||||||
|
description: "Checks if two values are not equal.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "<".into(),
|
||||||
|
name: "LessThan".into(),
|
||||||
|
description: "Checks if a value is less than another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "<=".into(),
|
||||||
|
name: "LessThanOrEqual".into(),
|
||||||
|
description: "Checks if a value is less than or equal to another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: ">".into(),
|
||||||
|
name: "GreaterThan".into(),
|
||||||
|
description: "Checks if a value is greater than another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: ">=".into(),
|
||||||
|
name: "GreaterThanOrEqual".into(),
|
||||||
|
description: "Checks if a value is greater than or equal to another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "=~".into(),
|
||||||
|
name: "RegexMatch".into(),
|
||||||
|
description: "Checks if a value matches a regular expression.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "!~".into(),
|
||||||
|
name: "NotRegexMatch".into(),
|
||||||
|
description: "Checks if a value does not match a regular expression.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "in".into(),
|
||||||
|
name: "In".into(),
|
||||||
|
description: "Checks if a value is in a list or string.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "not-in".into(),
|
||||||
|
name: "NotIn".into(),
|
||||||
|
description: "Checks if a value is not in a list or string.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "starts-with".into(),
|
||||||
|
name: "StartsWith".into(),
|
||||||
|
description: "Checks if a string starts with another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "ends-with".into(),
|
||||||
|
name: "EndsWith".into(),
|
||||||
|
description: "Checks if a string ends with another.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Comparison".into(),
|
||||||
|
operator: "not".into(),
|
||||||
|
name: "UnaryNot".into(),
|
||||||
|
description: "Negates a value or expression.".into(),
|
||||||
|
precedence: 0,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "+".into(),
|
||||||
|
name: "Plus".into(),
|
||||||
|
description: "Adds two values.".into(),
|
||||||
|
precedence: 90,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "++".into(),
|
||||||
|
name: "Append".into(),
|
||||||
|
description: "Appends two lists or a list and a value.".into(),
|
||||||
|
precedence: 80,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "-".into(),
|
||||||
|
name: "Minus".into(),
|
||||||
|
description: "Subtracts two values.".into(),
|
||||||
|
precedence: 90,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "*".into(),
|
||||||
|
name: "Multiply".into(),
|
||||||
|
description: "Multiplies two values.".into(),
|
||||||
|
precedence: 95,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "/".into(),
|
||||||
|
name: "Divide".into(),
|
||||||
|
description: "Divides two values.".into(),
|
||||||
|
precedence: 95,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "//".into(),
|
||||||
|
name: "FloorDivision".into(),
|
||||||
|
description: "Divides two values and floors the result.".into(),
|
||||||
|
precedence: 95,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "mod".into(),
|
||||||
|
name: "Modulo".into(),
|
||||||
|
description: "Divides two values and returns the remainder.".into(),
|
||||||
|
precedence: 95,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Math".into(),
|
||||||
|
operator: "**".into(),
|
||||||
|
name: "Pow ".into(),
|
||||||
|
description: "Raises one value to the power of another.".into(),
|
||||||
|
precedence: 100,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Bitwise".into(),
|
||||||
|
operator: "bit-or".into(),
|
||||||
|
name: "BitOr".into(),
|
||||||
|
description: "Performs a bitwise OR on two values.".into(),
|
||||||
|
precedence: 60,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Bitwise".into(),
|
||||||
|
operator: "bit-xor".into(),
|
||||||
|
name: "BitXor".into(),
|
||||||
|
description: "Performs a bitwise XOR on two values.".into(),
|
||||||
|
precedence: 70,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Bitwise".into(),
|
||||||
|
operator: "bit-and".into(),
|
||||||
|
name: "BitAnd".into(),
|
||||||
|
description: "Performs a bitwise AND on two values.".into(),
|
||||||
|
precedence: 75,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Bitwise".into(),
|
||||||
|
operator: "bit-shl".into(),
|
||||||
|
name: "ShiftLeft".into(),
|
||||||
|
description: "Shifts a value left by another.".into(),
|
||||||
|
precedence: 85,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Bitwise".into(),
|
||||||
|
operator: "bit-shr".into(),
|
||||||
|
name: "ShiftRight".into(),
|
||||||
|
description: "Shifts a value right by another.".into(),
|
||||||
|
precedence: 85,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Boolean".into(),
|
||||||
|
operator: "&&".into(),
|
||||||
|
name: "And".into(),
|
||||||
|
description: "Deprecated. Checks if two values are true.".into(),
|
||||||
|
precedence: 50,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Boolean".into(),
|
||||||
|
operator: "and".into(),
|
||||||
|
name: "And".into(),
|
||||||
|
description: "Checks if two values are true.".into(),
|
||||||
|
precedence: 50,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Boolean".into(),
|
||||||
|
operator: "||".into(),
|
||||||
|
name: "Or".into(),
|
||||||
|
description: "Deprecated. Checks if either value is true.".into(),
|
||||||
|
precedence: 40,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Boolean".into(),
|
||||||
|
operator: "or".into(),
|
||||||
|
name: "Or".into(),
|
||||||
|
description: "Checks if either value is true.".into(),
|
||||||
|
precedence: 40,
|
||||||
|
},
|
||||||
|
OperatorInfo {
|
||||||
|
op_type: "Boolean".into(),
|
||||||
|
operator: "xor".into(),
|
||||||
|
name: "Xor".into(),
|
||||||
|
description: "Checks if one value is true and the other is false.".into(),
|
||||||
|
precedence: 45,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use super::HelpOperators;
|
||||||
|
use crate::test_examples;
|
||||||
|
test_examples(HelpOperators {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use nu_protocol::ast::{Call, Expr, Expression};
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -14,6 +14,7 @@ impl Command for Hide {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("hide")
|
Signature::build("hide")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
|
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ 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::{
|
||||||
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
|
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
|
||||||
SyntaxShape, Value,
|
SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -16,6 +16,7 @@ impl Command for HideEnv {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("hide-env")
|
Signature::build("hide-env")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.rest(
|
.rest(
|
||||||
"name",
|
"name",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt};
|
use nu_engine::{eval_block, eval_expression, eval_expression_with_input, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
use nu_protocol::engine::{Block, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, FromValue, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -19,10 +19,11 @@ impl Command for If {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("if")
|
Signature::build("if")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
.required("cond", SyntaxShape::Expression, "condition to check")
|
.required("cond", SyntaxShape::Expression, "condition to check")
|
||||||
.required(
|
.required(
|
||||||
"then_block",
|
"then_block",
|
||||||
SyntaxShape::Block(Some(vec![])),
|
SyntaxShape::Block,
|
||||||
"block to run if check succeeds",
|
"block to run if check succeeds",
|
||||||
)
|
)
|
||||||
.optional(
|
.optional(
|
||||||
@ -50,7 +51,7 @@ impl Command for If {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let then_block: CaptureBlock = call.req(engine_state, stack, 1)?;
|
let then_block: Block = call.req(engine_state, stack, 1)?;
|
||||||
let else_case = call.positional_nth(2);
|
let else_case = call.positional_nth(2);
|
||||||
|
|
||||||
let result = eval_expression(engine_state, stack, cond)?;
|
let result = eval_expression(engine_state, stack, cond)?;
|
||||||
@ -58,10 +59,9 @@ impl Command for If {
|
|||||||
Value::Bool { val, .. } => {
|
Value::Bool { val, .. } => {
|
||||||
if *val {
|
if *val {
|
||||||
let block = engine_state.get_block(then_block.block_id);
|
let block = engine_state.get_block(then_block.block_id);
|
||||||
let mut stack = stack.captures_to_stack(&then_block.captures);
|
|
||||||
eval_block(
|
eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
stack,
|
||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
@ -70,14 +70,10 @@ impl Command for If {
|
|||||||
} else if let Some(else_case) = else_case {
|
} else if let Some(else_case) = else_case {
|
||||||
if let Some(else_expr) = else_case.as_keyword() {
|
if let Some(else_expr) = else_case.as_keyword() {
|
||||||
if let Some(block_id) = else_expr.as_block() {
|
if let Some(block_id) = else_expr.as_block() {
|
||||||
let result = eval_expression(engine_state, stack, else_expr)?;
|
|
||||||
let else_block: CaptureBlock = FromValue::from_value(&result)?;
|
|
||||||
|
|
||||||
let mut stack = stack.captures_to_stack(&else_block.captures);
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
eval_block(
|
eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack,
|
stack,
|
||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Ignore;
|
pub struct Ignore;
|
||||||
@ -15,7 +15,9 @@ impl Command for Ignore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("ignore").category(Category::Core)
|
Signature::build("ignore")
|
||||||
|
.input_output_types(vec![(Type::Any, Type::Nothing)])
|
||||||
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -37,7 +39,7 @@ impl Command for Ignore {
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Ignore the output of an echo command",
|
description: "Ignore the output of an echo command",
|
||||||
example: "echo done | ignore",
|
example: "echo done | ignore",
|
||||||
result: None,
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_engine::eval_expression_with_input;
|
use nu_engine::eval_expression_with_input;
|
||||||
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::{Category, Example, PipelineData, Signature, SyntaxShape};
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Let;
|
pub struct Let;
|
||||||
@ -17,6 +17,8 @@ impl Command for Let {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("let")
|
Signature::build("let")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
||||||
.required(
|
.required(
|
||||||
"initial_value",
|
"initial_value",
|
||||||
@ -97,6 +99,8 @@ impl Command for Let {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -105,4 +109,9 @@ mod test {
|
|||||||
|
|
||||||
test_examples(Let {})
|
test_examples(Let {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_command_type() {
|
||||||
|
assert!(matches!(Let.command_type(), CommandType::Keyword));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
101
crates/nu-command/src/core_commands/loop_.rs
Normal file
101
crates/nu-command/src/core_commands/loop_.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use nu_engine::{eval_block, CallExt};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Block, Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Loop;
|
||||||
|
|
||||||
|
impl Command for Loop {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"loop"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Run a block in a loop."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("loop")
|
||||||
|
.required("block", SyntaxShape::Block, "block to loop")
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let block: Block = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(ctrlc) = &engine_state.ctrlc {
|
||||||
|
if ctrlc.load(Ordering::SeqCst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = engine_state.get_block(block.block_id);
|
||||||
|
match eval_block(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
block,
|
||||||
|
PipelineData::new(call.head),
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
) {
|
||||||
|
Err(ShellError::Break(_)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ShellError::Continue(_)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(pipeline) => {
|
||||||
|
pipeline.into_value(call.head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(PipelineData::new(call.head))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Loop while a condition is true",
|
||||||
|
example: "mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x",
|
||||||
|
result: Some(Value::Int {
|
||||||
|
val: 11,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Loop {})
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ use nu_protocol::ast::{Call, Expr, Expression};
|
|||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature,
|
Category, DataSource, Example, IntoPipelineData, PipelineData, PipelineMetadata, Signature,
|
||||||
Span, SyntaxShape, Value,
|
Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -20,6 +20,8 @@ impl Command for Metadata {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("metadata")
|
Signature::build("metadata")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
.optional(
|
.optional(
|
||||||
"expression",
|
"expression",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
@ -84,6 +86,15 @@ impl Command for Metadata {
|
|||||||
span: head,
|
span: head,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
PipelineMetadata {
|
||||||
|
data_source: DataSource::HtmlThemes,
|
||||||
|
} => {
|
||||||
|
cols.push("source".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: "into html --list".into(),
|
||||||
|
span: head,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +157,15 @@ fn build_metadata_record(arg: &Value, metadata: &Option<PipelineMetadata>, head:
|
|||||||
span: head,
|
span: head,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
PipelineMetadata {
|
||||||
|
data_source: DataSource::HtmlThemes,
|
||||||
|
} => {
|
||||||
|
cols.push("source".into());
|
||||||
|
vals.push(Value::String {
|
||||||
|
val: "into html --list".into(),
|
||||||
|
span: head,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
mod alias;
|
mod alias;
|
||||||
mod ast;
|
mod ast;
|
||||||
|
mod break_;
|
||||||
mod commandline;
|
mod commandline;
|
||||||
|
mod continue_;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod def;
|
mod def;
|
||||||
mod def_env;
|
mod def_env;
|
||||||
@ -17,20 +19,28 @@ mod export_use;
|
|||||||
mod extern_;
|
mod extern_;
|
||||||
mod for_;
|
mod for_;
|
||||||
pub mod help;
|
pub mod help;
|
||||||
|
mod help_operators;
|
||||||
mod hide;
|
mod hide;
|
||||||
mod hide_env;
|
mod hide_env;
|
||||||
mod if_;
|
mod if_;
|
||||||
mod ignore;
|
mod ignore;
|
||||||
mod let_;
|
mod let_;
|
||||||
|
mod loop_;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
mod module;
|
mod module;
|
||||||
|
mod mut_;
|
||||||
pub(crate) mod overlay;
|
pub(crate) mod overlay;
|
||||||
|
mod return_;
|
||||||
|
mod try_;
|
||||||
mod use_;
|
mod use_;
|
||||||
mod version;
|
mod version;
|
||||||
|
mod while_;
|
||||||
|
|
||||||
pub use alias::Alias;
|
pub use alias::Alias;
|
||||||
pub use ast::Ast;
|
pub use ast::Ast;
|
||||||
|
pub use break_::Break;
|
||||||
pub use commandline::Commandline;
|
pub use commandline::Commandline;
|
||||||
|
pub use continue_::Continue;
|
||||||
pub use debug::Debug;
|
pub use debug::Debug;
|
||||||
pub use def::Def;
|
pub use def::Def;
|
||||||
pub use def_env::DefEnv;
|
pub use def_env::DefEnv;
|
||||||
@ -47,16 +57,22 @@ pub use export_use::ExportUse;
|
|||||||
pub use extern_::Extern;
|
pub use extern_::Extern;
|
||||||
pub use for_::For;
|
pub use for_::For;
|
||||||
pub use help::Help;
|
pub use help::Help;
|
||||||
|
pub use help_operators::HelpOperators;
|
||||||
pub use hide::Hide;
|
pub use hide::Hide;
|
||||||
pub use hide_env::HideEnv;
|
pub use hide_env::HideEnv;
|
||||||
pub use if_::If;
|
pub use if_::If;
|
||||||
pub use ignore::Ignore;
|
pub use ignore::Ignore;
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
|
pub use loop_::Loop;
|
||||||
pub use metadata::Metadata;
|
pub use metadata::Metadata;
|
||||||
pub use module::Module;
|
pub use module::Module;
|
||||||
|
pub use mut_::Mut;
|
||||||
pub use overlay::*;
|
pub use overlay::*;
|
||||||
|
pub use return_::Return;
|
||||||
|
pub use try_::Try;
|
||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
pub use version::Version;
|
pub use version::Version;
|
||||||
|
pub use while_::While;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
mod register;
|
mod register;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Module;
|
pub struct Module;
|
||||||
@ -16,12 +16,9 @@ impl Command for Module {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("module")
|
Signature::build("module")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.required("module_name", SyntaxShape::String, "module name")
|
.required("module_name", SyntaxShape::String, "module name")
|
||||||
.required(
|
.required("block", SyntaxShape::Block, "body of the module")
|
||||||
"block",
|
|
||||||
SyntaxShape::Block(Some(vec![])),
|
|
||||||
"body of the module",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
117
crates/nu-command/src/core_commands/mut_.rs
Normal file
117
crates/nu-command/src/core_commands/mut_.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use nu_engine::eval_expression_with_input;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Mut;
|
||||||
|
|
||||||
|
impl Command for Mut {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"mut"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create a mutable variable and give it a value."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("mut")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
|
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
|
||||||
|
.required(
|
||||||
|
"initial_value",
|
||||||
|
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
|
||||||
|
"equals sign followed by value",
|
||||||
|
)
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["set", "mutable"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let var_id = call
|
||||||
|
.positional_nth(0)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_var()
|
||||||
|
.expect("internal error: missing variable");
|
||||||
|
|
||||||
|
let keyword_expr = call
|
||||||
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_keyword()
|
||||||
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
|
let rhs = eval_expression_with_input(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
keyword_expr,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
)?
|
||||||
|
.0;
|
||||||
|
|
||||||
|
//println!("Adding: {:?} to {}", rhs, var_id);
|
||||||
|
|
||||||
|
stack.add_var(var_id, rhs.into_value(call.head));
|
||||||
|
Ok(PipelineData::new(call.head))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Set a mutable variable to a value, then update it",
|
||||||
|
example: "mut x = 10; $x = 12",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Set a mutable variable to the result of an expression",
|
||||||
|
example: "mut x = 10 + 100",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Set a mutable variable based on the condition",
|
||||||
|
example: "mut x = if false { -1 } else { 1 }",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Mut {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_command_type() {
|
||||||
|
assert!(matches!(Mut.command_type(), CommandType::Keyword));
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user