forked from extern/nushell
Compare commits
119 Commits
Author | SHA1 | Date | |
---|---|---|---|
a1b7261121 | |||
5a4b6f0f7f | |||
78b227d1ef | |||
07598c9620 | |||
7413ef2824 | |||
0cc735a7b2 | |||
d00038eb4b | |||
47af701380 | |||
24b4ac692e | |||
fb72da0e82 | |||
d339902dc6 | |||
393f424f1c | |||
c8f54476c9 | |||
d42c2b2dbc | |||
ed64a44b82 | |||
1855dfb656 | |||
91c01bf6b3 | |||
29256b161c | |||
32f098d91d | |||
06996d8c7f | |||
2306ef3063 | |||
f82a1d8e4e | |||
f0e0ab35fc | |||
3b20d6890c | |||
6eb00f6c60 | |||
4ecec59224 | |||
bf3bb66c3e | |||
6b3236715b | |||
cbedc8403f | |||
1d68c48a92 | |||
fff4de5c44 | |||
45d33e70db | |||
9b35d59023 | |||
71611dec4f | |||
a122e55129 | |||
6cedc05384 | |||
8efbb48cb0 | |||
0bfa769b7d | |||
5afc49250f | |||
efb81a1277 | |||
4a8124ad1e | |||
8ddebcb932 | |||
b2d7427d2d | |||
b4400c4896 | |||
5a8d4c628f | |||
ebdb7ac2d8 | |||
a33a7960bb | |||
3603610026 | |||
017151dff1 | |||
e892aad3f6 | |||
ad90b6e5f3 | |||
4da7bbbb59 | |||
b9808c8598 | |||
36036d9308 | |||
99c0a2575f | |||
487789b45b | |||
b3d6896977 | |||
8ee52b6ee1 | |||
46dba8853a | |||
eb4d19fb9c | |||
c3678764b4 | |||
9bb2c8faf5 | |||
74dcac3b0d | |||
6d51e34d0a | |||
de76c7a57d | |||
0e23400510 | |||
d0a83fec69 | |||
57510f2fd2 | |||
58b96fdede | |||
9e3d6c3bfd | |||
4a955d7e76 | |||
637283ffad | |||
5afbfb5c2c | |||
d128c0e02b | |||
60e6ea5abd | |||
415607706f | |||
f2b977b9c5 | |||
35e8420780 | |||
1c5846e1fb | |||
5ec6edb9c5 | |||
5d8bedfbe4 | |||
0477493734 | |||
e6b196c141 | |||
49960beb35 | |||
1b677f167e | |||
d881481758 | |||
a3ea0c304a | |||
4fda6d7eaa | |||
771e24913d | |||
aded2c1937 | |||
e54b867e8e | |||
c12b4b4af7 | |||
87ddba0193 | |||
a29b61bd4f | |||
7c6ea81dd4 | |||
d06ebb1686 | |||
d93953a56f | |||
74283c3ebc | |||
8a030f3bfc | |||
5518ffd248 | |||
bcdb9bf5b4 | |||
3509bde1a9 | |||
add20873d0 | |||
427db0d101 | |||
7bac0b417f | |||
56efbd7de9 | |||
91282d4404 | |||
398976e43e | |||
f723bc6989 | |||
22142bd4ae | |||
caf1432dc7 | |||
65c90d5b45 | |||
50ca77437d | |||
71b4949843 | |||
54a18991ab | |||
1fcb98289a | |||
01e5ba01f6 | |||
1134c2f16c | |||
d18cf19a3f |
5
.githooks/pre-commit
Executable file
5
.githooks/pre-commit
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
use ../toolkit.nu fmt
|
||||||
|
|
||||||
|
fmt --check --verbose
|
6
.githooks/pre-push
Executable file
6
.githooks/pre-push
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
use ../toolkit.nu [fmt, clippy]
|
||||||
|
|
||||||
|
fmt --check --verbose
|
||||||
|
clippy --verbose
|
11
.github/ISSUE_TEMPLATE/standard-library-bug-or-feature-report.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/standard-library-bug-or-feature-report.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: standard library bug or feature report
|
||||||
|
about: Used to submit issues related to the nu standard library
|
||||||
|
title: ''
|
||||||
|
labels: std-library
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug or feature**
|
||||||
|
A clear and concise description of what the bug is.
|
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
<!--
|
||||||
|
Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.
|
||||||
|
|
||||||
_(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_
|
Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.
|
||||||
|
-->
|
||||||
_(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
|
|
||||||
|
|
||||||
# User-Facing Changes
|
# User-Facing Changes
|
||||||
|
<!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. -->
|
||||||
_(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
|
|
||||||
|
|
||||||
# Tests + Formatting
|
# Tests + Formatting
|
||||||
|
<!--
|
||||||
Don't forget to add tests that cover your changes.
|
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:
|
||||||
@ -18,7 +18,7 @@ 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 -- -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` to check that all tests pass
|
- `cargo test --workspace` to check that all tests pass
|
||||||
- `cargo run -- crates/nu-utils/standard_library/tests.nu` to run the tests for the standard library
|
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> from `nushell` you can also use the `toolkit` as follows
|
> from `nushell` you can also use the `toolkit` as follows
|
||||||
@ -26,7 +26,7 @@ Make sure you've run and fixed any issues with these commands:
|
|||||||
> use toolkit.nu # or use an `env_change` hook to activate it automatically
|
> use toolkit.nu # or use an `env_change` hook to activate it automatically
|
||||||
> toolkit check pr
|
> toolkit check pr
|
||||||
> ```
|
> ```
|
||||||
|
-->
|
||||||
|
|
||||||
# After Submitting
|
# After Submitting
|
||||||
|
<!-- 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. -->
|
||||||
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.
|
|
||||||
|
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
@ -116,7 +116,7 @@ jobs:
|
|||||||
run: cargo install --path . --locked --no-default-features
|
run: cargo install --path . --locked --no-default-features
|
||||||
|
|
||||||
- name: Standard library tests
|
- name: Standard library tests
|
||||||
run: nu crates/nu-utils/standard_library/tests.nu
|
run: nu -c 'use std; std run-tests --path crates/nu-std'
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@ -169,40 +169,39 @@ jobs:
|
|||||||
run: cargo test --profile ci --package nu_plugin_*
|
run: cargo test --profile ci --package nu_plugin_*
|
||||||
|
|
||||||
|
|
||||||
# nu-coverage:
|
nu-coverage:
|
||||||
# needs: nu-tests
|
env:
|
||||||
# env:
|
NUSHELL_CARGO_TARGET: ci
|
||||||
# NUSHELL_CARGO_TARGET: ci
|
|
||||||
|
|
||||||
# strategy:
|
strategy:
|
||||||
# fail-fast: true
|
fail-fast: true
|
||||||
# matrix:
|
matrix:
|
||||||
# # disabled mac due to problems with merging coverage and similarity to linux
|
# disabled mac due to problems with merging coverage and similarity to linux
|
||||||
# # disabled windows due to running out of disk space when having too many crates or tests
|
# disabled windows due to running out of disk space when having too many crates or tests
|
||||||
# platform: [ubuntu-20.04] # windows-latest
|
platform: [ubuntu-20.04] # windows-latest
|
||||||
# rust:
|
rust:
|
||||||
# - stable
|
- stable
|
||||||
|
|
||||||
# runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
# steps:
|
steps:
|
||||||
# - uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
# - name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
# uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
|
||||||
# - name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
# uses: taiki-e/install-action@cargo-llvm-cov
|
uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
|
|
||||||
# - name: Tests
|
- name: Tests
|
||||||
# shell: bash
|
shell: bash
|
||||||
# run: |
|
run: |
|
||||||
# source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
||||||
# cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
||||||
# cargo build --workspace --profile ci
|
cargo build --workspace --profile ci
|
||||||
# cargo test --workspace --profile ci
|
cargo test --workspace --profile ci
|
||||||
# cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
||||||
|
|
||||||
# - name: Upload coverage reports to Codecov with GitHub Action
|
- name: Upload coverage reports to Codecov with GitHub Action
|
||||||
# uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
# with:
|
with:
|
||||||
# files: lcov.info
|
files: lcov.info
|
||||||
|
72
.github/workflows/release-pkg.nu
vendored
72
.github/workflows/release-pkg.nu
vendored
@ -50,17 +50,17 @@ 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 $'Debugging info:'
|
||||||
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
||||||
|
|
||||||
# $env
|
# $env
|
||||||
|
|
||||||
let USE_UBUNTU = 'ubuntu-20.04'
|
let USE_UBUNTU = 'ubuntu-20.04'
|
||||||
|
|
||||||
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
print $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
||||||
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
||||||
|
|
||||||
$'Start building ($bin)...'; hr-line
|
print $'Start building ($bin)...'; hr-line
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Ubuntu and macOS
|
# Build for Ubuntu and macOS
|
||||||
@ -70,23 +70,28 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt-get install libxcb-composite0-dev -y
|
sudo apt-get install libxcb-composite0-dev -y
|
||||||
}
|
}
|
||||||
if $target == 'aarch64-unknown-linux-gnu' {
|
match $target {
|
||||||
sudo apt-get install gcc-aarch64-linux-gnu -y
|
'aarch64-unknown-linux-gnu' => {
|
||||||
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
|
sudo apt-get install gcc-aarch64-linux-gnu -y
|
||||||
cargo-build-nu $flags
|
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
|
||||||
} else if $target == 'armv7-unknown-linux-gnueabihf' {
|
cargo-build-nu $flags
|
||||||
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
}
|
||||||
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
'riscv64gc-unknown-linux-gnu' => {
|
||||||
cargo-build-nu $flags
|
sudo apt-get install gcc-riscv64-linux-gnu -y
|
||||||
} else if $target == 'riscv64gc-unknown-linux-gnu' {
|
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
|
||||||
sudo apt-get install gcc-riscv64-linux-gnu -y
|
cargo-build-nu $flags
|
||||||
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
|
}
|
||||||
cargo-build-nu $flags
|
'armv7-unknown-linux-gnueabihf' => {
|
||||||
} else {
|
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
cargo-build-nu $flags
|
||||||
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
}
|
||||||
cargo-build-nu $flags
|
_ => {
|
||||||
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
|
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
||||||
|
cargo-build-nu $flags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,31 +112,34 @@ if $os in ['windows-latest'] {
|
|||||||
let suffix = if $os == 'windows-latest' { '.exe' }
|
let suffix = if $os == 'windows-latest' { '.exe' }
|
||||||
# nu, nu_plugin_* were all included
|
# nu, nu_plugin_* were all included
|
||||||
let executable = $'target/($target)/release/($bin)*($suffix)'
|
let executable = $'target/($target)/release/($bin)*($suffix)'
|
||||||
$'Current executable file: ($executable)'
|
print $'Current executable file: ($executable)'
|
||||||
|
|
||||||
cd $src; mkdir $dist;
|
cd $src; mkdir $dist;
|
||||||
rm -rf $'target/($target)/release/*.d' $'target/($target)/release/nu_pretty_hex*'
|
rm -rf $'target/($target)/release/*.d' $'target/($target)/release/nu_pretty_hex*'
|
||||||
$'(char nl)All executable files:'; hr-line
|
print $'(char nl)All executable files:'; hr-line
|
||||||
ls -f $executable
|
# We have to use `print` here to make sure the command output is displayed
|
||||||
|
print (ls -f $executable); sleep 1sec
|
||||||
|
|
||||||
$'(char nl)Copying release files...'; hr-line
|
print $'(char nl)Copying release files...'; hr-line
|
||||||
cp -v README.release.txt $'($dist)/README.txt'
|
cp -v README.release.txt $'($dist)/README.txt'
|
||||||
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
|
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
|
||||||
|
# Sleep a few seconds to make sure the cp process finished successfully
|
||||||
|
sleep 3sec
|
||||||
|
|
||||||
$'(char nl)Check binary release version detail:'; hr-line
|
print $'(char nl)Check binary release version detail:'; hr-line
|
||||||
let ver = if $os == 'windows-latest' {
|
let ver = if $os == 'windows-latest' {
|
||||||
(do -i { ./output/nu.exe -c 'version' }) | str join
|
(do -i { ./output/nu.exe -c 'version' }) | str join
|
||||||
} else {
|
} else {
|
||||||
(do -i { ./output/nu -c 'version' }) | str join
|
(do -i { ./output/nu -c 'version' }) | str join
|
||||||
}
|
}
|
||||||
if ($ver | str trim | is-empty) {
|
if ($ver | str trim | is-empty) {
|
||||||
$'(ansi r)Incompatible nu binary...(ansi reset)'
|
print $'(ansi r)Incompatible nu binary...(ansi reset)'
|
||||||
} else { $ver }
|
} else { print $ver }
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# 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; print $'(char nl)Creating release archive...'; hr-line
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
||||||
|
|
||||||
let files = (ls | get name)
|
let files = (ls | get name)
|
||||||
@ -141,7 +149,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
mkdir $dest
|
mkdir $dest
|
||||||
$files | each {|it| mv $it $dest } | ignore
|
$files | each {|it| mv $it $dest } | ignore
|
||||||
|
|
||||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
||||||
|
|
||||||
tar -czf $archive $dest
|
tar -czf $archive $dest
|
||||||
print $'archive: ---> ($archive)'; ls $archive
|
print $'archive: ---> ($archive)'; ls $archive
|
||||||
@ -152,7 +160,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
$'(char nl)Download less related stuffs...'; hr-line
|
print $'(char nl)Download less related stuffs...'; hr-line
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
||||||
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
|
||||||
|
|
||||||
@ -160,7 +168,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
if (get-env _EXTRA_) == 'msi' {
|
if (get-env _EXTRA_) == 'msi' {
|
||||||
|
|
||||||
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
|
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
|
||||||
$'(char nl)Start creating Windows msi package...'
|
print $'(char nl)Start creating Windows msi package...'
|
||||||
cd $src; hr-line
|
cd $src; hr-line
|
||||||
# Wix need the binaries be stored in target/release/
|
# Wix need the binaries be stored in target/release/
|
||||||
cp -r $'($dist)/*' target/release/
|
cp -r $'($dist)/*' target/release/
|
||||||
@ -171,7 +179,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
||||||
let archive = $'($dist)/($releaseStem).zip'
|
let archive = $'($dist)/($releaseStem).zip'
|
||||||
7z a $archive *
|
7z a $archive *
|
||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)';
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -75,7 +75,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.72.1
|
version: 0.78.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
@ -61,12 +61,22 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
```shell
|
```shell
|
||||||
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
```
|
```
|
||||||
|
or via the `toolkit.nu` command:
|
||||||
|
```shell
|
||||||
|
use toolkit.nu clippy
|
||||||
|
clippy
|
||||||
|
```
|
||||||
|
|
||||||
- Run all tests:
|
- Run all tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo test --workspace
|
cargo test --workspace
|
||||||
```
|
```
|
||||||
|
or via the `toolkit.nu` command:
|
||||||
|
```shell
|
||||||
|
use toolkit.nu test
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
- Run all tests for a specific command
|
- Run all tests for a specific command
|
||||||
|
|
||||||
@ -79,12 +89,30 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
```shell
|
```shell
|
||||||
cargo fmt --all -- --check
|
cargo fmt --all -- --check
|
||||||
```
|
```
|
||||||
|
or via the `toolkit.nu` command:
|
||||||
|
```shell
|
||||||
|
use toolkit.nu fmt
|
||||||
|
fmt --check
|
||||||
|
```
|
||||||
|
|
||||||
- Format the code in the project
|
- Format the code in the project
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo fmt --all
|
cargo fmt --all
|
||||||
```
|
```
|
||||||
|
or via the `toolkit.nu` command:
|
||||||
|
```shell
|
||||||
|
use toolkit.nu fmt
|
||||||
|
fmt
|
||||||
|
```
|
||||||
|
|
||||||
|
- Set up `git` hooks to check formatting and run `clippy` before committing and pushing:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
use toolkit.nu setup-git-hooks
|
||||||
|
setup-git-hooks
|
||||||
|
```
|
||||||
|
_Unfortunately, this hook isn't available on Windows._
|
||||||
|
|
||||||
### Debugging Tips
|
### Debugging Tips
|
||||||
|
|
||||||
|
874
Cargo.lock
generated
874
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
|||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.78.0"
|
version = "0.79.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -37,38 +37,41 @@ members = [
|
|||||||
"crates/nu_plugin_query",
|
"crates/nu_plugin_query",
|
||||||
"crates/nu_plugin_custom_values",
|
"crates/nu_plugin_custom_values",
|
||||||
"crates/nu_plugin_formats",
|
"crates/nu_plugin_formats",
|
||||||
|
"crates/nu-std",
|
||||||
"crates/nu-utils",
|
"crates/nu-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.23", features = ["serde"] }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.26"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.78.0" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.79.0" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.78.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.79.0" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.78.0" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.79.0" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.78.0" }
|
nu-command = { path = "./crates/nu-command", version = "0.79.0" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.78.0" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.79.0" }
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.78.0" }
|
nu-json = { path = "./crates/nu-json", version = "0.79.0" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.78.0" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.79.0" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.78.0" }
|
nu-path = { path = "./crates/nu-path", version = "0.79.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.78.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.79.0" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.78.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.79.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.78.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.79.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.78.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.79.0" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.78.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.79.0" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.78.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.79.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.78.0" }
|
nu-std = { path = "./crates/nu-std", version = "0.79.0" }
|
||||||
|
nu-utils = { path = "./crates/nu-utils", version = "0.79.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.19.0", features = ["bashisms", "sqlite"]}
|
||||||
|
|
||||||
rayon = "1.7.0"
|
rayon = "1.7.0"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
simplelog = "0.12.0"
|
simplelog = "0.12.0"
|
||||||
time = "0.3.12"
|
time = "0.3.12"
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
# Our dependencies don't use OpenSSL on Windows
|
# Our dependencies don't use OpenSSL on Windows
|
||||||
@ -89,8 +92,8 @@ nix = { version = "0.26", default-features = false, features = [
|
|||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.78.0" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.79.0" }
|
||||||
tempfile = "3.4.0"
|
tempfile = "3.5.0"
|
||||||
assert_cmd = "2.0.2"
|
assert_cmd = "2.0.2"
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
@ -157,7 +160,7 @@ bench = false
|
|||||||
# To use a development version of a dependency please use a global override here
|
# To use a development version of a dependency please use a global override here
|
||||||
# changing versions in each sub-crate of the workspace is tedious
|
# changing versions in each sub-crate of the workspace is tedious
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
|
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main"}
|
||||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||||
|
|
||||||
# Criterion benchmarking setup
|
# Criterion benchmarking setup
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
[](https://twitter.com/nu_shell)
|
[](https://twitter.com/nu_shell)
|
||||||
[](https://github.com/nushell/nushell/graphs/commit-activity)
|
[](https://github.com/nushell/nushell/graphs/commit-activity)
|
||||||
[](https://github.com/nushell/nushell/graphs/contributors)
|
[](https://github.com/nushell/nushell/graphs/contributors)
|
||||||
<!-- [](https://codecov.io/gh/nushell/nushell) -->
|
[](https://codecov.io/gh/nushell/nushell)
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||
@ -46,6 +46,8 @@ To quickly install Nu:
|
|||||||
brew install nushell
|
brew install nushell
|
||||||
# Windows
|
# Windows
|
||||||
winget install nushell
|
winget install nushell
|
||||||
|
# Cross Platform installation if you have node and npm installed, Note that nu plugins were not included
|
||||||
|
npm install -g nushell
|
||||||
```
|
```
|
||||||
|
|
||||||
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
||||||
@ -222,6 +224,7 @@ Please submit an issue or PR to be added to this list.
|
|||||||
- [oh-my-posh](https://ohmyposh.dev)
|
- [oh-my-posh](https://ohmyposh.dev)
|
||||||
- [Couchbase Shell](https://couchbase.sh)
|
- [Couchbase Shell](https://couchbase.sh)
|
||||||
- [virtualenv](https://github.com/pypa/virtualenv)
|
- [virtualenv](https://github.com/pypa/virtualenv)
|
||||||
|
- [atuin](https://github.com/ellie/atuin)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ fn parser_benchmarks(c: &mut Criterion) {
|
|||||||
c.bench_function("parse_default_env_file", |b| {
|
c.bench_function("parse_default_env_file", |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
||||||
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
|
|mut working_set| parse(&mut working_set, None, default_env, false),
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -31,7 +31,7 @@ fn parser_benchmarks(c: &mut Criterion) {
|
|||||||
c.bench_function("parse_default_config_file", |b| {
|
c.bench_function("parse_default_config_file", |b| {
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
||||||
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
|
|mut working_set| parse(&mut working_set, None, default_config, false),
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -5,39 +5,40 @@ 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.78.0"
|
version = "0.79.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.78.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.79.0" }
|
||||||
rstest = { version = "0.17.0", default-features = false }
|
rstest = { version = "0.17.0", default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-command = { path = "../nu-command", version = "0.78.0" }
|
nu-command = { path = "../nu-command", version = "0.79.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
nu-engine = { path = "../nu-engine", version = "0.79.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.78.0" }
|
nu-path = { path = "../nu-path", version = "0.79.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
nu-parser = { path = "../nu-parser", version = "0.79.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
nu-utils = { path = "../nu-utils", version = "0.79.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.79.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
reedline = { version = "0.19.0", features = ["bashisms", "sqlite"]}
|
||||||
|
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.26"
|
||||||
fancy-regex = "0.11.0"
|
fancy-regex = "0.11.0"
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
once_cell = "1.17.0"
|
once_cell = "1.17.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
sysinfo = "0.28.2"
|
sysinfo = "0.28.2"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
unicode-segmentation = "1.10.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
33
crates/nu-cli/src/commands/default_context.rs
Normal file
33
crates/nu-cli/src/commands/default_context.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
|
|
||||||
|
use crate::commands::*;
|
||||||
|
|
||||||
|
pub fn add_cli_context(mut engine_state: EngineState) -> EngineState {
|
||||||
|
let delta = {
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
macro_rules! bind_command {
|
||||||
|
( $( $command:expr ),* $(,)? ) => {
|
||||||
|
$( working_set.add_decl(Box::new($command)); )*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bind_command! {
|
||||||
|
Commandline,
|
||||||
|
History,
|
||||||
|
HistorySession,
|
||||||
|
Keybindings,
|
||||||
|
KeybindingsDefault,
|
||||||
|
KeybindingsList,
|
||||||
|
KeybindingsListen,
|
||||||
|
};
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = engine_state.merge_delta(delta) {
|
||||||
|
eprintln!("Error creating default context: {err:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
}
|
@ -91,7 +91,7 @@ impl Command for History {
|
|||||||
match engine_state.config.history_file_format {
|
match engine_state.config.history_file_format {
|
||||||
HistoryFileFormat::PlainText => Ok(history_reader
|
HistoryFileFormat::PlainText => Ok(history_reader
|
||||||
.and_then(|h| {
|
.and_then(|h| {
|
||||||
h.search(SearchQuery::everything(SearchDirection::Forward))
|
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
.map(move |entries| {
|
.map(move |entries| {
|
||||||
@ -114,7 +114,7 @@ impl Command for History {
|
|||||||
.into_pipeline_data(ctrlc)),
|
.into_pipeline_data(ctrlc)),
|
||||||
HistoryFileFormat::Sqlite => Ok(history_reader
|
HistoryFileFormat::Sqlite => Ok(history_reader
|
||||||
.and_then(|h| {
|
.and_then(|h| {
|
||||||
h.search(SearchQuery::everything(SearchDirection::Forward))
|
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
.map(move |entries| {
|
.map(move |entries| {
|
@ -102,7 +102,13 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
// are printed, it's a good chance your terminal is eating
|
// are printed, it's a good chance your terminal is eating
|
||||||
// those events.
|
// those events.
|
||||||
fn print_events_helper(event: Event) -> Result<Value, ShellError> {
|
fn print_events_helper(event: Event) -> Result<Value, ShellError> {
|
||||||
if let Event::Key(KeyEvent { code, modifiers }) = event {
|
if let Event::Key(KeyEvent {
|
||||||
|
code,
|
||||||
|
modifiers,
|
||||||
|
kind,
|
||||||
|
state,
|
||||||
|
}) = event
|
||||||
|
{
|
||||||
match code {
|
match code {
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
let record = Value::Record {
|
let record = Value::Record {
|
||||||
@ -111,12 +117,16 @@ fn print_events_helper(event: Event) -> Result<Value, ShellError> {
|
|||||||
"code".into(),
|
"code".into(),
|
||||||
"modifier".into(),
|
"modifier".into(),
|
||||||
"flags".into(),
|
"flags".into(),
|
||||||
|
"kind".into(),
|
||||||
|
"state".into(),
|
||||||
],
|
],
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::string(format!("{c}"), Span::unknown()),
|
Value::string(format!("{c}"), Span::unknown()),
|
||||||
Value::string(format!("{:#08x}", u32::from(c)), Span::unknown()),
|
Value::string(format!("{:#08x}", u32::from(c)), Span::unknown()),
|
||||||
Value::string(format!("{modifiers:?}"), Span::unknown()),
|
Value::string(format!("{modifiers:?}"), Span::unknown()),
|
||||||
Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
Value::string(format!("{modifiers:#08b}"), Span::unknown()),
|
||||||
|
Value::string(format!("{kind:?}"), Span::unknown()),
|
||||||
|
Value::string(format!("{state:?}"), Span::unknown()),
|
||||||
],
|
],
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
@ -144,14 +154,3 @@ fn print_events_helper(event: Event) -> Result<Value, ShellError> {
|
|||||||
Ok(record)
|
Ok(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::KeybindingsListen;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() {
|
|
||||||
use crate::test_examples;
|
|
||||||
test_examples(KeybindingsListen {})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,18 @@
|
|||||||
|
mod commandline;
|
||||||
|
mod default_context;
|
||||||
|
mod history;
|
||||||
|
mod history_session;
|
||||||
mod keybindings;
|
mod keybindings;
|
||||||
mod keybindings_default;
|
mod keybindings_default;
|
||||||
mod keybindings_list;
|
mod keybindings_list;
|
||||||
mod keybindings_listen;
|
mod keybindings_listen;
|
||||||
|
|
||||||
|
pub use commandline::Commandline;
|
||||||
|
pub use history::History;
|
||||||
|
pub use history_session::HistorySession;
|
||||||
pub use keybindings::Keybindings;
|
pub use keybindings::Keybindings;
|
||||||
pub use keybindings_default::KeybindingsDefault;
|
pub use keybindings_default::KeybindingsDefault;
|
||||||
pub use keybindings_list::KeybindingsList;
|
pub use keybindings_list::KeybindingsList;
|
||||||
pub use keybindings_listen::KeybindingsListen;
|
pub use keybindings_listen::KeybindingsListen;
|
||||||
|
|
||||||
|
pub use default_context::add_cli_context;
|
@ -88,7 +88,7 @@ impl CommandCompletion {
|
|||||||
|
|
||||||
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
|
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
|
||||||
|
|
||||||
let results = working_set
|
let mut results = working_set
|
||||||
.find_commands_by_predicate(filter_predicate)
|
.find_commands_by_predicate(filter_predicate)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| Suggestion {
|
.map(move |x| Suggestion {
|
||||||
@ -97,20 +97,8 @@ impl CommandCompletion {
|
|||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
});
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let results_aliases = working_set
|
|
||||||
.find_aliases_by_predicate(filter_predicate)
|
|
||||||
.into_iter()
|
|
||||||
.map(move |x| Suggestion {
|
|
||||||
value: String::from_utf8_lossy(&x).to_string(),
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
|
||||||
append_whitespace: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let partial = working_set.get_span_contents(span);
|
let partial = working_set.get_span_contents(span);
|
||||||
let partial = String::from_utf8_lossy(partial).to_string();
|
let partial = String::from_utf8_lossy(partial).to_string();
|
||||||
@ -169,7 +157,7 @@ impl Completer for CommandCompletion {
|
|||||||
.take_while(|x| {
|
.take_while(|x| {
|
||||||
matches!(
|
matches!(
|
||||||
x.1,
|
x.1,
|
||||||
FlatShape::InternalCall
|
FlatShape::InternalCall(_)
|
||||||
| FlatShape::External
|
| FlatShape::External
|
||||||
| FlatShape::ExternalArg
|
| FlatShape::ExternalArg
|
||||||
| FlatShape::Literal
|
| FlatShape::Literal
|
||||||
@ -197,7 +185,7 @@ impl Completer for CommandCompletion {
|
|||||||
|
|
||||||
let config = working_set.get_config();
|
let config = working_set.get_config();
|
||||||
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
||||||
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall)
|
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
|
||||||
|| ((span.end - span.start) == 0)
|
|| ((span.end - span.start) == 0)
|
||||||
|| is_passthrough_command(working_set.delta.get_file_contents())
|
|| is_passthrough_command(working_set.delta.get_file_contents())
|
||||||
{
|
{
|
||||||
@ -311,7 +299,7 @@ mod command_completions_tests {
|
|||||||
|
|
||||||
let delta = {
|
let delta = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
working_set.add_file("child.nu".into(), input);
|
let _ = working_set.add_file("child.nu".into(), input);
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,14 +113,13 @@ impl NuCompleter {
|
|||||||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
|
||||||
let initial_line = line.to_string();
|
let initial_line = line.to_string();
|
||||||
let alias_total_offset: usize = alias_offset.iter().sum();
|
let mut line = line.to_string();
|
||||||
new_line.insert(alias_total_offset + pos, b'a');
|
line.insert(pos, 'a');
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let config = self.engine_state.get_config();
|
let config = self.engine_state.get_config();
|
||||||
|
|
||||||
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
let output = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
||||||
|
|
||||||
for pipeline in output.pipelines.into_iter() {
|
for pipeline in output.pipelines.into_iter() {
|
||||||
for pipeline_element in pipeline.elements {
|
for pipeline_element in pipeline.elements {
|
||||||
@ -131,7 +130,6 @@ impl NuCompleter {
|
|||||||
| PipelineElement::Or(_, expr)
|
| PipelineElement::Or(_, expr)
|
||||||
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
let span_offset: usize = alias_offset.iter().sum();
|
|
||||||
let mut spans: Vec<String> = vec![];
|
let mut spans: Vec<String> = vec![];
|
||||||
|
|
||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
@ -154,24 +152,17 @@ impl NuCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Complete based on the last span
|
// Complete based on the last span
|
||||||
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
if pos >= flat.0.start && pos < flat.0.end {
|
||||||
// Context variables
|
// Context variables
|
||||||
let most_left_var =
|
let most_left_var =
|
||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
|
|
||||||
// Create a new span
|
// Create a new span
|
||||||
let new_span = if flat_idx == 0 {
|
let new_span = Span::new(flat.0.start, flat.0.end - 1);
|
||||||
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
|
|
||||||
} else {
|
|
||||||
Span::new(
|
|
||||||
flat.0.start - span_offset,
|
|
||||||
flat.0.end - 1 - span_offset,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
// 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 mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
let index = pos - (flat.0.start - span_offset);
|
let index = pos - flat.0.start;
|
||||||
prefix.drain(index..);
|
prefix.drain(index..);
|
||||||
|
|
||||||
// Variables completion
|
// Variables completion
|
||||||
@ -391,85 +382,6 @@ impl ReedlineCompleter for NuCompleter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchedAlias = Vec<(Vec<u8>, Vec<u8>)>;
|
|
||||||
|
|
||||||
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
|
|
||||||
// that `g` is an alias of `git`
|
|
||||||
fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<usize>) {
|
|
||||||
// An vector represents the offsets of alias
|
|
||||||
// e.g: the offset is 2 for the alias `g` of `git`
|
|
||||||
let mut alias_offset = vec![];
|
|
||||||
let mut output = vec![];
|
|
||||||
if let Some(matched_alias) = search_alias(line, working_set) {
|
|
||||||
let mut lens = matched_alias.len();
|
|
||||||
for (input_vec, line_vec) in matched_alias {
|
|
||||||
alias_offset.push(line_vec.len() - input_vec.len());
|
|
||||||
output.extend(line_vec);
|
|
||||||
if lens > 1 {
|
|
||||||
output.push(b' ');
|
|
||||||
lens -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !line.is_empty() {
|
|
||||||
let last = line.last().expect("input is empty");
|
|
||||||
if last == &b' ' {
|
|
||||||
output.push(b' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output = line.to_vec();
|
|
||||||
}
|
|
||||||
|
|
||||||
(output, alias_offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAlias> {
|
|
||||||
let mut vec_names = vec![];
|
|
||||||
let mut vec_alias = vec![];
|
|
||||||
let mut pos = 0;
|
|
||||||
let mut is_alias = false;
|
|
||||||
for (index, character) in input.iter().enumerate() {
|
|
||||||
if *character == b' ' {
|
|
||||||
let range = &input[pos..index];
|
|
||||||
vec_names.push(range.to_owned());
|
|
||||||
pos = index + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Push the rest to names vector.
|
|
||||||
if pos < input.len() {
|
|
||||||
vec_names.push(input[pos..].to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
for name in &vec_names {
|
|
||||||
if let Some(alias_id) = working_set.find_alias(&name[..]) {
|
|
||||||
let alias_span = working_set.get_alias(alias_id);
|
|
||||||
let mut span_vec = vec![];
|
|
||||||
is_alias = true;
|
|
||||||
for alias in alias_span {
|
|
||||||
let name = working_set.get_span_contents(*alias);
|
|
||||||
if !name.is_empty() {
|
|
||||||
span_vec.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Join span of vector together for complex alias, e.g: `f` is an alias for `git remote -v`
|
|
||||||
let full_aliases = span_vec.join(&[b' '][..]);
|
|
||||||
vec_alias.push(full_aliases);
|
|
||||||
} else {
|
|
||||||
vec_alias.push(name.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_alias {
|
|
||||||
// Zip names and alias vectors, the original inputs and its aliases mapping.
|
|
||||||
// e.g:(['g'], ['g','i','t'])
|
|
||||||
let output = vec_names.into_iter().zip(vec_alias).collect();
|
|
||||||
Some(output)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reads the most left variable returning it's name (e.g: $myvar)
|
// reads the most left variable returning it's name (e.g: $myvar)
|
||||||
// and the depth (a.b.c)
|
// and the depth (a.b.c)
|
||||||
fn most_left_variable(
|
fn most_left_variable(
|
||||||
@ -490,7 +402,7 @@ fn most_left_variable(
|
|||||||
let result = working_set.get_span_contents(item.0).to_vec();
|
let result = working_set.get_span_contents(item.0).to_vec();
|
||||||
|
|
||||||
match item.1 {
|
match item.1 {
|
||||||
FlatShape::Variable => {
|
FlatShape::Variable(_) => {
|
||||||
variables_found.push(result);
|
variables_found.push(result);
|
||||||
found_var = true;
|
found_var = true;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
|||||||
PipelineData, Span, Type, Value,
|
PipelineData, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::sync::Arc;
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use super::completer::map_value_completions;
|
use super::completer::map_value_completions;
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ impl Completer for CustomCompletion {
|
|||||||
],
|
],
|
||||||
redirect_stdout: true,
|
redirect_stdout: true,
|
||||||
redirect_stderr: true,
|
redirect_stderr: true,
|
||||||
parser_info: vec![],
|
parser_info: HashMap::new(),
|
||||||
},
|
},
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
);
|
);
|
||||||
|
@ -179,11 +179,7 @@ impl Completer for VariableCompletion {
|
|||||||
let mut removed_overlays = vec![];
|
let mut removed_overlays = vec![];
|
||||||
// Working set scope vars
|
// Working set scope vars
|
||||||
for scope_frame in working_set.delta.scope.iter().rev() {
|
for scope_frame in working_set.delta.scope.iter().rev() {
|
||||||
for overlay_frame in scope_frame
|
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
|
||||||
.active_overlays(&mut removed_overlays)
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
{
|
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8_insensitive(
|
||||||
options.case_sensitive,
|
options.case_sensitive,
|
||||||
@ -204,12 +200,7 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
// Permanent state vars
|
// Permanent state vars
|
||||||
// for scope in &self.engine_state.scope {
|
// for scope in &self.engine_state.scope {
|
||||||
for overlay_frame in self
|
for overlay_frame in self.engine_state.active_overlays(&removed_overlays).rev() {
|
||||||
.engine_state
|
|
||||||
.active_overlays(&removed_overlays)
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
{
|
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8_insensitive(
|
||||||
options.case_sensitive,
|
options.case_sensitive,
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
use crate::util::eval_source;
|
use crate::util::eval_source;
|
||||||
use nu_command::util::report_error;
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
use nu_parser::ParseError;
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||||
#[cfg(feature = "plugin")]
|
use nu_protocol::report_error;
|
||||||
use nu_protocol::Spanned;
|
|
||||||
use nu_protocol::{HistoryFileFormat, PipelineData};
|
use nu_protocol::{HistoryFileFormat, PipelineData};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
use nu_protocol::{ParseError, Spanned};
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
use miette::Result;
|
use miette::Result;
|
||||||
use nu_command::util::report_error;
|
|
||||||
use nu_engine::{convert_env_values, eval_block};
|
use nu_engine::{convert_env_values, eval_block};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::Stack;
|
use nu_protocol::engine::Stack;
|
||||||
|
use nu_protocol::report_error;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
PipelineData, Spanned, Value,
|
PipelineData, Spanned, Value,
|
||||||
@ -34,9 +34,9 @@ pub fn evaluate_commands(
|
|||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let (output, err) = parse(&mut working_set, None, commands.item.as_bytes(), false, &[]);
|
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
|
||||||
if let Some(err) = err {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, err);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
@ -2,10 +2,10 @@ use crate::util::eval_source;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_command::util::report_error;
|
|
||||||
use nu_engine::{convert_env_values, current_dir};
|
use nu_engine::{convert_env_values, current_dir};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
|
use nu_protocol::report_error;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
@ -93,10 +93,14 @@ pub fn evaluate_file(
|
|||||||
"FILE_PWD".to_string(),
|
"FILE_PWD".to_string(),
|
||||||
Value::string(parent.to_string_lossy(), Span::unknown()),
|
Value::string(parent.to_string_lossy(), Span::unknown()),
|
||||||
);
|
);
|
||||||
|
stack.add_env_var(
|
||||||
|
"CURRENT_FILE".to_string(),
|
||||||
|
Value::string(file_path.to_string_lossy(), Span::unknown()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
trace!("parsing file: {}", file_path_str);
|
trace!("parsing file: {}", file_path_str);
|
||||||
let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]);
|
let _ = parse(&mut working_set, Some(file_path_str), &file, false);
|
||||||
|
|
||||||
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
let args = format!("main {}", args.join(" "));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod completions;
|
mod completions;
|
||||||
mod config_files;
|
mod config_files;
|
||||||
|
mod eval_cmds;
|
||||||
mod eval_file;
|
mod eval_file;
|
||||||
mod menus;
|
mod menus;
|
||||||
mod nu_highlight;
|
mod nu_highlight;
|
||||||
@ -13,12 +14,13 @@ mod syntax_highlight;
|
|||||||
mod util;
|
mod util;
|
||||||
mod validation;
|
mod validation;
|
||||||
|
|
||||||
pub use commands::evaluate_commands;
|
pub use commands::add_cli_context;
|
||||||
pub use completions::{FileCompletion, NuCompleter};
|
pub use completions::{FileCompletion, NuCompleter};
|
||||||
pub use config_files::eval_config_contents;
|
pub use config_files::eval_config_contents;
|
||||||
|
pub use eval_cmds::evaluate_commands;
|
||||||
pub use eval_file::evaluate_file;
|
pub use eval_file::evaluate_file;
|
||||||
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
pub use menus::{DescriptionMenu, NuHelpCompleter};
|
||||||
pub use nu_command::util::{get_init_cwd, report_error, report_error_new};
|
pub use nu_command::util::get_init_cwd;
|
||||||
pub use nu_highlight::NuHighlight;
|
pub use nu_highlight::NuHighlight;
|
||||||
pub use print::Print;
|
pub use print::Print;
|
||||||
pub use prompt::NushellPrompt;
|
pub use prompt::NushellPrompt;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::NushellPrompt;
|
use crate::NushellPrompt;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_command::util::report_error;
|
|
||||||
use nu_engine::eval_subexpression;
|
use nu_engine::eval_subexpression;
|
||||||
|
use nu_protocol::report_error;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, PipelineData, Value,
|
Config, PipelineData, Value,
|
||||||
|
@ -97,12 +97,11 @@ pub(crate) fn add_menus(
|
|||||||
{
|
{
|
||||||
let (block, _) = {
|
let (block, _) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let (output, _) = parse(
|
let output = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some(name), // format!("entry #{}", entry_num)
|
Some(name), // format!("entry #{}", entry_num)
|
||||||
definition.as_bytes(),
|
definition.as_bytes(),
|
||||||
true,
|
true,
|
||||||
&[],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
|
@ -5,18 +5,19 @@ use crate::{
|
|||||||
util::eval_source,
|
util::eval_source,
|
||||||
NuHighlighter, NuValidator, NushellPrompt,
|
NuHighlighter, NuValidator, NushellPrompt,
|
||||||
};
|
};
|
||||||
use crossterm::cursor::CursorShape;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_command::hook::eval_hook;
|
use nu_command::hook::eval_hook;
|
||||||
use nu_command::util::{get_guaranteed_cwd, report_error, report_error_new};
|
use nu_command::util::get_guaranteed_cwd;
|
||||||
use nu_engine::{convert_env_values, eval_block};
|
use nu_engine::{convert_env_values, eval_block};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
config::NuCursorShape,
|
config::NuCursorShape,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
format_duration, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
format_duration, report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError,
|
||||||
|
Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
||||||
@ -706,11 +707,11 @@ pub fn evaluate_repl(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> CursorShape {
|
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
|
||||||
match shape {
|
match shape {
|
||||||
NuCursorShape::Block => CursorShape::Block,
|
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
|
||||||
NuCursorShape::UnderScore => CursorShape::UnderScore,
|
NuCursorShape::UnderScore => SetCursorStyle::DefaultUserShape,
|
||||||
NuCursorShape::Line => CursorShape::Line,
|
NuCursorShape::Line => SetCursorStyle::BlinkingBar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,7 +790,7 @@ pub fn eval_string_with_input(
|
|||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let (output, _) = parse(&mut working_set, None, source.as_bytes(), false, &[]);
|
let output = parse(&mut working_set, None, source.as_bytes(), false);
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
@ -18,10 +18,7 @@ impl Highlighter for NuHighlighter {
|
|||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
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, 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())
|
||||||
@ -91,9 +88,10 @@ impl Highlighter for NuHighlighter {
|
|||||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::InternalCall => add_colored_token(&shape.1, next_token),
|
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||||
@ -113,11 +111,16 @@ impl Highlighter for NuHighlighter {
|
|||||||
FlatShape::Block => {
|
FlatShape::Block => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
}
|
}
|
||||||
|
FlatShape::Closure => {
|
||||||
|
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||||
|
}
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Variable => add_colored_token(&shape.1, next_token),
|
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||||
|
add_colored_token(&shape.1, next_token)
|
||||||
|
}
|
||||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::And => add_colored_token(&shape.1, next_token),
|
FlatShape::And => add_colored_token(&shape.1, next_token),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_command::hook::eval_hook;
|
use nu_command::hook::eval_hook;
|
||||||
use nu_command::util::{report_error, report_error_new};
|
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||||
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;
|
||||||
@ -7,6 +6,7 @@ use nu_protocol::{
|
|||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
|
use nu_protocol::{report_error, report_error_new};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
@ -113,7 +113,8 @@ fn gather_env_vars(
|
|||||||
span,
|
span,
|
||||||
}) = parts.get(0)
|
}) = parts.get(0)
|
||||||
{
|
{
|
||||||
let bytes = engine_state.get_span_contents(span);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
let bytes = working_set.get_span_contents(*span);
|
||||||
|
|
||||||
if bytes.len() < 2 {
|
if bytes.len() < 2 {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
@ -125,9 +126,12 @@ fn gather_env_vars(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
|
let (bytes, err) = unescape_unquote_string(bytes, *span);
|
||||||
|
if let Some(err) = err {
|
||||||
|
working_set.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
if parse_error.is_some() {
|
if working_set.parse_errors.first().is_some() {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&String::from_utf8_lossy(contents),
|
&String::from_utf8_lossy(contents),
|
||||||
@ -153,7 +157,8 @@ fn gather_env_vars(
|
|||||||
span,
|
span,
|
||||||
}) = parts.get(2)
|
}) = parts.get(2)
|
||||||
{
|
{
|
||||||
let bytes = engine_state.get_span_contents(span);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
let bytes = working_set.get_span_contents(*span);
|
||||||
|
|
||||||
if bytes.len() < 2 {
|
if bytes.len() < 2 {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
@ -165,9 +170,12 @@ fn gather_env_vars(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
|
let (bytes, err) = unescape_unquote_string(bytes, *span);
|
||||||
|
if let Some(err) = err {
|
||||||
|
working_set.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
if parse_error.is_some() {
|
if working_set.parse_errors.first().is_some() {
|
||||||
report_capture_error(
|
report_capture_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&String::from_utf8_lossy(contents),
|
&String::from_utf8_lossy(contents),
|
||||||
@ -209,16 +217,15 @@ pub fn 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 = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some(fname), // format!("entry #{}", entry_num)
|
Some(fname), // format!("entry #{}", entry_num)
|
||||||
source,
|
source,
|
||||||
false,
|
false,
|
||||||
&[],
|
|
||||||
);
|
);
|
||||||
if let Some(err) = err {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
set_last_exit_code(stack, 1);
|
set_last_exit_code(stack, 1);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use nu_parser::{parse, ParseError};
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::{
|
||||||
|
engine::{EngineState, StateWorkingSet},
|
||||||
|
ParseError,
|
||||||
|
};
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -10,9 +13,12 @@ pub struct NuValidator {
|
|||||||
impl Validator for NuValidator {
|
impl Validator for NuValidator {
|
||||||
fn validate(&self, line: &str) -> ValidationResult {
|
fn validate(&self, line: &str) -> ValidationResult {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let (_, err) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
|
|
||||||
if matches!(err, Some(ParseError::UnexpectedEof(..))) {
|
if matches!(
|
||||||
|
working_set.parse_errors.first(),
|
||||||
|
Some(ParseError::UnexpectedEof(..))
|
||||||
|
) {
|
||||||
ValidationResult::Incomplete
|
ValidationResult::Incomplete
|
||||||
} else {
|
} else {
|
||||||
ValidationResult::Complete
|
ValidationResult::Complete
|
||||||
|
@ -524,10 +524,12 @@ fn variables_completions() {
|
|||||||
// Test completions for $nu
|
// Test completions for $nu
|
||||||
let suggestions = completer.complete("$nu.", 4);
|
let suggestions = completer.complete("$nu.", 4);
|
||||||
|
|
||||||
assert_eq!(12, suggestions.len());
|
assert_eq!(14, suggestions.len());
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
let expected: Vec<String> = vec![
|
||||||
"config-path".into(),
|
"config-path".into(),
|
||||||
|
"current-exe".into(),
|
||||||
|
"default-config-dir".into(),
|
||||||
"env-path".into(),
|
"env-path".into(),
|
||||||
"history-path".into(),
|
"history-path".into(),
|
||||||
"home-path".into(),
|
"home-path".into(),
|
||||||
@ -663,8 +665,8 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
|||||||
let (dir, _, mut engine_state, mut stack) = new_engine();
|
let (dir, _, mut engine_state, mut stack) = new_engine();
|
||||||
let (_, delta) = {
|
let (_, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]);
|
let block = parse(&mut working_set, None, block.as_bytes(), false);
|
||||||
assert!(err.is_none());
|
assert!(working_set.parse_errors.is_empty());
|
||||||
|
|
||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
|
@ -146,9 +146,9 @@ pub fn merge_input(
|
|||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let (block, err) = parse(&mut working_set, None, input, false, &[]);
|
let block = parse(&mut working_set, None, input, false);
|
||||||
|
|
||||||
assert!(err.is_none());
|
assert!(working_set.parse_errors.is_empty());
|
||||||
|
|
||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
|
@ -6,17 +6,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.78.0"
|
version = "0.79.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.79.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
nu-engine = { path = "../nu-engine", version = "0.79.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
nu-parser = { path = "../nu-parser", version = "0.79.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
nu-utils = { path = "../nu-utils", version = "0.79.0" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
|
|
||||||
@ -24,10 +24,9 @@ fancy-regex = "0.11.0"
|
|||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
shadow-rs = { version = "0.21.0", default-features = false }
|
shadow-rs = { version = "0.21.0", default-features = false }
|
||||||
unicode-segmentation = "1.10.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.21.0", default-features = false }
|
shadow-rs = { version = "0.21.0", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.78.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.79.0" }
|
||||||
|
@ -20,15 +20,6 @@ impl Command for Break {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
@ -20,15 +20,6 @@ impl Command for Continue {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
@ -120,9 +120,11 @@ impl Command for For {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
return Ok(PipelineData::new_external_stream_with_only_exit_code(
|
||||||
|
exit_code,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +166,9 @@ impl Command for For {
|
|||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.drain_with_exit_code()?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
return Ok(PipelineData::new_external_stream_with_only_exit_code(
|
||||||
|
exit_code,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ use nu_protocol::{
|
|||||||
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HelpAliases;
|
pub struct HelpAliases;
|
||||||
@ -112,22 +111,22 @@ pub fn help_aliases(
|
|||||||
name.push_str(&r.item);
|
name.push_str(&r.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
let alias_id = if let Some(id) = engine_state.find_alias(name.as_bytes(), &[]) {
|
let Some(alias) = engine_state.find_decl(name.as_bytes(), &[]) else {
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::AliasNotFound(span(
|
return Err(ShellError::AliasNotFound(span(
|
||||||
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
|
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
let alias_expansion = engine_state
|
let Some(alias) = engine_state.get_decl(alias).as_alias() else {
|
||||||
.get_alias(alias_id)
|
return Err(ShellError::AliasNotFound(span(
|
||||||
.iter()
|
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
|
||||||
.map(|span| String::from_utf8_lossy(engine_state.get_span_contents(span)))
|
)));
|
||||||
.collect::<Vec<Cow<str>>>()
|
};
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
let alias_usage = engine_state.build_alias_usage(alias_id);
|
let alias_expansion =
|
||||||
|
String::from_utf8_lossy(engine_state.get_span_contents(&alias.wrapped_call.span));
|
||||||
|
let usage = alias.usage();
|
||||||
|
let extra_usage = alias.extra_usage();
|
||||||
|
|
||||||
// TODO: merge this into documentation.rs at some point
|
// TODO: merge this into documentation.rs at some point
|
||||||
const G: &str = "\x1b[32m"; // green
|
const G: &str = "\x1b[32m"; // green
|
||||||
@ -136,14 +135,12 @@ pub fn help_aliases(
|
|||||||
|
|
||||||
let mut long_desc = String::new();
|
let mut long_desc = String::new();
|
||||||
|
|
||||||
if let Some((usage, extra_usage)) = alias_usage {
|
long_desc.push_str(usage);
|
||||||
long_desc.push_str(&usage);
|
long_desc.push_str("\n\n");
|
||||||
long_desc.push_str("\n\n");
|
|
||||||
|
|
||||||
if !extra_usage.is_empty() {
|
if !extra_usage.is_empty() {
|
||||||
long_desc.push_str(&extra_usage);
|
long_desc.push_str(extra_usage);
|
||||||
long_desc.push_str("\n\n");
|
long_desc.push_str("\n\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long_desc.push_str(&format!("{G}Alias{RESET}: {C}{name}{RESET}"));
|
long_desc.push_str(&format!("{G}Alias{RESET}: {C}{name}{RESET}"));
|
||||||
@ -165,7 +162,7 @@ pub fn help_aliases(
|
|||||||
|
|
||||||
fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
|
fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
|
||||||
let mut scope_data = ScopeData::new(engine_state, stack);
|
let mut scope_data = ScopeData::new(engine_state, stack);
|
||||||
scope_data.populate_aliases();
|
scope_data.populate_all();
|
||||||
|
|
||||||
scope_data.collect_aliases(span)
|
scope_data.collect_aliases(span)
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use nu_engine::{scope::ScopeData, CallExt};
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
span, AliasId, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData,
|
span, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||||
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -117,9 +117,7 @@ pub fn help_modules(
|
|||||||
name.push_str(&r.item);
|
name.push_str(&r.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_id = if let Some(id) = engine_state.find_module(name.as_bytes(), &[]) {
|
let Some(module_id) = engine_state.find_module(name.as_bytes(), &[]) else {
|
||||||
id
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::ModuleNotFoundAtRuntime {
|
return Err(ShellError::ModuleNotFoundAtRuntime {
|
||||||
mod_name: name,
|
mod_name: name,
|
||||||
span: span(&rest.iter().map(|r| r.span).collect::<Vec<Span>>()),
|
span: span(&rest.iter().map(|r| r.span).collect::<Vec<Span>>()),
|
||||||
@ -152,9 +150,16 @@ pub fn help_modules(
|
|||||||
long_desc.push_str("\n\n");
|
long_desc.push_str("\n\n");
|
||||||
|
|
||||||
if !module.decls.is_empty() || module.main.is_some() {
|
if !module.decls.is_empty() || module.main.is_some() {
|
||||||
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
|
let commands: Vec<(Vec<u8>, DeclId)> = engine_state
|
||||||
|
.get_decls_sorted(false)
|
||||||
|
.filter(|(_, id)| !engine_state.get_decl(*id).is_alias())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut module_commands = module.decls();
|
let mut module_commands: Vec<(Vec<u8>, DeclId)> = module
|
||||||
|
.decls()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, id)| !engine_state.get_decl(*id).is_alias())
|
||||||
|
.collect();
|
||||||
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
|
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
|
||||||
let commands_str = module_commands
|
let commands_str = module_commands
|
||||||
@ -181,15 +186,18 @@ pub fn help_modules(
|
|||||||
long_desc.push_str("\n\n");
|
long_desc.push_str("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !module.aliases.is_empty() {
|
if !module.decls.is_empty() {
|
||||||
let aliases: Vec<(Vec<u8>, AliasId)> = engine_state.get_aliases_sorted(false).collect();
|
let aliases: Vec<(Vec<u8>, DeclId)> = engine_state
|
||||||
|
.get_decls_sorted(false)
|
||||||
let mut module_aliases: Vec<(&[u8], AliasId)> = module
|
.filter(|(_, id)| engine_state.get_decl(*id).is_alias())
|
||||||
.aliases
|
|
||||||
.iter()
|
|
||||||
.map(|(name, id)| (name.as_ref(), *id))
|
|
||||||
.collect();
|
.collect();
|
||||||
module_aliases.sort_by(|a, b| a.0.cmp(b.0));
|
|
||||||
|
let mut module_aliases: Vec<(Vec<u8>, DeclId)> = module
|
||||||
|
.decls()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, id)| engine_state.get_decl(*id).is_alias())
|
||||||
|
.collect();
|
||||||
|
module_aliases.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
|
||||||
let aliases_str = module_aliases
|
let aliases_str = module_aliases
|
||||||
.iter()
|
.iter()
|
||||||
@ -198,7 +206,7 @@ pub fn help_modules(
|
|||||||
if let Some((used_name_bytes, _)) =
|
if let Some((used_name_bytes, _)) =
|
||||||
aliases.iter().find(|(_, alias_id)| id == alias_id)
|
aliases.iter().find(|(_, alias_id)| id == alias_id)
|
||||||
{
|
{
|
||||||
if engine_state.find_alias(name.as_bytes(), &[]).is_some() {
|
if engine_state.find_decl(name.as_bytes(), &[]).is_some() {
|
||||||
format!("{CB}{name}{RESET}")
|
format!("{CB}{name}{RESET}")
|
||||||
} else {
|
} else {
|
||||||
let alias_name = String::from_utf8_lossy(used_name_bytes);
|
let alias_name = String::from_utf8_lossy(used_name_bytes);
|
||||||
|
@ -40,15 +40,6 @@ impl Command for If {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -25,15 +25,6 @@ impl Command for Loop {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -69,7 +60,9 @@ impl Command for Loop {
|
|||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.drain_with_exit_code()?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
return Ok(PipelineData::new_external_stream_with_only_exit_code(
|
||||||
|
exit_code,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,15 +29,6 @@ impl Command for Match {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod alias;
|
mod alias;
|
||||||
mod break_;
|
mod break_;
|
||||||
mod collect;
|
mod collect;
|
||||||
mod commandline;
|
|
||||||
mod const_;
|
mod const_;
|
||||||
mod continue_;
|
mod continue_;
|
||||||
mod def;
|
mod def;
|
||||||
@ -43,7 +42,6 @@ mod while_;
|
|||||||
pub use alias::Alias;
|
pub use alias::Alias;
|
||||||
pub use break_::Break;
|
pub use break_::Break;
|
||||||
pub use collect::Collect;
|
pub use collect::Collect;
|
||||||
pub use commandline::Commandline;
|
|
||||||
pub use const_::Const;
|
pub use const_::Const;
|
||||||
pub use continue_::Continue;
|
pub use continue_::Continue;
|
||||||
pub use def::Def;
|
pub use def::Def;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
|
use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env, CallExt};
|
||||||
use nu_parser::trim_quotes_str;
|
use nu_parser::trim_quotes_str;
|
||||||
use nu_protocol::ast::{Call, Expr};
|
use nu_protocol::ast::{Call, Expr};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
@ -66,23 +66,24 @@ impl Command for OverlayUse {
|
|||||||
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
||||||
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
|
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
|
||||||
|
|
||||||
let maybe_origin_module_id = if let Some(overlay_expr) = call.parser_info_nth(0) {
|
let maybe_origin_module_id =
|
||||||
if let Expr::Overlay(module_id) = overlay_expr.expr {
|
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
|
||||||
module_id
|
if let Expr::Overlay(module_id) = overlay_expr.expr {
|
||||||
|
module_id
|
||||||
|
} else {
|
||||||
|
return Err(ShellError::NushellFailedSpanned {
|
||||||
|
msg: "Not an overlay".to_string(),
|
||||||
|
label: "requires an overlay (path or a string)".to_string(),
|
||||||
|
span: overlay_expr.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::NushellFailedSpanned {
|
return Err(ShellError::NushellFailedSpanned {
|
||||||
msg: "Not an overlay".to_string(),
|
msg: "Missing positional".to_string(),
|
||||||
label: "requires an overlay (path or a string)".to_string(),
|
label: "missing required overlay".to_string(),
|
||||||
span: overlay_expr.span,
|
span: call.head,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
} else {
|
|
||||||
return Err(ShellError::NushellFailedSpanned {
|
|
||||||
msg: "Missing positional".to_string(),
|
|
||||||
label: "missing required overlay".to_string(),
|
|
||||||
span: call.head,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
|
let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
|
||||||
name
|
name
|
||||||
@ -113,7 +114,12 @@ impl Command for OverlayUse {
|
|||||||
|
|
||||||
// Evaluate the export-env block (if any) and keep its environment
|
// Evaluate the export-env block (if any) and keep its environment
|
||||||
if let Some(block_id) = module.env_block {
|
if let Some(block_id) = module.env_block {
|
||||||
let maybe_path = find_in_dirs_env(&name_arg.item, engine_state, caller_stack)?;
|
let maybe_path = find_in_dirs_env(
|
||||||
|
&name_arg.item,
|
||||||
|
engine_state,
|
||||||
|
caller_stack,
|
||||||
|
get_dirs_var_from_call(call),
|
||||||
|
)?;
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
||||||
@ -128,6 +134,11 @@ impl Command for OverlayUse {
|
|||||||
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(file_path) = &maybe_path {
|
||||||
|
let file_path = Value::string(file_path.to_string_lossy(), call.head);
|
||||||
|
callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path);
|
||||||
|
}
|
||||||
|
|
||||||
let _ = eval_block(
|
let _ = eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut callee_stack,
|
&mut callee_stack,
|
||||||
|
@ -36,15 +36,6 @@ impl Command for Try {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{eval_block, find_in_dirs_env, redirect_env};
|
use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env};
|
||||||
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::{
|
||||||
@ -45,13 +45,10 @@ impl Command for Use {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let import_pattern = if let Some(Expression {
|
let Some(Expression {
|
||||||
expr: Expr::ImportPattern(pat),
|
expr: Expr::ImportPattern(import_pattern),
|
||||||
..
|
..
|
||||||
}) = call.parser_info_nth(0)
|
}) = call.get_parser_info("import_pattern") else {
|
||||||
{
|
|
||||||
pat
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Unexpected import".into(),
|
"Unexpected import".into(),
|
||||||
"import pattern not supported".into(),
|
"import pattern not supported".into(),
|
||||||
@ -72,13 +69,16 @@ impl Command for Use {
|
|||||||
let module_arg_str = String::from_utf8_lossy(
|
let module_arg_str = String::from_utf8_lossy(
|
||||||
engine_state.get_span_contents(&import_pattern.head.span),
|
engine_state.get_span_contents(&import_pattern.head.span),
|
||||||
);
|
);
|
||||||
let maybe_parent = if let Some(path) =
|
|
||||||
find_in_dirs_env(&module_arg_str, engine_state, caller_stack)?
|
let maybe_file_path = find_in_dirs_env(
|
||||||
{
|
&module_arg_str,
|
||||||
path.parent().map(|p| p.to_path_buf()).or(None)
|
engine_state,
|
||||||
} else {
|
caller_stack,
|
||||||
None
|
get_dirs_var_from_call(call),
|
||||||
};
|
)?;
|
||||||
|
let maybe_parent = maybe_file_path
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|path| path.parent().map(|p| p.to_path_buf()));
|
||||||
|
|
||||||
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
||||||
|
|
||||||
@ -88,6 +88,11 @@ impl Command for Use {
|
|||||||
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(file_path) = maybe_file_path {
|
||||||
|
let file_path = Value::string(file_path.to_string_lossy(), call.head);
|
||||||
|
callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the block (discard the result)
|
// Run the block (discard the result)
|
||||||
let _ = eval_block(
|
let _ = eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
|
@ -30,15 +30,6 @@ impl Command for While {
|
|||||||
.category(Category::Core)
|
.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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -79,7 +70,11 @@ impl Command for While {
|
|||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let exit_code = pipeline.drain_with_exit_code()?;
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
break;
|
return Ok(
|
||||||
|
PipelineData::new_external_stream_with_only_exit_code(
|
||||||
|
exit_code,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Alias,
|
Alias,
|
||||||
Break,
|
Break,
|
||||||
Collect,
|
Collect,
|
||||||
Commandline,
|
|
||||||
Const,
|
Const,
|
||||||
Continue,
|
Continue,
|
||||||
Def,
|
Def,
|
||||||
|
@ -126,9 +126,9 @@ fn eval_pipeline_without_terminal_expression(
|
|||||||
|
|
||||||
pub fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) {
|
pub fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let (output, err) = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]);
|
let output = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false);
|
||||||
|
|
||||||
if let Some(err) = err {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
panic!("test parse error in `{contents}`: {err:?}")
|
panic!("test parse error in `{contents}`: {err:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ 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.78.0"
|
version = "0.79.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
@ -15,11 +15,11 @@ serde = { version="1.0.123", features=["derive"] }
|
|||||||
# used only for text_style Alignments
|
# used only for text_style Alignments
|
||||||
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
nu-utils = { path = "../nu-utils", version = "0.79.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
nu-engine = { path = "../nu-engine", version = "0.79.0" }
|
||||||
nu-json = { path="../nu-json", version = "0.78.0" }
|
nu-json = { path="../nu-json", version = "0.79.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.78.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.79.0" }
|
||||||
|
@ -9,6 +9,7 @@ pub fn default_shape_color(shape: String) -> Style {
|
|||||||
"shape_binary" => Style::new().fg(Color::Purple).bold(),
|
"shape_binary" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_block" => Style::new().fg(Color::Blue).bold(),
|
"shape_block" => Style::new().fg(Color::Blue).bold(),
|
||||||
"shape_bool" => Style::new().fg(Color::LightCyan),
|
"shape_bool" => Style::new().fg(Color::LightCyan),
|
||||||
|
"shape_closure" => Style::new().fg(Color::Green).bold(),
|
||||||
"shape_custom" => Style::new().fg(Color::Green),
|
"shape_custom" => Style::new().fg(Color::Green),
|
||||||
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
|
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_directory" => Style::new().fg(Color::Cyan),
|
"shape_directory" => Style::new().fg(Color::Cyan),
|
||||||
@ -21,6 +22,7 @@ pub fn default_shape_color(shape: String) -> Style {
|
|||||||
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
|
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_int" => Style::new().fg(Color::Purple).bold(),
|
"shape_int" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_keyword" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_literal" => Style::new().fg(Color::Blue),
|
"shape_literal" => Style::new().fg(Color::Blue),
|
||||||
"shape_match_pattern" => Style::new().fg(Color::Green),
|
"shape_match_pattern" => Style::new().fg(Color::Green),
|
||||||
@ -36,6 +38,7 @@ pub fn default_shape_color(shape: String) -> Style {
|
|||||||
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
|
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_table" => Style::new().fg(Color::Blue).bold(),
|
"shape_table" => Style::new().fg(Color::Blue).bold(),
|
||||||
"shape_variable" => Style::new().fg(Color::Purple),
|
"shape_variable" => Style::new().fg(Color::Purple),
|
||||||
|
"shape_vardecl" => Style::new().fg(Color::Purple),
|
||||||
_ => Style::default(),
|
_ => Style::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||||
version = "0.78.0"
|
version = "0.79.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -13,20 +13,20 @@ version = "0.78.0"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.78.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.79.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.78.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.79.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.78.0" }
|
nu-engine = { path = "../nu-engine", version = "0.79.0" }
|
||||||
nu-explore = { path = "../nu-explore", version = "0.78.0" }
|
nu-explore = { path = "../nu-explore", version = "0.79.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.78.0" }
|
nu-glob = { path = "../nu-glob", version = "0.79.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.78.0" }
|
nu-json = { path = "../nu-json", version = "0.79.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.78.0" }
|
nu-parser = { path = "../nu-parser", version = "0.79.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.78.0" }
|
nu-path = { path = "../nu-path", version = "0.79.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.78.0" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.79.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.78.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.79.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.78.0" }
|
nu-system = { path = "../nu-system", version = "0.79.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.78.0" }
|
nu-table = { path = "../nu-table", version = "0.79.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.78.0" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.79.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.78.0" }
|
nu-utils = { path = "../nu-utils", version = "0.79.0" }
|
||||||
num-format = { version = "0.4.3" }
|
num-format = { version = "0.4.3" }
|
||||||
|
|
||||||
nu-ansi-term = "0.47.0"
|
nu-ansi-term = "0.47.0"
|
||||||
@ -42,11 +42,11 @@ calamine = "0.19.1"
|
|||||||
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
|
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
|
||||||
chrono-humanize = "0.2.1"
|
chrono-humanize = "0.2.1"
|
||||||
chrono-tz = "0.8.1"
|
chrono-tz = "0.8.1"
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.26"
|
||||||
csv = "1.2.0"
|
csv = "1.2.0"
|
||||||
dialoguer = { default-features = false, version = "0.10.3" }
|
dialoguer = { default-features = false, version = "0.10.3" }
|
||||||
digest = { default-features = false, version = "0.10.0" }
|
digest = { default-features = false, version = "0.10.0" }
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.4.0"
|
||||||
encoding_rs = "0.8.30"
|
encoding_rs = "0.8.30"
|
||||||
fancy-regex = "0.11.0"
|
fancy-regex = "0.11.0"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
@ -58,16 +58,16 @@ indicatif = "0.17.2"
|
|||||||
is-root = "0.1.2"
|
is-root = "0.1.2"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
lscolors = { version = "0.14", default-features = false, features = ["nu-ansi-term"] }
|
||||||
md5 = { package = "md-5", version = "0.10.0" }
|
md5 = { package = "md-5", version = "0.10.0" }
|
||||||
miette = { version = "5.6.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
mime_guess = "2.0.4"
|
mime_guess = "2.0.4"
|
||||||
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.17"
|
once_cell = "1.17"
|
||||||
open = "4.0.0"
|
open = "4.0.2"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
powierza-coefficient = "1.0.2"
|
powierza-coefficient = "1.0.2"
|
||||||
quick-xml = "0.28"
|
quick-xml = "0.28"
|
||||||
@ -85,7 +85,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)
|
||||||
percent-encoding = "2.2.0"
|
percent-encoding = "2.2.0"
|
||||||
reedline = { version = "0.18.0", features = ["bashisms", "sqlite"] }
|
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.32.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.32.0", features = ["serde"], optional = true }
|
||||||
sysinfo = "0.28.2"
|
sysinfo = "0.28.2"
|
||||||
@ -103,11 +102,11 @@ which = { version = "4.4.0", optional = true }
|
|||||||
print-positions = "0.6.1"
|
print-positions = "0.6.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.11.0"
|
winreg = "0.50.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
umask = "2.0.0"
|
umask = "2.1.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
@ -147,7 +146,7 @@ version = "0.27.2"
|
|||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
||||||
version = "0.46.0"
|
version = "0.48.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dataframe = ["num", "polars", "sqlparser"]
|
dataframe = ["num", "polars", "sqlparser"]
|
||||||
@ -157,7 +156,7 @@ trash-support = ["trash"]
|
|||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.78.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.79.0" }
|
||||||
mockito = "1.0.0"
|
mockito = "1.0.0"
|
||||||
|
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
|
@ -3,7 +3,7 @@ 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, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -25,6 +25,7 @@ impl Command for SubCommand {
|
|||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, convert data at the given cell paths",
|
"for a data structure input, convert data at the given cell paths",
|
||||||
)
|
)
|
||||||
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
|
@ -50,15 +50,9 @@ pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
|||||||
|
|
||||||
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 = parse(&mut working_set, None, example.example.as_bytes(), false);
|
||||||
&mut working_set,
|
|
||||||
None,
|
|
||||||
example.example.as_bytes(),
|
|
||||||
false,
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(err) = err {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
panic!("test parse error in `{}`: {:?}", example.example, err)
|
panic!("test parse error in `{}`: {:?}", example.example, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ impl Command for SubCommand {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Get the current date in UTC+05:00",
|
description: "Get the current date in UTC+05:00",
|
||||||
example: "date now | date to-timezone +0500",
|
example: "date now | date to-timezone '+0500'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -41,8 +41,10 @@ impl Command for Ast {
|
|||||||
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let (block_output, error_output) =
|
let block_output = parse(&mut working_set, None, pipeline.item.as_bytes(), false);
|
||||||
parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
|
|
||||||
|
let error_output = working_set.parse_errors.first();
|
||||||
|
|
||||||
let block_value = Value::String {
|
let block_value = Value::String {
|
||||||
val: format!("{block_output:#?}"),
|
val: format!("{block_output:#?}"),
|
||||||
span: pipeline.span,
|
span: pipeline.span,
|
||||||
|
@ -51,7 +51,7 @@ impl Command for Explain {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Explain a command within a closure",
|
description: "Explain a command within a closure",
|
||||||
example: "explain { ls | sort-by name type -i | get name } | table -e",
|
example: "explain {|| ls | sort-by name type -i | get name } | table -e",
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ Current known limitations are:
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description:
|
description:
|
||||||
"Profile some code, stepping into the `spam` command and collecting source.",
|
"Profile some code, stepping into the `spam` command and collecting source.",
|
||||||
example: r#"def spam [] { "spam" }; profile { spam | str length } -d 2 --source"#,
|
example: r#"def spam [] { "spam" }; profile {|| spam | str length } -d 2 --source"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
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};
|
||||||
@ -133,15 +132,6 @@ impl Command for ViewSource {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else if let Some(alias_id) = engine_state.find_alias(val.as_bytes(), &[]) {
|
|
||||||
let contents = &mut engine_state.get_alias(alias_id).iter().map(|span| {
|
|
||||||
String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string()
|
|
||||||
});
|
|
||||||
Ok(Value::String {
|
|
||||||
val: contents.join(" "),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::GenericError(
|
Err(ShellError::GenericError(
|
||||||
"Cannot view value".to_string(),
|
"Cannot view value".to_string(),
|
||||||
@ -166,7 +156,7 @@ impl Command for ViewSource {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "View the source of a code block",
|
description: "View the source of a code block",
|
||||||
example: r#"let abc = { echo 'hi' }; view source $abc"#,
|
example: r#"let abc = {|| echo 'hi' }; view source $abc"#,
|
||||||
result: Some(Value::test_string("{ echo 'hi' }")),
|
result: Some(Value::test_string("{ echo 'hi' }")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -56,6 +56,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
GroupBy,
|
GroupBy,
|
||||||
Headers,
|
Headers,
|
||||||
Insert,
|
Insert,
|
||||||
|
Items,
|
||||||
Join,
|
Join,
|
||||||
SplitBy,
|
SplitBy,
|
||||||
Take,
|
Take,
|
||||||
@ -102,9 +103,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
bind_command! {
|
bind_command! {
|
||||||
History,
|
|
||||||
Tutor,
|
Tutor,
|
||||||
HistorySession,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Path
|
// Path
|
||||||
@ -256,12 +255,8 @@ pub fn create_default_context() -> EngineState {
|
|||||||
AnsiLink,
|
AnsiLink,
|
||||||
Clear,
|
Clear,
|
||||||
Du,
|
Du,
|
||||||
KeybindingsDefault,
|
|
||||||
Input,
|
Input,
|
||||||
KeybindingsListen,
|
|
||||||
Keybindings,
|
|
||||||
Kill,
|
Kill,
|
||||||
KeybindingsList,
|
|
||||||
Sleep,
|
Sleep,
|
||||||
TermSize,
|
TermSize,
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ impl Command for Source {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||||
// it is put here by the parser
|
// it is put here by the parser
|
||||||
let block_id: i64 = call.req_parser_info(engine_state, stack, 0)?;
|
let block_id: i64 = call.req_parser_info(engine_state, stack, "block_id")?;
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id as usize).clone();
|
let block = engine_state.get_block(block_id as usize).clone();
|
||||||
eval_block_with_early_return(
|
eval_block_with_early_return(
|
||||||
|
11
crates/nu-command/src/env/config/config_env.rs
vendored
11
crates/nu-command/src/env/config/config_env.rs
vendored
@ -42,21 +42,18 @@ impl Command for ConfigEnv {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let env_vars_str = env_to_strings(engine_state, stack)?;
|
let env_vars_str = env_to_strings(engine_state, stack)?;
|
||||||
let mut config_path = match nu_path::config_dir() {
|
let nu_config = match engine_state.get_config_path("env-path") {
|
||||||
Some(path) => path,
|
Some(path) => path.clone(),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Could not find nu env path".to_string(),
|
"Could not find $nu.env-path".to_string(),
|
||||||
"Could not find nu env path".to_string(),
|
"Could not find $nu.env-path".to_string(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
config_path.push("nushell");
|
|
||||||
let mut nu_config = config_path.clone();
|
|
||||||
nu_config.push("env.nu");
|
|
||||||
|
|
||||||
let (item, config_args) = get_editor(engine_state, stack, call.head)?;
|
let (item, config_args) = get_editor(engine_state, stack, call.head)?;
|
||||||
|
|
||||||
|
11
crates/nu-command/src/env/config/config_nu.rs
vendored
11
crates/nu-command/src/env/config/config_nu.rs
vendored
@ -42,21 +42,18 @@ impl Command for ConfigNu {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let env_vars_str = env_to_strings(engine_state, stack)?;
|
let env_vars_str = env_to_strings(engine_state, stack)?;
|
||||||
let mut config_path = match nu_path::config_dir() {
|
let nu_config = match engine_state.get_config_path("config-path") {
|
||||||
Some(path) => path,
|
Some(path) => path.clone(),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Could not find nu config path".to_string(),
|
"Could not find $nu.config-path".to_string(),
|
||||||
"Could not find nu config path".to_string(),
|
"Could not find $nu.config-path".to_string(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
config_path.push("nushell");
|
|
||||||
let mut nu_config = config_path.clone();
|
|
||||||
nu_config.push("config.nu");
|
|
||||||
|
|
||||||
let (item, config_args) = get_editor(engine_state, stack, call.head)?;
|
let (item, config_args) = get_editor(engine_state, stack, call.head)?;
|
||||||
|
|
||||||
|
2
crates/nu-command/src/env/let_env.rs
vendored
2
crates/nu-command/src/env/let_env.rs
vendored
@ -51,7 +51,7 @@ impl Command for LetEnv {
|
|||||||
.0
|
.0
|
||||||
.into_value(call.head);
|
.into_value(call.head);
|
||||||
|
|
||||||
if env_var.item == "FILE_PWD" || env_var.item == "PWD" {
|
if env_var.item == "FILE_PWD" || env_var.item == "CURRENT_FILE" || env_var.item == "PWD" {
|
||||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
return Err(ShellError::AutomaticEnvVarSetManually {
|
||||||
envvar_name: env_var.item,
|
envvar_name: env_var.item,
|
||||||
span: env_var.span,
|
span: env_var.span,
|
||||||
|
16
crates/nu-command/src/env/load_env.rs
vendored
16
crates/nu-command/src/env/load_env.rs
vendored
@ -42,28 +42,22 @@ impl Command for LoadEnv {
|
|||||||
match arg {
|
match arg {
|
||||||
Some((cols, vals)) => {
|
Some((cols, vals)) => {
|
||||||
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
||||||
if env_var == "FILE_PWD" {
|
let env_var_ = env_var.as_str();
|
||||||
|
if ["FILE_PWD", "CURRENT_FILE", "PWD"].contains(&env_var_) {
|
||||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
return Err(ShellError::AutomaticEnvVarSetManually {
|
||||||
envvar_name: env_var,
|
envvar_name: env_var,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
stack.add_env_var(env_var, rhs);
|
||||||
if env_var == "PWD" {
|
|
||||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
|
||||||
envvar_name: env_var,
|
|
||||||
span: call.head,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
stack.add_env_var(env_var, rhs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
None => match input {
|
None => match input {
|
||||||
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
|
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
|
||||||
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
for (env_var, rhs) in cols.into_iter().zip(vals) {
|
||||||
if env_var == "FILE_PWD" {
|
let env_var_ = env_var.as_str();
|
||||||
|
if ["FILE_PWD", "CURRENT_FILE"].contains(&env_var_) {
|
||||||
return Err(ShellError::AutomaticEnvVarSetManually {
|
return Err(ShellError::AutomaticEnvVarSetManually {
|
||||||
envvar_name: env_var,
|
envvar_name: env_var,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
|
28
crates/nu-command/src/env/source_env.rs
vendored
28
crates/nu-command/src/env/source_env.rs
vendored
@ -1,6 +1,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_engine::{eval_block_with_early_return, find_in_dirs_env, redirect_env, CallExt};
|
use nu_engine::{
|
||||||
|
eval_block_with_early_return, find_in_dirs_env, get_dirs_var_from_call, redirect_env, 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::{
|
||||||
@ -42,21 +44,30 @@ impl Command for SourceEnv {
|
|||||||
|
|
||||||
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||||
// it is put here by the parser
|
// it is put here by the parser
|
||||||
let block_id: i64 = call.req_parser_info(engine_state, caller_stack, 0)?;
|
let block_id: i64 = call.req_parser_info(engine_state, caller_stack, "block_id")?;
|
||||||
|
|
||||||
// Set the currently evaluated directory (file-relative PWD)
|
// Set the currently evaluated directory (file-relative PWD)
|
||||||
let mut parent = if let Some(path) =
|
let file_path = if let Some(path) = find_in_dirs_env(
|
||||||
find_in_dirs_env(&source_filename.item, engine_state, caller_stack)?
|
&source_filename.item,
|
||||||
{
|
engine_state,
|
||||||
|
caller_stack,
|
||||||
|
get_dirs_var_from_call(call),
|
||||||
|
)? {
|
||||||
PathBuf::from(&path)
|
PathBuf::from(&path)
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::FileNotFound(source_filename.span));
|
return Err(ShellError::FileNotFound(source_filename.span));
|
||||||
};
|
};
|
||||||
parent.pop();
|
|
||||||
|
|
||||||
let file_pwd = Value::string(parent.to_string_lossy(), call.head);
|
if let Some(parent) = file_path.parent() {
|
||||||
|
let file_pwd = Value::string(parent.to_string_lossy(), call.head);
|
||||||
|
|
||||||
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
caller_stack.add_env_var(
|
||||||
|
"CURRENT_FILE".to_string(),
|
||||||
|
Value::string(file_path.to_string_lossy(), call.head),
|
||||||
|
);
|
||||||
|
|
||||||
// Evaluate the block
|
// Evaluate the block
|
||||||
let block = engine_state.get_block(block_id as usize).clone();
|
let block = engine_state.get_block(block_id as usize).clone();
|
||||||
@ -76,6 +87,7 @@ impl Command for SourceEnv {
|
|||||||
|
|
||||||
// Remove the file-relative PWD
|
// Remove the file-relative PWD
|
||||||
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
||||||
|
caller_stack.remove_env_var(engine_state, "CURRENT_FILE");
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ impl Command for Ls {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "List given paths and show directories themselves",
|
description: "List given paths and show directories themselves",
|
||||||
example: "['/path/to/directory' '/path/to/file'] | each { ls -D $in } | flatten",
|
example: "['/path/to/directory' '/path/to/file'] | each {|| ls -D $in } | flatten",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -50,21 +50,15 @@ impl Command for Rm {
|
|||||||
"filename",
|
"filename",
|
||||||
SyntaxShape::Filepath,
|
SyntaxShape::Filepath,
|
||||||
"the path of the file you want to remove",
|
"the path of the file you want to remove",
|
||||||
);
|
)
|
||||||
#[cfg(all(
|
|
||||||
feature = "trash-support",
|
|
||||||
not(target_os = "android"),
|
|
||||||
not(target_os = "ios")
|
|
||||||
))]
|
|
||||||
let sig = sig
|
|
||||||
.switch(
|
.switch(
|
||||||
"trash",
|
"trash",
|
||||||
"move to the platform's trash instead of permanently deleting",
|
"move to the platform's trash instead of permanently deleting. not used on android and ios",
|
||||||
Some('t'),
|
Some('t'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"permanent",
|
"permanent",
|
||||||
"delete permanently, ignoring the 'always_trash' config option",
|
"delete permanently, ignoring the 'always_trash' config option. always enabled on android and ios",
|
||||||
Some('p'),
|
Some('p'),
|
||||||
);
|
);
|
||||||
sig.switch("recursive", "delete subdirectories recursively", Some('r'))
|
sig.switch("recursive", "delete subdirectories recursively", Some('r'))
|
||||||
@ -169,18 +163,13 @@ fn rm(
|
|||||||
|
|
||||||
let rm_always_trash = config.rm_always_trash;
|
let rm_always_trash = config.rm_always_trash;
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(not(feature = "trash-support"))]
|
||||||
not(feature = "trash-support"),
|
|
||||||
target_os = "android",
|
|
||||||
target_os = "ios"
|
|
||||||
))]
|
|
||||||
{
|
{
|
||||||
if rm_always_trash {
|
if rm_always_trash {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Cannot execute `rm`; the current configuration specifies \
|
"Cannot execute `rm`; the current configuration specifies \
|
||||||
`always_trash = true`, but the current nu executable was not \
|
`always_trash = true`, but the current nu executable was not \
|
||||||
built with feature `trash_support` or trash is not supported on \
|
built with feature `trash_support`."
|
||||||
your platform."
|
|
||||||
.into(),
|
.into(),
|
||||||
"trash required to be true but not supported".into(),
|
"trash required to be true but not supported".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
@ -189,8 +178,7 @@ fn rm(
|
|||||||
));
|
));
|
||||||
} else if trash {
|
} else if trash {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Cannot execute `rm` with option `--trash`; feature `trash-support` not \
|
"Cannot execute `rm` with option `--trash`; feature `trash-support` not enabled"
|
||||||
enabled or trash is not supported on your platform"
|
|
||||||
.into(),
|
.into(),
|
||||||
"this option is only available if nu is built with the `trash-support` feature"
|
"this option is only available if nu is built with the `trash-support` feature"
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -269,7 +269,7 @@ impl Command for Watch {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Run `cargo test` whenever a Rust file changes",
|
description: "Run `cargo test` whenever a Rust file changes",
|
||||||
example: r#"watch . --glob=**/*.rs { cargo test }"#,
|
example: r#"watch . --glob=**/*.rs {|| cargo test }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -164,6 +164,7 @@ with 'transpose' first."#
|
|||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
) {
|
) {
|
||||||
Ok(v) => Some(v.into_value(span)),
|
Ok(v) => Some(v.into_value(span)),
|
||||||
|
Err(ShellError::Continue(v)) => Some(Value::nothing(v)),
|
||||||
Err(ShellError::Break(_)) => None,
|
Err(ShellError::Break(_)) => None,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = chain_error_with_input(error, input_span);
|
let error = chain_error_with_input(error, input_span);
|
||||||
@ -188,6 +189,7 @@ with 'transpose' first."#
|
|||||||
|
|
||||||
let x = match x {
|
let x = match x {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
|
Err(ShellError::Continue(v)) => return Some(Value::nothing(v)),
|
||||||
Err(ShellError::Break(_)) => return None,
|
Err(ShellError::Break(_)) => return None,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Some(Value::Error {
|
return Some(Value::Error {
|
||||||
@ -212,6 +214,7 @@ with 'transpose' first."#
|
|||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
) {
|
) {
|
||||||
Ok(v) => Some(v.into_value(span)),
|
Ok(v) => Some(v.into_value(span)),
|
||||||
|
Err(ShellError::Continue(v)) => Some(Value::nothing(v)),
|
||||||
Err(ShellError::Break(_)) => None,
|
Err(ShellError::Break(_)) => None,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = Box::new(chain_error_with_input(error, input_span));
|
let error = Box::new(chain_error_with_input(error, input_span));
|
||||||
|
@ -58,6 +58,12 @@ impl Command for Find {
|
|||||||
"dotall regex mode: allow a dot . to match newlines \\n; equivalent to (?s)",
|
"dotall regex mode: allow a dot . to match newlines \\n; equivalent to (?s)",
|
||||||
Some('s'),
|
Some('s'),
|
||||||
)
|
)
|
||||||
|
.named(
|
||||||
|
"columns",
|
||||||
|
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||||
|
"column names to be searched (with rest parameter, not regex yet)",
|
||||||
|
Some('c'),
|
||||||
|
)
|
||||||
.switch("invert", "invert the match", Some('v'))
|
.switch("invert", "invert the match", Some('v'))
|
||||||
.rest("rest", SyntaxShape::Any, "terms to search")
|
.rest("rest", SyntaxShape::Any, "terms to search")
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
@ -131,6 +137,36 @@ impl Command for Find {
|
|||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Remove ANSI sequences from result",
|
||||||
|
example: "[[foo bar]; [abc 123] [def 456]] | find 123 | get bar | ansi strip",
|
||||||
|
result: None, // This is None because ansi strip is not available in tests
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Find and highlight text in specific columns",
|
||||||
|
example: "[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1 col3]",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_record(
|
||||||
|
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
|
||||||
|
vec![
|
||||||
|
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
|
||||||
|
Value::test_string("larry".to_string()),
|
||||||
|
Value::test_string("curly".to_string()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
Value::test_record(
|
||||||
|
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
|
||||||
|
vec![
|
||||||
|
Value::test_string("larry".to_string()),
|
||||||
|
Value::test_string("curly".to_string()),
|
||||||
|
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,13 +210,13 @@ fn find_with_regex(
|
|||||||
|
|
||||||
let flags = match (insensitive, multiline, dotall) {
|
let flags = match (insensitive, multiline, dotall) {
|
||||||
(false, false, false) => "",
|
(false, false, false) => "",
|
||||||
(true, false, false) => "(?i)",
|
(true, false, false) => "(?i)", // case insensitive
|
||||||
(false, true, false) => "(?m)",
|
(false, true, false) => "(?m)", // multi-line mode
|
||||||
(false, false, true) => "(?s)",
|
(false, false, true) => "(?s)", // allow . to match \n
|
||||||
(true, true, false) => "(?im)",
|
(true, true, false) => "(?im)", // case insensitive and multi-line mode
|
||||||
(true, false, true) => "(?is)",
|
(true, false, true) => "(?is)", // case insensitive and allow . to match \n
|
||||||
(false, true, true) => "(?ms)",
|
(false, true, true) => "(?ms)", // multi-line mode and allow . to match \n
|
||||||
(true, true, true) => "(?ims)",
|
(true, true, true) => "(?ims)", // case insensitive, multi-line mode and allow . to match \n
|
||||||
};
|
};
|
||||||
|
|
||||||
let regex = flags.to_string() + regex.as_str();
|
let regex = flags.to_string() + regex.as_str();
|
||||||
@ -221,7 +257,9 @@ fn find_with_regex(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_terms_in_record(
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn highlight_terms_in_record_with_search_columns(
|
||||||
|
search_cols: &Vec<String>,
|
||||||
cols: &mut [String],
|
cols: &mut [String],
|
||||||
vals: &mut Vec<Value>,
|
vals: &mut Vec<Value>,
|
||||||
span: &mut Span,
|
span: &mut Span,
|
||||||
@ -230,23 +268,32 @@ fn highlight_terms_in_record(
|
|||||||
string_style: Style,
|
string_style: Style,
|
||||||
ls_colors: &LsColors,
|
ls_colors: &LsColors,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
|
let cols_to_search = if search_cols.is_empty() {
|
||||||
|
cols.to_vec()
|
||||||
|
} else {
|
||||||
|
search_cols.to_vec()
|
||||||
|
};
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for val in vals {
|
let mut potential_output = vec![];
|
||||||
|
let mut found_a_hit = false;
|
||||||
|
for (cur_col, val) in cols.iter().zip(vals) {
|
||||||
let val_str = val.into_string("", config);
|
let val_str = val.into_string("", config);
|
||||||
let lower_val = val.into_string("", config).to_lowercase();
|
let lower_val = val.into_string("", config).to_lowercase();
|
||||||
let mut term_added_to_output = false;
|
let mut term_added_to_output = false;
|
||||||
for term in terms {
|
for term in terms {
|
||||||
let term_str = term.into_string("", config);
|
let term_str = term.into_string("", config);
|
||||||
let lower_term = term.into_string("", config).to_lowercase();
|
let lower_term = term.into_string("", config).to_lowercase();
|
||||||
if lower_val.contains(&lower_term) {
|
if lower_val.contains(&lower_term) && cols_to_search.contains(cur_col) {
|
||||||
|
found_a_hit = true;
|
||||||
|
term_added_to_output = true;
|
||||||
if config.use_ls_colors {
|
if config.use_ls_colors {
|
||||||
// Get the original LS_COLORS color
|
// Get the original LS_COLORS color
|
||||||
let style = ls_colors.style_for_path(val_str.clone());
|
let style = ls_colors.style_for_path(val_str.clone());
|
||||||
let ansi_style = style
|
let ansi_style = style
|
||||||
.map(LsColors_Style::to_crossterm_style)
|
.map(LsColors_Style::to_nu_ansi_term_style)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let ls_colored_val = ansi_style.apply(&val_str).to_string();
|
let ls_colored_val = ansi_style.paint(&val_str).to_string();
|
||||||
|
|
||||||
let ansi_term_style = style
|
let ansi_term_style = style
|
||||||
.map(to_nu_ansi_term_style)
|
.map(to_nu_ansi_term_style)
|
||||||
@ -258,11 +305,10 @@ fn highlight_terms_in_record(
|
|||||||
Ok(hi) => hi,
|
Ok(hi) => hi,
|
||||||
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
||||||
};
|
};
|
||||||
output.push(Value::String {
|
potential_output.push(Value::String {
|
||||||
val: hi,
|
val: hi,
|
||||||
span: *span,
|
span: *span,
|
||||||
});
|
});
|
||||||
term_added_to_output = true;
|
|
||||||
} else {
|
} else {
|
||||||
// No LS_COLORS support, so just use the original value
|
// No LS_COLORS support, so just use the original value
|
||||||
let hi = match highlight_search_string(&val_str, &term_str, &string_style) {
|
let hi = match highlight_search_string(&val_str, &term_str, &string_style) {
|
||||||
@ -277,9 +323,14 @@ fn highlight_terms_in_record(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !term_added_to_output {
|
if !term_added_to_output {
|
||||||
output.push(val.clone());
|
potential_output.push(val.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if found_a_hit {
|
||||||
|
output.append(&mut potential_output);
|
||||||
|
}
|
||||||
|
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: cols.to_vec(),
|
cols: cols.to_vec(),
|
||||||
vals: output,
|
vals: output,
|
||||||
@ -310,6 +361,7 @@ fn find_with_rest_and_highlight(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<Value>>();
|
.collect::<Vec<Value>>();
|
||||||
|
let columns_to_search: Option<Vec<String>> = call.get_flag(&engine_state, stack, "columns")?;
|
||||||
|
|
||||||
let style_computer = StyleComputer::from_config(&engine_state, stack);
|
let style_computer = StyleComputer::from_config(&engine_state, stack);
|
||||||
// Currently, search results all use the same style.
|
// Currently, search results all use the same style.
|
||||||
@ -323,20 +375,28 @@ fn find_with_rest_and_highlight(
|
|||||||
};
|
};
|
||||||
let ls_colors = get_ls_colors(ls_colors_env_str);
|
let ls_colors = get_ls_colors(ls_colors_env_str);
|
||||||
|
|
||||||
|
let cols_to_search = match columns_to_search {
|
||||||
|
Some(cols) => cols,
|
||||||
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
PipelineData::Value(_, _) => input
|
PipelineData::Value(_, _) => input
|
||||||
.map(
|
.map(
|
||||||
move |mut x| match &mut x {
|
move |mut x| match &mut x {
|
||||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
Value::Record { cols, vals, span } => {
|
||||||
cols,
|
highlight_terms_in_record_with_search_columns(
|
||||||
vals,
|
&cols_to_search,
|
||||||
span,
|
cols,
|
||||||
&config,
|
vals,
|
||||||
&terms,
|
span,
|
||||||
string_style,
|
&config,
|
||||||
&ls_colors,
|
&terms,
|
||||||
),
|
string_style,
|
||||||
|
&ls_colors,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => x,
|
_ => x,
|
||||||
},
|
},
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
@ -412,15 +472,18 @@ fn find_with_rest_and_highlight(
|
|||||||
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
||||||
stream
|
stream
|
||||||
.map(move |mut x| match &mut x {
|
.map(move |mut x| match &mut x {
|
||||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
Value::Record { cols, vals, span } => {
|
||||||
cols,
|
highlight_terms_in_record_with_search_columns(
|
||||||
vals,
|
&cols_to_search,
|
||||||
span,
|
cols,
|
||||||
&config,
|
vals,
|
||||||
&terms,
|
span,
|
||||||
string_style,
|
&config,
|
||||||
&ls_colors,
|
&terms,
|
||||||
),
|
string_style,
|
||||||
|
&ls_colors,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => x,
|
_ => x,
|
||||||
})
|
})
|
||||||
.filter(move |value| {
|
.filter(move |value| {
|
||||||
|
155
crates/nu-command/src/filters/items.rs
Normal file
155
crates/nu-command/src/filters/items.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
use super::utils::chain_error_with_input;
|
||||||
|
use nu_engine::{eval_block_with_early_return, CallExt};
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
|
||||||
|
SyntaxShape, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Items;
|
||||||
|
|
||||||
|
impl Command for Items {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"items"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_output_types(vec![(
|
||||||
|
Type::Record(vec![]),
|
||||||
|
Type::List(Box::new(Type::String)),
|
||||||
|
)])
|
||||||
|
.required(
|
||||||
|
"closure",
|
||||||
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Any])),
|
||||||
|
"the closure to run",
|
||||||
|
)
|
||||||
|
.category(Category::Filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Given a record, iterate on each pair of column name and associated value."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"This is a the fusion of `columns`, `values` and `each`."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
let metadata = input.metadata();
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
let block = engine_state.get_block(capture_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 span = call.head;
|
||||||
|
let redirect_stdout = call.redirect_stdout;
|
||||||
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
|
let input_span = input.span().unwrap_or(call.head);
|
||||||
|
let run_for_each_item = move |keyval: (String, Value)| -> Option<Value> {
|
||||||
|
// with_env() is used here to ensure that each iteration uses
|
||||||
|
// a different set of environment variables.
|
||||||
|
// Hence, a 'cd' in the first loop won't affect the next loop.
|
||||||
|
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, Value::string(keyval.0.clone(), span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(1) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, keyval.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block_with_early_return(
|
||||||
|
&engine_state,
|
||||||
|
&mut stack,
|
||||||
|
&block,
|
||||||
|
PipelineData::empty(),
|
||||||
|
redirect_stdout,
|
||||||
|
redirect_stderr,
|
||||||
|
) {
|
||||||
|
Ok(v) => Some(v.into_value(span)),
|
||||||
|
Err(ShellError::Break(_)) => None,
|
||||||
|
Err(error) => {
|
||||||
|
let error = chain_error_with_input(error, Ok(input_span));
|
||||||
|
Some(Value::Error {
|
||||||
|
error: Box::new(error),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match input {
|
||||||
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
|
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => Ok(cols
|
||||||
|
.into_iter()
|
||||||
|
.zip(vals.into_iter())
|
||||||
|
.into_iter()
|
||||||
|
.map_while(run_for_each_item)
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
// Errors
|
||||||
|
PipelineData::ListStream(..) => Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "record".into(),
|
||||||
|
wrong_type: "stream".into(),
|
||||||
|
dst_span: call.head,
|
||||||
|
src_span: input_span,
|
||||||
|
}),
|
||||||
|
PipelineData::Value(Value::Error { error }, ..) => Err(*error),
|
||||||
|
PipelineData::Value(other, ..) => Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "record".into(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: call.head,
|
||||||
|
src_span: other.expect_span(),
|
||||||
|
}),
|
||||||
|
PipelineData::ExternalStream { .. } => Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "record".into(),
|
||||||
|
wrong_type: "raw data".into(),
|
||||||
|
dst_span: call.head,
|
||||||
|
src_span: input_span,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
.map(|x| x.set_metadata(metadata))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
example:
|
||||||
|
"{ new: york, san: francisco } | items {|key, value| echo $'($key) ($value)' }",
|
||||||
|
description: "Iterate over each key-value pair of a record",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_string("new york"),
|
||||||
|
Value::test_string("san francisco"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Items {})
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ mod group;
|
|||||||
mod group_by;
|
mod group_by;
|
||||||
mod headers;
|
mod headers;
|
||||||
mod insert;
|
mod insert;
|
||||||
|
mod items;
|
||||||
mod join;
|
mod join;
|
||||||
mod last;
|
mod last;
|
||||||
mod length;
|
mod length;
|
||||||
@ -75,6 +76,7 @@ pub use group::Group;
|
|||||||
pub use group_by::GroupBy;
|
pub use group_by::GroupBy;
|
||||||
pub use headers::Headers;
|
pub use headers::Headers;
|
||||||
pub use insert::Insert;
|
pub use insert::Insert;
|
||||||
|
pub use items::Items;
|
||||||
pub use join::Join;
|
pub use join::Join;
|
||||||
pub use last::Last;
|
pub use last::Last;
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
|
@ -47,7 +47,7 @@ impl Command for ParEach {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
example: "[1 2 3] | par-each { 2 * $in }",
|
example: "[1 2 3] | par-each {|| 2 * $in }",
|
||||||
description:
|
description:
|
||||||
"Multiplies each number. Note that the list will become arbitrarily disordered.",
|
"Multiplies each number. Note that the list will become arbitrarily disordered.",
|
||||||
result: None,
|
result: None,
|
||||||
@ -279,7 +279,6 @@ impl Command for ParEach {
|
|||||||
// This match allows non-iterables to be accepted,
|
// This match allows non-iterables to be accepted,
|
||||||
// which is currently considered undesirable (Nov 2022).
|
// which is currently considered undesirable (Nov 2022).
|
||||||
PipelineData::Value(x, ..) => {
|
PipelineData::Value(x, ..) => {
|
||||||
eprint!("value");
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
@ -46,7 +46,7 @@ impl Command for Shuffle {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Shuffle rows randomly (execute it several times and see the difference)",
|
description: "Shuffle rows randomly (execute it several times and see the difference)",
|
||||||
example: r#"[[version patch]; [1.0.0 false] [3.0.1 true] [2.0.0 false]] | shuffle"#,
|
example: r#"[[version patch]; ['1.0.0' false] ['3.0.1' true] ['2.0.0' false]] | shuffle"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,11 @@ impl Command for Update {
|
|||||||
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|row| $row.authors | str join ','}",
|
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|row| $row.authors | str join ','}",
|
||||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "You can also use a simple command to update 'authors' to a single string",
|
||||||
|
example: "[[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | update authors {|| str join ','}",
|
||||||
|
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,11 +123,16 @@ fn update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let input_at_path = match input.clone().follow_cell_path(&cell_path.members, false)
|
||||||
|
{
|
||||||
|
Err(e) => return Value::Error { error: Box::new(e) },
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
let output = eval_block(
|
let output = eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
&block,
|
&block,
|
||||||
input.clone().into_pipeline_data(),
|
input_at_path.into_pipeline_data(),
|
||||||
redirect_stdout,
|
redirect_stdout,
|
||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
);
|
);
|
||||||
|
@ -150,7 +150,7 @@ not supported."#
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Find files whose filenames don't begin with the correct sequential number",
|
description: "Find files whose filenames don't begin with the correct sequential number",
|
||||||
example: "ls | where type == file | sort-by name -n | enumerate | where {|e| $e.item.name !~ $'^($e.index + 1)' } | each { get item }",
|
example: "ls | where type == file | sort-by name -n | enumerate | where {|e| $e.item.name !~ $'^($e.index + 1)' } | each {|| get item }",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -77,7 +77,7 @@ impl Command for Zip {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
example: "glob *.ogg | zip ['bang.ogg', 'fanfare.ogg', 'laser.ogg'] | each { mv $in.0 $in.1 }",
|
example: "glob *.ogg | zip ['bang.ogg', 'fanfare.ogg', 'laser.ogg'] | each {|| mv $in.0 $in.1 }",
|
||||||
description: "Rename .ogg files to match an existing list of filenames",
|
description: "Rename .ogg files to match an existing list of filenames",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -64,10 +64,8 @@ impl Command for FromNuon {
|
|||||||
let engine_state = engine_state.clone();
|
let engine_state = engine_state.clone();
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let mut error = None;
|
|
||||||
let (mut block, err) =
|
let mut block = nu_parser::parse(&mut working_set, None, string_input.as_bytes(), false);
|
||||||
nu_parser::parse(&mut working_set, None, string_input.as_bytes(), false, &[]);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
if let Some(pipeline) = block.pipelines.get(1) {
|
if let Some(pipeline) = block.pipelines.get(1) {
|
||||||
if let Some(element) = pipeline.elements.get(0) {
|
if let Some(element) = pipeline.elements.get(0) {
|
||||||
@ -146,7 +144,7 @@ impl Command for FromNuon {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(err) = error {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"error when parsing nuon text".into(),
|
"error when parsing nuon text".into(),
|
||||||
"could not parse nuon text".into(),
|
"could not parse nuon text".into(),
|
||||||
|
@ -2,10 +2,10 @@ 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::Span;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
use nu_protocol::{IntoPipelineData, Span};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub trait HashDigest: digest::Digest + Clone {
|
pub trait HashDigest: digest::Digest + Clone {
|
||||||
@ -88,13 +88,58 @@ where
|
|||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
let args = Arguments { binary, cell_paths };
|
let args = Arguments { binary, cell_paths };
|
||||||
operate(
|
let mut hasher = D::new();
|
||||||
action::<D>,
|
match input {
|
||||||
args,
|
PipelineData::ExternalStream {
|
||||||
input,
|
stdout: Some(stream),
|
||||||
call.head,
|
span,
|
||||||
engine_state.ctrlc.clone(),
|
..
|
||||||
)
|
} => {
|
||||||
|
for item in stream {
|
||||||
|
match item {
|
||||||
|
// String and binary data are valid byte patterns
|
||||||
|
Ok(Value::String { val, .. }) => hasher.update(val.as_bytes()),
|
||||||
|
Ok(Value::Binary { val, .. }) => hasher.update(val),
|
||||||
|
// If any Error value is output, echo it back
|
||||||
|
Ok(v @ Value::Error { .. }) => return Ok(v.into_pipeline_data()),
|
||||||
|
// Unsupported data
|
||||||
|
Ok(other) => {
|
||||||
|
return Ok(Value::Error {
|
||||||
|
error: Box::new(ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "string and binary".into(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: span,
|
||||||
|
src_span: other.expect_span(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
.into_pipeline_data());
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let digest = hasher.finalize();
|
||||||
|
if args.binary {
|
||||||
|
Ok(Value::Binary {
|
||||||
|
val: digest.to_vec(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
} else {
|
||||||
|
Ok(Value::String {
|
||||||
|
val: format!("{digest:x}"),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => operate(
|
||||||
|
action::<D>,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::util::{get_guaranteed_cwd, report_error, report_error_new};
|
use crate::util::get_guaranteed_cwd;
|
||||||
use miette::Result;
|
use miette::Result;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::ast::PathMember;
|
use nu_protocol::ast::PathMember;
|
||||||
|
use nu_protocol::cli_error::{report_error, report_error_new};
|
||||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||||
use nu_protocol::{BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId};
|
use nu_protocol::{BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId};
|
||||||
|
|
||||||
@ -166,10 +167,10 @@ pub fn eval_hook(
|
|||||||
vars.push((var_id, val));
|
vars.push((var_id, val));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (output, err) =
|
let output =
|
||||||
parse(&mut working_set, Some("hook"), val.as_bytes(), false, &[]);
|
parse(&mut working_set, Some("hook"), val.as_bytes(), false);
|
||||||
if let Some(err) = err {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, err);
|
||||||
|
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"valid source code".into(),
|
"valid source code".into(),
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
mod history;
|
|
||||||
mod history_session;
|
|
||||||
mod tutor;
|
mod tutor;
|
||||||
|
|
||||||
pub use history::History;
|
|
||||||
pub use history_session::HistorySession;
|
|
||||||
pub use tutor::Tutor;
|
pub use tutor::Tutor;
|
||||||
|
@ -507,10 +507,7 @@ impl Command for AnsiCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("ansi")
|
Signature::build("ansi")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||||
(Type::Nothing, Type::String),
|
|
||||||
(Type::List(Box::new(Type::String)), Type::String),
|
|
||||||
])
|
|
||||||
.optional(
|
.optional(
|
||||||
"code",
|
"code",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
@ -518,12 +515,12 @@ impl Command for AnsiCommand {
|
|||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"escape", // \x1b[
|
"escape", // \x1b[
|
||||||
"escape sequence without the escape character(s)",
|
r#"escape sequence without the escape character(s) ('\x1b[' is not required)"#,
|
||||||
Some('e'),
|
Some('e'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"osc", // \x1b]
|
"osc", // \x1b]
|
||||||
"operating system command (ocs) escape sequence without the escape character(s)",
|
r#"operating system command (osc) escape sequence without the escape character(s) ('\x1b]' is not required)"#,
|
||||||
Some('o'),
|
Some('o'),
|
||||||
)
|
)
|
||||||
.switch("list", "list available ansi code names", Some('l'))
|
.switch("list", "list available ansi code names", Some('l'))
|
||||||
@ -531,58 +528,72 @@ impl Command for AnsiCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Output ANSI codes to change color."
|
"Output ANSI codes to change color and style of text."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"For escape sequences:
|
"An introduction to what ANSI escape sequences are can be found in the
|
||||||
Escape: '\x1b[' is not required for --escape parameter
|
\u{1b}]8;;https://en.wikipedia.org/wiki/ANSI_escape_code\u{1b}\\ANSI escape code\u{1b}]8;;\u{1b}\\ Wikipedia page.
|
||||||
Format: #(;#)m
|
|
||||||
Example: 1;31m for bold red or 2;37;41m for dimmed white fg with red bg
|
|
||||||
There can be multiple text formatting sequence numbers
|
|
||||||
separated by a ; and ending with an m where the # is of the
|
|
||||||
following values:
|
|
||||||
attribute_number, abbreviation, description
|
|
||||||
0 reset / normal display
|
|
||||||
1 b bold or increased intensity
|
|
||||||
2 d faint or decreased intensity
|
|
||||||
3 i italic on (non-mono font)
|
|
||||||
4 u underline on
|
|
||||||
5 l slow blink on
|
|
||||||
6 fast blink on
|
|
||||||
7 r reverse video on
|
|
||||||
8 h nondisplayed (invisible) on
|
|
||||||
9 s strike-through on
|
|
||||||
|
|
||||||
foreground/bright colors background/bright colors
|
Escape sequences usual values:
|
||||||
30/90 black 40/100 black
|
╭────┬────────────┬────────┬────────┬─────────╮
|
||||||
31/91 red 41/101 red
|
│ # │ type │ normal │ bright │ name │
|
||||||
32/92 green 42/102 green
|
├────┼────────────┼────────┼────────┼─────────┤
|
||||||
33/93 yellow 43/103 yellow
|
│ 0 │ foreground │ 30 │ 90 │ black │
|
||||||
34/94 blue 44/104 blue
|
│ 1 │ foreground │ 31 │ 91 │ red │
|
||||||
35/95 magenta 45/105 magenta
|
│ 2 │ foreground │ 32 │ 92 │ green │
|
||||||
36/96 cyan 46/106 cyan
|
│ 3 │ foreground │ 33 │ 93 │ yellow │
|
||||||
37/97 white 47/107 white
|
│ 4 │ foreground │ 34 │ 94 │ blue │
|
||||||
39 default 49 default
|
│ 5 │ foreground │ 35 │ 95 │ magenta │
|
||||||
https://en.wikipedia.org/wiki/ANSI_escape_code
|
│ 6 │ foreground │ 36 │ 96 │ cyan │
|
||||||
|
│ 7 │ foreground │ 37 │ 97 │ white │
|
||||||
|
│ 8 │ foreground │ 39 │ │ default │
|
||||||
|
│ 9 │ background │ 40 │ 100 │ black │
|
||||||
|
│ 10 │ background │ 41 │ 101 │ red │
|
||||||
|
│ 11 │ background │ 42 │ 102 │ green │
|
||||||
|
│ 12 │ background │ 43 │ 103 │ yellow │
|
||||||
|
│ 13 │ background │ 44 │ 104 │ blue │
|
||||||
|
│ 14 │ background │ 45 │ 105 │ magenta │
|
||||||
|
│ 15 │ background │ 46 │ 106 │ cyan │
|
||||||
|
│ 16 │ background │ 47 │ 107 │ white │
|
||||||
|
│ 17 │ background │ 49 │ │ default │
|
||||||
|
╰────┴────────────┴────────┴────────┴─────────╯
|
||||||
|
|
||||||
OSC: '\x1b]' is not required for --osc parameter
|
Escape sequences attributes:
|
||||||
Example: echo [(ansi -o '0') 'some title' (char bel)] | str join
|
╭───┬────┬──────────────┬──────────────────────────────╮
|
||||||
Format: #
|
│ # │ id │ abbreviation │ description │
|
||||||
0 Set window title and icon name
|
├───┼────┼──────────────┼──────────────────────────────┤
|
||||||
1 Set icon name
|
│ 0 │ 0 │ │ reset / normal display │
|
||||||
2 Set window title
|
│ 1 │ 1 │ b │ bold or increased intensity │
|
||||||
4 Set/read color palette
|
│ 2 │ 2 │ d │ faint or decreased intensity │
|
||||||
9 iTerm2 Grown notifications
|
│ 3 │ 3 │ i │ italic on (non-mono font) │
|
||||||
10 Set foreground color (x11 color spec)
|
│ 4 │ 4 │ u │ underline on │
|
||||||
11 Set background color (x11 color spec)
|
│ 5 │ 5 │ l │ slow blink on │
|
||||||
... others"#
|
│ 6 │ 6 │ │ fast blink on │
|
||||||
|
│ 7 │ 7 │ r │ reverse video on │
|
||||||
|
│ 8 │ 8 │ h │ nondisplayed (invisible) on │
|
||||||
|
│ 9 │ 9 │ s │ strike-through on │
|
||||||
|
╰───┴────┴──────────────┴──────────────────────────────╯
|
||||||
|
|
||||||
|
Operating system commands:
|
||||||
|
╭───┬─────┬───────────────────────────────────────╮
|
||||||
|
│ # │ id │ description │
|
||||||
|
├───┼─────┼───────────────────────────────────────┤
|
||||||
|
│ 0 │ 0 │ Set window title and icon name │
|
||||||
|
│ 1 │ 1 │ Set icon name │
|
||||||
|
│ 2 │ 2 │ Set window title │
|
||||||
|
│ 3 │ 4 │ Set/read color palette │
|
||||||
|
│ 4 │ 9 │ iTerm2 Grown notifications │
|
||||||
|
│ 5 │ 10 │ Set foreground color (x11 color spec) │
|
||||||
|
│ 6 │ 11 │ Set background color (x11 color spec) │
|
||||||
|
│ 7 │ ... │ others │
|
||||||
|
╰───┴─────┴───────────────────────────────────────╯"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Change color to green",
|
description: "Change color to green (see how the next example text will be green!)",
|
||||||
example: r#"ansi green"#,
|
example: r#"ansi green"#,
|
||||||
result: Some(Value::test_string("\u{1b}[32m")),
|
result: Some(Value::test_string("\u{1b}[32m")),
|
||||||
},
|
},
|
||||||
@ -592,23 +603,32 @@ Format: #
|
|||||||
result: Some(Value::test_string("\u{1b}[0m")),
|
result: Some(Value::test_string("\u{1b}[0m")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Use different colors and styles in the same text",
|
||||||
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
example: r#"$'(ansi red_bold)Hello(ansi reset) (ansi green_dimmed)Nu(ansi reset) (ansi purple_italic)World(ansi reset)'"#,
|
||||||
example: r#"$'(ansi rb)Hello (ansi gb)Nu (ansi pb)World(ansi reset)'"#,
|
|
||||||
result: Some(Value::test_string(
|
result: Some(Value::test_string(
|
||||||
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m",
|
"\u{1b}[1;31mHello\u{1b}[0m \u{1b}[2;32mNu\u{1b}[0m \u{1b}[3;35mWorld\u{1b}[0m",
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Use ansi to color text (italic bright yellow on red 'Hello' with green bold 'Nu' and purple bold 'World')",
|
description: "The same example as above with short names",
|
||||||
example: r#"[(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World (ansi reset)] | str join"#,
|
example: r#"$'(ansi rb)Hello(ansi reset) (ansi gd)Nu(ansi reset) (ansi pi)World(ansi reset)'"#,
|
||||||
result: Some(Value::test_string(
|
result: Some(Value::test_string(
|
||||||
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m",
|
"\u{1b}[1;31mHello\u{1b}[0m \u{1b}[2;32mNu\u{1b}[0m \u{1b}[3;35mWorld\u{1b}[0m",
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Use ansi to color text with a style (blue on red in bold)",
|
description: "Use escape codes, without the '\\x1b['",
|
||||||
example: r#"$"(ansi -e { fg: '#0000ff' bg: '#ff0000' attr: b })Hello Nu World(ansi reset)""#,
|
example: r#"$"(ansi -e '3;93;41m')Hello(ansi reset)" # italic bright yellow on red background"#,
|
||||||
|
result: Some(Value::test_string("\u{1b}[3;93;41mHello\u{1b}[0m")),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Use structured escape codes",
|
||||||
|
example: r#"let bold_blue_on_red = { # `fg`, `bg`, `attr` are the acceptable keys, all other keys are considered invalid and will throw errors.
|
||||||
|
fg: '#0000ff'
|
||||||
|
bg: '#ff0000'
|
||||||
|
attr: b
|
||||||
|
}
|
||||||
|
$"(ansi -e $bold_blue_on_red)Hello Nu World(ansi reset)""#,
|
||||||
result: Some(Value::test_string(
|
result: Some(Value::test_string(
|
||||||
"\u{1b}[1;48;2;255;0;0;38;2;0;0;255mHello Nu World\u{1b}[0m",
|
"\u{1b}[1;48;2;255;0;0;38;2;0;0;255mHello Nu World\u{1b}[0m",
|
||||||
)),
|
)),
|
||||||
@ -745,7 +765,7 @@ Format: #
|
|||||||
"attr" => nu_style.attr = Some(v.as_string()?),
|
"attr" => nu_style.attr = Some(v.as_string()?),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::IncompatibleParametersSingle {
|
return Err(ShellError::IncompatibleParametersSingle {
|
||||||
msg: format!("problem with key: {k}"),
|
msg: format!("unknown ANSI format key: expected one of ['fg', 'bg', 'attr'], found '{k}'"),
|
||||||
span: code.expect_span(),
|
span: code.expect_span(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -72,25 +72,25 @@ impl Command for SubCommand {
|
|||||||
Example {
|
Example {
|
||||||
description: "draw text in a gradient with foreground start and end colors",
|
description: "draw text in a gradient with foreground start and end colors",
|
||||||
example:
|
example:
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff --fgend 0xe81cff",
|
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
||||||
example:
|
example:
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff --fgend 0xe81cff --bgstart 0xe81cff --bgend 0x40c9ff",
|
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff' --bgstart '0xe81cff' --bgend '0x40c9ff'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
||||||
example:
|
example:
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff",
|
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
||||||
example:
|
example:
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgend 0xe81cff",
|
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgend '0xe81cff'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -4,7 +4,6 @@ mod dir_info;
|
|||||||
mod du;
|
mod du;
|
||||||
mod input;
|
mod input;
|
||||||
mod kill;
|
mod kill;
|
||||||
mod reedline_commands;
|
|
||||||
mod sleep;
|
mod sleep;
|
||||||
mod term_size;
|
mod term_size;
|
||||||
|
|
||||||
@ -14,6 +13,5 @@ pub use dir_info::{DirBuilder, DirInfo, FileInfo};
|
|||||||
pub use du::Du;
|
pub use du::Du;
|
||||||
pub use input::Input;
|
pub use input::Input;
|
||||||
pub use kill::Kill;
|
pub use kill::Kill;
|
||||||
pub use reedline_commands::{Keybindings, KeybindingsDefault, KeybindingsList, KeybindingsListen};
|
|
||||||
pub use sleep::Sleep;
|
pub use sleep::Sleep;
|
||||||
pub use term_size::TermSize;
|
pub use term_size::TermSize;
|
||||||
|
@ -197,7 +197,7 @@ impl Command for Char {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Output Unicode character",
|
description: "Output Unicode character",
|
||||||
example: r#"char -u '1f378'"#,
|
example: r#"char -u 1f378"#,
|
||||||
result: Some(Value::test_string("\u{1f378}")),
|
result: Some(Value::test_string("\u{1f378}")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -207,7 +207,7 @@ impl Command for Char {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Output multi-byte Unicode character",
|
description: "Output multi-byte Unicode character",
|
||||||
example: r#"char -u '1F468' '200D' '1F466' '200D' '1F466'"#,
|
example: r#"char -u 1F468 200D 1F466 200D 1F466"#,
|
||||||
result: Some(Value::test_string(
|
result: Some(Value::test_string(
|
||||||
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
|
"\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}",
|
||||||
)),
|
)),
|
||||||
|
@ -290,8 +290,8 @@ fn format_record(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FormatOperation::ValueNeedEval(_col_name, span) => {
|
FormatOperation::ValueNeedEval(_col_name, span) => {
|
||||||
let (exp, may_parse_err) = parse_expression(working_set, &[*span], &[], false);
|
let exp = parse_expression(working_set, &[*span], false);
|
||||||
match may_parse_err {
|
match working_set.parse_errors.first() {
|
||||||
None => {
|
None => {
|
||||||
let parsed_result = eval_expression(engine_state, stack, &exp);
|
let parsed_result = eval_expression(engine_state, stack, &exp);
|
||||||
if let Ok(val) = parsed_result {
|
if let Ok(val) = parsed_result {
|
||||||
|
@ -5,6 +5,7 @@ use nu_protocol::{
|
|||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
@ -30,6 +31,7 @@ impl Command for SubCommand {
|
|||||||
"the character or string that denotes what separates columns",
|
"the character or string that denotes what separates columns",
|
||||||
)
|
)
|
||||||
.switch("collapse-empty", "remove empty columns", Some('c'))
|
.switch("collapse-empty", "remove empty columns", Some('c'))
|
||||||
|
.switch("regex", "separator is a regular expression", Some('r'))
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
@ -117,6 +119,25 @@ impl Command for SubCommand {
|
|||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Split a list of strings into a table, ignoring padding",
|
||||||
|
example: r"['a - b' 'c - d'] | split column -r '\s*-\s*'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||||
|
vals: vec![Value::test_string("a"), Value::test_string("b")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["column1".to_string(), "column2".to_string()],
|
||||||
|
vals: vec![Value::test_string("c"), Value::test_string("d")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,30 +153,43 @@ fn split_column(
|
|||||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||||
let collapse_empty = call.has_flag("collapse-empty");
|
let collapse_empty = call.has_flag("collapse-empty");
|
||||||
|
|
||||||
|
let regex = if call.has_flag("regex") {
|
||||||
|
Regex::new(&separator.item)
|
||||||
|
} else {
|
||||||
|
let escaped = regex::escape(&separator.item);
|
||||||
|
Regex::new(&escaped)
|
||||||
|
}
|
||||||
|
.map_err(|err| {
|
||||||
|
ShellError::GenericError(
|
||||||
|
"Error with regular expression".into(),
|
||||||
|
err.to_string(),
|
||||||
|
Some(separator.span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
input.flat_map(
|
input.flat_map(
|
||||||
move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span),
|
move |x| split_column_helper(&x, ®ex, &rest, collapse_empty, name_span),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_column_helper(
|
fn split_column_helper(
|
||||||
v: &Value,
|
v: &Value,
|
||||||
separator: &Spanned<String>,
|
separator: &Regex,
|
||||||
rest: &[Spanned<String>],
|
rest: &[Spanned<String>],
|
||||||
collapse_empty: bool,
|
collapse_empty: bool,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Vec<Value> {
|
) -> Vec<Value> {
|
||||||
if let Ok(s) = v.as_string() {
|
if let Ok(s) = v.as_string() {
|
||||||
let split_result: Vec<_> = if collapse_empty {
|
let split_result: Vec<_> = separator
|
||||||
s.split(&separator.item).filter(|s| !s.is_empty()).collect()
|
.split(&s)
|
||||||
} else {
|
.filter(|x| !(collapse_empty && x.is_empty()))
|
||||||
s.split(&separator.item).collect()
|
.collect();
|
||||||
};
|
|
||||||
|
|
||||||
let positional: Vec<_> = rest.iter().map(|f| f.item.clone()).collect();
|
let positional: Vec<_> = rest.iter().map(|f| f.item.clone()).collect();
|
||||||
|
|
||||||
// If they didn't provide column names, make up our own
|
// If they didn't provide column names, make up our own
|
||||||
|
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
if positional.is_empty() {
|
if positional.is_empty() {
|
||||||
|
@ -5,6 +5,7 @@ use nu_protocol::{
|
|||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Type, Value,
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
@ -25,6 +26,10 @@ impl Command for SubCommand {
|
|||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"the value that denotes what separates the list",
|
"the value that denotes what separates the list",
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"regex",
|
||||||
|
"separator is a regular expression, matching values that can be coerced into a string",
|
||||||
|
Some('r'))
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,10 +126,76 @@ impl Command for SubCommand {
|
|||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Split a list of chars into lists based on multiple characters",
|
||||||
|
example: r"[a, b, c, d, a, e, f, g] | split list -r '(b|e)'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::List {
|
||||||
|
vals: vec![Value::test_string("a")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_string("c"),
|
||||||
|
Value::test_string("d"),
|
||||||
|
Value::test_string("a"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::List {
|
||||||
|
vals: vec![Value::test_string("f"), Value::test_string("g")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Matcher {
|
||||||
|
Regex(Regex),
|
||||||
|
Direct(Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matcher {
|
||||||
|
pub fn new(regex: bool, lhs: Value) -> Result<Self, ShellError> {
|
||||||
|
if regex {
|
||||||
|
Ok(Matcher::Regex(Regex::new(&lhs.as_string()?).map_err(
|
||||||
|
|err| {
|
||||||
|
ShellError::GenericError(
|
||||||
|
"Error with regular expression".into(),
|
||||||
|
err.to_string(),
|
||||||
|
match lhs {
|
||||||
|
Value::Error { error: _ } => None,
|
||||||
|
_ => Some(lhs.expect_span()),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
Ok(Matcher::Direct(lhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(&self, rhs: &Value) -> Result<bool, ShellError> {
|
||||||
|
Ok(match self {
|
||||||
|
Matcher::Regex(regex) => {
|
||||||
|
if let Ok(rhs_str) = rhs.as_string() {
|
||||||
|
regex.is_match(&rhs_str)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Matcher::Direct(lhs) => rhs == lhs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn split_list(
|
fn split_list(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
@ -134,9 +205,11 @@ fn split_list(
|
|||||||
let separator: Value = call.req(engine_state, stack, 0)?;
|
let separator: Value = call.req(engine_state, stack, 0)?;
|
||||||
let mut temp_list = Vec::new();
|
let mut temp_list = Vec::new();
|
||||||
let mut returned_list = Vec::new();
|
let mut returned_list = Vec::new();
|
||||||
|
|
||||||
let iter = input.into_interruptible_iter(engine_state.ctrlc.clone());
|
let iter = input.into_interruptible_iter(engine_state.ctrlc.clone());
|
||||||
|
let matcher = Matcher::new(call.has_flag("regex"), separator)?;
|
||||||
for val in iter {
|
for val in iter {
|
||||||
if val == separator {
|
if matcher.compare(&val)? {
|
||||||
if !temp_list.is_empty() {
|
if !temp_list.is_empty() {
|
||||||
returned_list.push(Value::List {
|
returned_list.push(Value::List {
|
||||||
vals: temp_list.clone(),
|
vals: temp_list.clone(),
|
||||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
|||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
use regex::Regex;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ impl Command for SubCommand {
|
|||||||
.required(
|
.required(
|
||||||
"separator",
|
"separator",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
"the character that denotes what separates rows",
|
"a character or regex that denotes what separates rows",
|
||||||
)
|
)
|
||||||
.named(
|
.named(
|
||||||
"number",
|
"number",
|
||||||
@ -29,6 +29,7 @@ impl Command for SubCommand {
|
|||||||
"Split into maximum number of items",
|
"Split into maximum number of items",
|
||||||
Some('n'),
|
Some('n'),
|
||||||
)
|
)
|
||||||
|
.switch("regex", "use regex syntax for separator", Some('r'))
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +93,18 @@ impl Command for SubCommand {
|
|||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Split a string by regex",
|
||||||
|
example: r"'a b c' | split row -r '\s+'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_string("a"),
|
||||||
|
Value::test_string("b"),
|
||||||
|
Value::test_string("c"),
|
||||||
|
],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,30 +117,40 @@ fn split_row(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name_span = call.head;
|
let name_span = call.head;
|
||||||
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
let regex = if call.has_flag("regex") {
|
||||||
|
Regex::new(&separator.item)
|
||||||
|
} else {
|
||||||
|
let escaped = regex::escape(&separator.item);
|
||||||
|
Regex::new(&escaped)
|
||||||
|
}
|
||||||
|
.map_err(|err| {
|
||||||
|
ShellError::GenericError(
|
||||||
|
"Error with regular expression".into(),
|
||||||
|
err.to_string(),
|
||||||
|
Some(separator.span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
|
||||||
input.flat_map(
|
input.flat_map(
|
||||||
move |x| split_row_helper(&x, &separator, max_split, name_span),
|
move |x| split_row_helper(&x, ®ex, max_split, name_span),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_row_helper(
|
fn split_row_helper(v: &Value, regex: &Regex, max_split: Option<usize>, name: Span) -> Vec<Value> {
|
||||||
v: &Value,
|
|
||||||
separator: &Spanned<String>,
|
|
||||||
max_split: Option<usize>,
|
|
||||||
name: Span,
|
|
||||||
) -> Vec<Value> {
|
|
||||||
match v.span() {
|
match v.span() {
|
||||||
Ok(v_span) => {
|
Ok(v_span) => {
|
||||||
if let Ok(s) = v.as_string() {
|
if let Ok(s) = v.as_string() {
|
||||||
match max_split {
|
match max_split {
|
||||||
Some(max_split) => s
|
Some(max_split) => regex
|
||||||
.splitn(max_split, &separator.item)
|
.splitn(&s, max_split)
|
||||||
.map(|s| Value::string(s, v_span))
|
.map(|x: &str| Value::string(x, v_span))
|
||||||
.collect(),
|
.collect(),
|
||||||
None => s
|
None => regex
|
||||||
.split(&separator.item)
|
.split(&s)
|
||||||
.map(|s| Value::string(s, v_span))
|
.map(|x: &str| Value::string(x, v_span))
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
use crate::grapheme_flags;
|
|
||||||
use crate::input_handler::{operate, CmdArgument};
|
use crate::input_handler::{operate, CmdArgument};
|
||||||
|
use crate::{grapheme_flags, util};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::{
|
||||||
use nu_protocol::ast::CellPath;
|
ast::{Call, CellPath},
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
engine::{Command, EngineState, Stack},
|
||||||
use nu_protocol::Category;
|
Category, Example, PipelineData, Range, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||||
use nu_protocol::Spanned;
|
Type, Value,
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
end: bool,
|
end: bool,
|
||||||
substring: String,
|
substring: String,
|
||||||
range: Option<Value>,
|
range: Option<Range>,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
cell_paths: Option<Vec<CellPath>>,
|
||||||
graphemes: bool,
|
graphemes: bool,
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ impl Command for SubCommand {
|
|||||||
)
|
)
|
||||||
.named(
|
.named(
|
||||||
"range",
|
"range",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Range,
|
||||||
"optional start and/or end index",
|
"optional start and/or end index",
|
||||||
Some('r'),
|
Some('r'),
|
||||||
)
|
)
|
||||||
@ -105,23 +105,18 @@ impl Command for SubCommand {
|
|||||||
result: Some(Value::test_int(4)),
|
result: Some(Value::test_int(4)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns index of string in input with start index",
|
description: "Returns index of string in input within a`rhs open range`",
|
||||||
example: " '.rb.rb' | str index-of '.rb' -r '1,'",
|
example: " '.rb.rb' | str index-of '.rb' -r 1..",
|
||||||
result: Some(Value::test_int(3)),
|
result: Some(Value::test_int(3)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns index of string in input with end index",
|
description: "Returns index of string in input within a lhs open range",
|
||||||
example: " '123456' | str index-of '6' -r ',4'",
|
example: " '123456' | str index-of '6' -r ..4",
|
||||||
result: Some(Value::test_int(-1)),
|
result: Some(Value::test_int(-1)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Returns index of string in input with start and end index",
|
description: "Returns index of string in input within a range",
|
||||||
example: " '123456' | str index-of '3' -r '1,4'",
|
example: " '123456' | str index-of '3' -r 1..4",
|
||||||
result: Some(Value::test_int(2)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Alternatively you can use this form",
|
|
||||||
example: " '123456' | str index-of '3' -r [1 4]",
|
|
||||||
result: Some(Value::test_int(2)),
|
result: Some(Value::test_int(2)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -144,18 +139,29 @@ fn action(
|
|||||||
}: &Arguments,
|
}: &Arguments,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
let range = match range {
|
|
||||||
Some(range) => range.clone(),
|
|
||||||
None => Value::string("", head),
|
|
||||||
};
|
|
||||||
|
|
||||||
let r = process_range(input, &range, head);
|
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::String { val: s, .. } => {
|
Value::String { val: s, .. } => {
|
||||||
let (start_index, end_index) = match r {
|
let (start_index, end_index) = if let Some(range) = range {
|
||||||
Ok(r) => (r.0 as usize, r.1 as usize),
|
match util::process_range(range) {
|
||||||
Err(e) => return Value::Error { error: Box::new(e) },
|
Ok(r) => {
|
||||||
|
// `process_range()` returns `isize::MAX` if the range is open-ended,
|
||||||
|
// which is not ideal for us
|
||||||
|
let end = if r.1 as usize > s.len() {
|
||||||
|
s.len()
|
||||||
|
} else {
|
||||||
|
r.1 as usize
|
||||||
|
};
|
||||||
|
(r.0 as usize, end)
|
||||||
|
}
|
||||||
|
Err(processing_error) => {
|
||||||
|
let err = processing_error("could not find `index-of`", head);
|
||||||
|
return Value::Error {
|
||||||
|
error: Box::new(err),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(0usize, s.len())
|
||||||
};
|
};
|
||||||
|
|
||||||
// When the -e flag is present, search using rfind instead of find.s
|
// When the -e flag is present, search using rfind instead of find.s
|
||||||
@ -196,73 +202,10 @@ fn action(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_range(
|
|
||||||
input: &Value,
|
|
||||||
range: &Value,
|
|
||||||
head: Span,
|
|
||||||
) -> Result<IndexOfOptionalBounds, ShellError> {
|
|
||||||
let input_len = match input {
|
|
||||||
Value::String { val: s, .. } => s.len(),
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
let min_index_str = String::from("0");
|
|
||||||
let max_index_str = input_len.to_string();
|
|
||||||
let r = match range {
|
|
||||||
Value::String { val: s, .. } => {
|
|
||||||
let indexes: Vec<&str> = s.split(',').collect();
|
|
||||||
|
|
||||||
let start_index = indexes.first().unwrap_or(&&min_index_str[..]).to_string();
|
|
||||||
|
|
||||||
let end_index = indexes.get(1).unwrap_or(&&max_index_str[..]).to_string();
|
|
||||||
|
|
||||||
Ok((start_index, end_index))
|
|
||||||
}
|
|
||||||
Value::List { vals, .. } => {
|
|
||||||
if vals.len() > 2 {
|
|
||||||
Err(ShellError::TypeMismatch {
|
|
||||||
err_message: String::from("there shouldn't be more than two indexes"),
|
|
||||||
span: head,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let idx: Vec<String> = vals
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.as_string().unwrap_or_else(|_| String::from("")))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let start_index = idx.get(0).unwrap_or(&min_index_str).to_string();
|
|
||||||
let end_index = idx.get(1).unwrap_or(&max_index_str).to_string();
|
|
||||||
|
|
||||||
Ok((start_index, end_index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Error { error } => Err(*error.clone()),
|
|
||||||
_ => Err(ShellError::OnlySupportsThisInputType {
|
|
||||||
exp_input_type: "string".into(),
|
|
||||||
wrong_type: input.get_type().to_string(),
|
|
||||||
dst_span: head,
|
|
||||||
src_span: input.expect_span(),
|
|
||||||
}),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let start_index = r.0.parse::<i32>().unwrap_or(0);
|
|
||||||
let end_index = r.1.parse::<i32>().unwrap_or(input_len as i32);
|
|
||||||
|
|
||||||
if start_index < 0 || start_index > end_index {
|
|
||||||
return Err(ShellError::TypeMismatch {
|
|
||||||
err_message: String::from("start index can't be negative or greater than end index"),
|
|
||||||
span: head,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if end_index < 0 || end_index < start_index || end_index > input_len as i32 {
|
|
||||||
return Err(ShellError::TypeMismatch { err_message: String::from(
|
|
||||||
"end index can't be negative, smaller than start index or greater than input length"), span: head });
|
|
||||||
}
|
|
||||||
Ok(IndexOfOptionalBounds(start_index, end_index))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use nu_protocol::ast::RangeInclusion;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::{action, Arguments, SubCommand};
|
use super::{action, Arguments, SubCommand};
|
||||||
|
|
||||||
@ -279,11 +222,7 @@ mod tests {
|
|||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from(".tomL"),
|
substring: String::from(".tomL"),
|
||||||
|
range: None,
|
||||||
range: Some(Value::String {
|
|
||||||
val: String::from(""),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
@ -300,10 +239,7 @@ mod tests {
|
|||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from("Lm"),
|
substring: String::from("Lm"),
|
||||||
|
|
||||||
range: Some(Value::String {
|
range: None,
|
||||||
val: String::from(""),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
@ -317,14 +253,25 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn returns_index_of_next_substring() {
|
fn returns_index_of_next_substring() {
|
||||||
let word = Value::test_string("Cargo.Cargo");
|
let word = Value::test_string("Cargo.Cargo");
|
||||||
|
let range = Range {
|
||||||
|
from: Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
incr: Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
to: Value::Nothing {
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
inclusion: RangeInclusion::Inclusive,
|
||||||
|
};
|
||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from("Cargo"),
|
substring: String::from("Cargo"),
|
||||||
|
|
||||||
range: Some(Value::String {
|
range: Some(range),
|
||||||
val: String::from("1"),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
@ -337,14 +284,25 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn index_does_not_exist_due_to_end_index() {
|
fn index_does_not_exist_due_to_end_index() {
|
||||||
let word = Value::test_string("Cargo.Banana");
|
let word = Value::test_string("Cargo.Banana");
|
||||||
|
let range = Range {
|
||||||
|
from: Value::Nothing {
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
inclusion: RangeInclusion::Inclusive,
|
||||||
|
incr: Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
to: Value::Int {
|
||||||
|
val: 5,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from("Banana"),
|
substring: String::from("Banana"),
|
||||||
|
|
||||||
range: Some(Value::String {
|
range: Some(range),
|
||||||
val: String::from(",5"),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
@ -357,14 +315,26 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn returns_index_of_nums_in_middle_due_to_index_limit_from_both_ends() {
|
fn returns_index_of_nums_in_middle_due_to_index_limit_from_both_ends() {
|
||||||
let word = Value::test_string("123123123");
|
let word = Value::test_string("123123123");
|
||||||
|
let range = Range {
|
||||||
|
from: Value::Int {
|
||||||
|
val: 2,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
incr: Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
to: Value::Int {
|
||||||
|
val: 6,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
inclusion: RangeInclusion::Inclusive,
|
||||||
|
};
|
||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from("123"),
|
substring: String::from("123"),
|
||||||
|
|
||||||
range: Some(Value::String {
|
range: Some(range),
|
||||||
val: String::from("2,6"),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
@ -377,14 +347,26 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn index_does_not_exists_due_to_strict_bounds() {
|
fn index_does_not_exists_due_to_strict_bounds() {
|
||||||
let word = Value::test_string("123456");
|
let word = Value::test_string("123456");
|
||||||
|
let range = Range {
|
||||||
|
from: Value::Int {
|
||||||
|
val: 2,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
incr: Value::Int {
|
||||||
|
val: 1,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
to: Value::Int {
|
||||||
|
val: 5,
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
inclusion: RangeInclusion::RightExclusive,
|
||||||
|
};
|
||||||
|
|
||||||
let options = Arguments {
|
let options = Arguments {
|
||||||
substring: String::from("1"),
|
substring: String::from("1"),
|
||||||
|
|
||||||
range: Some(Value::String {
|
range: Some(range),
|
||||||
val: String::from("2,4"),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
cell_paths: None,
|
cell_paths: None,
|
||||||
end: false,
|
end: false,
|
||||||
graphemes: false,
|
graphemes: false,
|
||||||
|
@ -81,6 +81,7 @@ fn exec(
|
|||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
|
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
|
||||||
command.current_dir(cwd);
|
command.current_dir(cwd);
|
||||||
|
command.envs(&external_command.env_vars);
|
||||||
|
|
||||||
let err = command.exec(); // this replaces our process, should not return
|
let err = command.exec(); // this replaces our process, should not return
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{find_in_dirs_env, CallExt};
|
use nu_engine::{find_in_dirs_env, get_dirs_var_from_call, CallExt};
|
||||||
use nu_parser::{parse, parse_module_block, unescape_unquote_string};
|
use nu_parser::{parse, parse_module_block, unescape_unquote_string};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
||||||
@ -106,7 +106,12 @@ impl Command for NuCheck {
|
|||||||
_ => {
|
_ => {
|
||||||
if let Some(path_str) = path {
|
if let Some(path_str) = path {
|
||||||
// look up the path as relative to FILE_PWD or inside NU_LIB_DIRS (same process as source-env)
|
// look up the path as relative to FILE_PWD or inside NU_LIB_DIRS (same process as source-env)
|
||||||
let path = match find_in_dirs_env(&path_str.item, engine_state, stack) {
|
let path = match find_in_dirs_env(
|
||||||
|
&path_str.item,
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
get_dirs_var_from_call(call),
|
||||||
|
) {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
path
|
path
|
||||||
@ -255,9 +260,14 @@ fn heuristic_parse_file(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
if err.is_none() {
|
let bytes = working_set.get_span_contents(call.head);
|
||||||
if let Ok(contents) = std::fs::read(&path) {
|
let (filename, err) = unescape_unquote_string(bytes, call.head);
|
||||||
|
if let Some(err) = err {
|
||||||
|
working_set.error(err);
|
||||||
|
}
|
||||||
|
if starting_error_count == working_set.parse_errors.len() {
|
||||||
|
if let Ok(contents) = std::fs::read(path) {
|
||||||
match parse_script(
|
match parse_script(
|
||||||
working_set,
|
working_set,
|
||||||
Some(filename.as_str()),
|
Some(filename.as_str()),
|
||||||
@ -304,18 +314,20 @@ fn parse_module(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let filename = filename.unwrap_or_else(|| "empty".to_string());
|
let filename = filename.unwrap_or_else(|| "empty".to_string());
|
||||||
|
|
||||||
let start = working_set.next_span_start();
|
let file_id = working_set.add_file(filename.clone(), contents);
|
||||||
working_set.add_file(filename.clone(), contents);
|
let new_span = working_set.get_span_for_file(file_id);
|
||||||
let end = working_set.next_span_start();
|
|
||||||
|
|
||||||
let new_span = Span::new(start, end);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
let (_, _, _, err) = parse_module_block(working_set, new_span, filename.as_bytes(), &[]);
|
parse_module_block(working_set, new_span, filename.as_bytes());
|
||||||
|
|
||||||
if err.is_some() {
|
if starting_error_count != working_set.parse_errors.len() {
|
||||||
if is_debug {
|
if is_debug {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
r#"Found : {}"#,
|
r#"Found : {}"#,
|
||||||
err.expect("Unable to parse content as module")
|
working_set
|
||||||
|
.parse_errors
|
||||||
|
.first()
|
||||||
|
.expect("Unable to parse content as module")
|
||||||
);
|
);
|
||||||
Err(ShellError::GenericError(
|
Err(ShellError::GenericError(
|
||||||
"Failed to parse content".to_string(),
|
"Failed to parse content".to_string(),
|
||||||
@ -339,9 +351,16 @@ fn parse_script(
|
|||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (_, err) = parse(working_set, filename, contents, false, &[]);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
if err.is_some() {
|
parse(working_set, filename, contents, false);
|
||||||
let msg = format!(r#"Found : {}"#, err.expect("Unable to parse content"));
|
if starting_error_count != working_set.parse_errors.len() {
|
||||||
|
let msg = format!(
|
||||||
|
r#"Found : {}"#,
|
||||||
|
working_set
|
||||||
|
.parse_errors
|
||||||
|
.first()
|
||||||
|
.expect("Unable to parse content")
|
||||||
|
);
|
||||||
if is_debug {
|
if is_debug {
|
||||||
Err(ShellError::GenericError(
|
Err(ShellError::GenericError(
|
||||||
"Failed to parse content".to_string(),
|
"Failed to parse content".to_string(),
|
||||||
@ -364,9 +383,14 @@ fn parse_file_script(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
if err.is_none() {
|
let bytes = working_set.get_span_contents(call.head);
|
||||||
if let Ok(contents) = std::fs::read(&path) {
|
let (filename, err) = unescape_unquote_string(bytes, call.head);
|
||||||
|
if let Some(err) = err {
|
||||||
|
working_set.error(err)
|
||||||
|
}
|
||||||
|
if starting_error_count == working_set.parse_errors.len() {
|
||||||
|
if let Ok(contents) = std::fs::read(path) {
|
||||||
parse_script(
|
parse_script(
|
||||||
working_set,
|
working_set,
|
||||||
Some(filename.as_str()),
|
Some(filename.as_str()),
|
||||||
@ -388,8 +412,13 @@ fn parse_file_module(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
let starting_error_count = working_set.parse_errors.len();
|
||||||
if err.is_none() {
|
let bytes = working_set.get_span_contents(call.head);
|
||||||
|
let (filename, err) = unescape_unquote_string(bytes, call.head);
|
||||||
|
if let Some(err) = err {
|
||||||
|
working_set.error(err);
|
||||||
|
}
|
||||||
|
if starting_error_count == working_set.parse_errors.len() {
|
||||||
if let Ok(contents) = std::fs::read(path) {
|
if let Ok(contents) = std::fs::read(path) {
|
||||||
parse_module(working_set, Some(filename), &contents, is_debug, call.head)
|
parse_module(working_set, Some(filename), &contents, is_debug, call.head)
|
||||||
} else {
|
} else {
|
||||||
|
@ -67,6 +67,11 @@ impl Command for Ps {
|
|||||||
example: "ps | where name =~ 'nu'",
|
example: "ps | where name =~ 'nu'",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Get the parent process id of the current nu process",
|
||||||
|
example: "ps | where pid == $nu.pid | get ppid",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +91,12 @@ fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, Shell
|
|||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cols.push("ppid".to_string());
|
||||||
|
vals.push(Value::Int {
|
||||||
|
val: proc.ppid() as i64,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
|
||||||
cols.push("name".to_string());
|
cols.push("name".to_string());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
val: proc.name(),
|
val: proc.name(),
|
||||||
|
@ -42,7 +42,7 @@ impl Command for External {
|
|||||||
.switch("redirect-stdout", "redirect stdout to the pipeline", None)
|
.switch("redirect-stdout", "redirect stdout to the pipeline", None)
|
||||||
.switch("redirect-stderr", "redirect stderr to the pipeline", None)
|
.switch("redirect-stderr", "redirect stderr to the pipeline", None)
|
||||||
.switch("trim-end-newline", "trimming end newlines", None)
|
.switch("trim-end-newline", "trimming end newlines", None)
|
||||||
.required("command", SyntaxShape::Any, "external command to run")
|
.required("command", SyntaxShape::String, "external command to run")
|
||||||
.rest("args", SyntaxShape::Any, "arguments for external command")
|
.rest("args", SyntaxShape::Any, "arguments for external command")
|
||||||
.category(Category::System)
|
.category(Category::System)
|
||||||
}
|
}
|
||||||
@ -312,7 +312,12 @@ impl ExternalCommand {
|
|||||||
format!("command '{cmd_name}' was not found but it exists in module '{module_name}'; try importing it with `use`")
|
format!("command '{cmd_name}' was not found but it exists in module '{module_name}'; try importing it with `use`")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("did you mean '{s}'?")
|
// If command and suggestion are the same, display not found
|
||||||
|
if cmd_name == &s {
|
||||||
|
format!("'{cmd_name}' was not found")
|
||||||
|
} else {
|
||||||
|
format!("did you mean '{s}'?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -543,7 +548,7 @@ impl ExternalCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_process(
|
pub fn create_process(
|
||||||
&self,
|
&self,
|
||||||
input: &PipelineData,
|
input: &PipelineData,
|
||||||
use_cmd: bool,
|
use_cmd: bool,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user