mirror of
https://github.com/nushell/nushell.git
synced 2025-07-02 23:51:49 +02:00
Compare commits
156 Commits
Author | SHA1 | Date | |
---|---|---|---|
3b220e07e3 | |||
16799a1d78 | |||
38bf3f6e1b | |||
e83123dcca | |||
648486400c | |||
59ee96c70d | |||
365f5954ee | |||
24ecb84d97 | |||
0c4d5330ee | |||
884d5312bb | |||
76d1d70e83 | |||
02de69de92 | |||
533603b72c | |||
1ecbb3e09f | |||
d23a3737c0 | |||
822c434c12 | |||
d126793290 | |||
adf38c7c76 | |||
79ebf0c0a9 | |||
f234a0ea33 | |||
2466a39574 | |||
1239e86320 | |||
5b0546cfce | |||
b6d765a2d8 | |||
530162b4c4 | |||
25cbcb511d | |||
9996e4a1f8 | |||
c52884b3c8 | |||
0f645b3bb6 | |||
1e453020b6 | |||
06c72df672 | |||
9d65c47313 | |||
b576123b0a | |||
1633004643 | |||
fbb4fad7ac | |||
c9bc0c7d3e | |||
eec8645b9c | |||
1f4131532d | |||
5c7f7883c8 | |||
b0acc1d890 | |||
bed236362a | |||
797a90520c | |||
20bf3c587f | |||
bae6d694ca | |||
83720a9f30 | |||
a60381a932 | |||
aad3ac11da | |||
8b7696f4c1 | |||
a900166e27 | |||
2595f31541 | |||
6cba7c6b40 | |||
3b1d405b96 | |||
5fd34320e9 | |||
5e52bd77e0 | |||
cf8fcef9bf | |||
47867a58df | |||
b274ec19fd | |||
be5ed3290c | |||
187b87c61c | |||
f2169c8022 | |||
9fb59a6f43 | |||
55edef5dda | |||
fac2f43aa4 | |||
6d2cb4382a | |||
351bff8233 | |||
7fe2e60af7 | |||
999dfdf936 | |||
cc7b5c5a26 | |||
9a265847e2 | |||
b088f395dc | |||
6ccd547d81 | |||
57b0c722c6 | |||
13160b3ec3 | |||
410f3c5c8a | |||
b296d6ee3c | |||
9a739d9f0d | |||
8cba6678b3 | |||
055aae9f5c | |||
a67dad3d15 | |||
43814dcb0f | |||
cc781a1ecd | |||
a7a5ec31be | |||
48e4448e55 | |||
e6fbf7d01d | |||
62555c997e | |||
03317ff92e | |||
1661bb68f9 | |||
c9e9b138eb | |||
5f818eaefe | |||
078ba5aabe | |||
c06ef201b7 | |||
67e7eec7da | |||
af72a18785 | |||
50b2dac8d7 | |||
2ae9ad8676 | |||
b508d1028c | |||
040f10e19d | |||
10a9a17b8c | |||
b9dd47ebb7 | |||
211d9c685c | |||
0110345755 | |||
56cdee1fd8 | |||
1bded8572c | |||
f975c9923a | |||
b7fb0af967 | |||
3a11b9d69d | |||
50fb8243c8 | |||
741e3c3d8f | |||
3eb9c2a565 | |||
de41345bf2 | |||
b9c2f9ee56 | |||
872945ae8e | |||
cd8f04041c | |||
3a39e3df22 | |||
39156930f5 | |||
83674909f1 | |||
18ddf95d44 | |||
81c61f3243 | |||
efc1cfa939 | |||
cbbccaa722 | |||
d735607ac8 | |||
d7ba8872bf | |||
6536fa5ff7 | |||
14b0ff3f05 | |||
00b3a07efe | |||
9a2a6ab52c | |||
40f72e80c3 | |||
56c9587cc3 | |||
625a9144cf | |||
773dafa8ac | |||
e234f3ea7b | |||
67c8b0db69 | |||
6b4cbe7a98 | |||
70520000d2 | |||
01c79bbefc | |||
db1dccc762 | |||
1c5bd21ebc | |||
fe99729cdb | |||
03667bdf8c | |||
eb36dbb091 | |||
e211e96d33 | |||
7a7d43344e | |||
75fedcc8dd | |||
ed4927441f | |||
6fba5f5ec7 | |||
12b897b149 | |||
16cbed7d6e | |||
0e36c43c64 | |||
2562e306b6 | |||
c82dfce246 | |||
82b7548c0c | |||
0884d1a5ce | |||
00b576b7f1 | |||
88ff622b16 | |||
394487b3a7 | |||
acc3ca9de7 |
2
.github/workflows/audit.yml
vendored
2
.github/workflows/audit.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
# Prevent sudden announcement of a new advisory from failing ci:
|
# Prevent sudden announcement of a new advisory from failing ci:
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
- uses: rustsec/audit-check@v1.4.1
|
- uses: rustsec/audit-check@v1.4.1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
12
.github/workflows/check-msrv.nu
vendored
Normal file
12
.github/workflows/check-msrv.nu
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
let toolchain_spec = open rust-toolchain.toml | get toolchain.channel
|
||||||
|
let msrv_spec = open Cargo.toml | get package.rust-version
|
||||||
|
|
||||||
|
# This check is conservative in the sense that we use `rust-toolchain.toml`'s
|
||||||
|
# override to ensure that this is the upper-bound for the minimum supported
|
||||||
|
# rust version
|
||||||
|
if $toolchain_spec != $msrv_spec {
|
||||||
|
print -e "Mismatching rust compiler versions specified in `Cargo.toml` and `rust-toolchain.toml`"
|
||||||
|
print -e $"Cargo.toml: ($msrv_spec)"
|
||||||
|
print -e $"rust-toolchain.toml: ($toolchain_spec)"
|
||||||
|
exit 1
|
||||||
|
}
|
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@ -10,7 +10,7 @@ env:
|
|||||||
NUSHELL_CARGO_PROFILE: ci
|
NUSHELL_CARGO_PROFILE: ci
|
||||||
NU_LOG_LEVEL: DEBUG
|
NU_LOG_LEVEL: DEBUG
|
||||||
# If changing these settings also change toolkit.nu
|
# If changing these settings also change toolkit.nu
|
||||||
CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used"
|
CLIPPY_OPTIONS: "-D warnings -D clippy::unwrap_used -D clippy::unchecked_duration_subtraction"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||||
@ -24,7 +24,11 @@ jobs:
|
|||||||
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
|
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
|
||||||
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
|
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
|
||||||
# revisiting this when 20.04 is closer to EOL (April 2025)
|
# revisiting this when 20.04 is closer to EOL (April 2025)
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
#
|
||||||
|
# Using macOS 13 runner because 14 is based on the M1 and has half as much RAM (7 GB,
|
||||||
|
# instead of 14 GB) which is too little for us right now. Revisit when `dfr` commands are
|
||||||
|
# removed and we're only building the `polars` plugin instead
|
||||||
|
platform: [windows-latest, macos-13, ubuntu-20.04]
|
||||||
feature: [default, dataframe]
|
feature: [default, dataframe]
|
||||||
include:
|
include:
|
||||||
- feature: default
|
- feature: default
|
||||||
@ -34,13 +38,13 @@ jobs:
|
|||||||
exclude:
|
exclude:
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
feature: dataframe
|
feature: dataframe
|
||||||
- platform: macos-latest
|
- platform: macos-13
|
||||||
feature: dataframe
|
feature: dataframe
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
@ -85,7 +89,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
@ -117,7 +121,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
@ -130,6 +134,9 @@ jobs:
|
|||||||
- name: Standard library tests
|
- name: Standard library tests
|
||||||
run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std'
|
run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std'
|
||||||
|
|
||||||
|
- name: Ensure that Cargo.toml MSRV and rust-toolchain.toml use the same version
|
||||||
|
run: nu .github/workflows/check-msrv.nu
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
@ -158,12 +165,16 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
# Using macOS 13 runner because 14 is based on the M1 and has half as much RAM (7 GB,
|
||||||
|
# instead of 14 GB) which is too little for us right now.
|
||||||
|
#
|
||||||
|
# Failure occuring with clippy for rust 1.77.2
|
||||||
|
platform: [windows-latest, macos-13, ubuntu-20.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
|
16
.github/workflows/nightly-build.yml
vendored
16
.github/workflows/nightly-build.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
# if: github.repository == 'nushell/nightly'
|
# if: github.repository == 'nushell/nightly'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.2
|
uses: actions/checkout@v4.1.3
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
@ -36,7 +36,7 @@ jobs:
|
|||||||
token: ${{ secrets.WORKFLOW_TOKEN }}
|
token: ${{ secrets.WORKFLOW_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
@ -123,7 +123,7 @@ jobs:
|
|||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -139,7 +139,7 @@ jobs:
|
|||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ jobs:
|
|||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -251,7 +251,7 @@ jobs:
|
|||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
|
|
||||||
@ -310,12 +310,12 @@ jobs:
|
|||||||
- name: Waiting for Release
|
- name: Waiting for Release
|
||||||
run: sleep 1800
|
run: sleep 1800
|
||||||
|
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
|
|
||||||
|
10
.github/workflows/release-pkg.nu
vendored
10
.github/workflows/release-pkg.nu
vendored
@ -134,9 +134,15 @@ print $'(char nl)All executable files:'; hr-line
|
|||||||
print (ls -f ($executable | into glob)); sleep 1sec
|
print (ls -f ($executable | into glob)); sleep 1sec
|
||||||
|
|
||||||
print $'(char nl)Copying release files...'; hr-line
|
print $'(char nl)Copying release files...'; hr-line
|
||||||
"To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
"To use the included Nushell plugins, register the binaries with the `plugin add` command to tell Nu where to find the plugin.
|
||||||
|
Then you can use `plugin use` to load the plugin into your session.
|
||||||
|
For example:
|
||||||
|
|
||||||
> register ./nu_plugin_query" | save $'($dist)/README.txt' -f
|
> plugin add ./nu_plugin_query
|
||||||
|
> plugin use query
|
||||||
|
|
||||||
|
For more information, refer to https://www.nushell.sh/book/plugins.html
|
||||||
|
" | save $'($dist)/README.txt' -f
|
||||||
[LICENSE ...(glob $executable)] | each {|it| cp -rv $it $dist } | flatten
|
[LICENSE ...(glob $executable)] | each {|it| cp -rv $it $dist } | flatten
|
||||||
|
|
||||||
print $'(char nl)Check binary release version detail:'; hr-line
|
print $'(char nl)Check binary release version detail:'; hr-line
|
||||||
|
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@ -18,6 +18,7 @@ jobs:
|
|||||||
name: Std
|
name: Std
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target:
|
target:
|
||||||
- aarch64-apple-darwin
|
- aarch64-apple-darwin
|
||||||
@ -72,20 +73,21 @@ jobs:
|
|||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Update Rust Toolchain Target
|
||||||
run: |
|
run: |
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||||
with:
|
with:
|
||||||
|
cache: false
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
|
|
||||||
@ -161,20 +163,21 @@ jobs:
|
|||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.2
|
- uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Update Rust Toolchain Target
|
||||||
run: |
|
run: |
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
||||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||||
with:
|
with:
|
||||||
|
cache: false
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.9
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.91.0
|
||||||
|
|
||||||
|
4
.github/workflows/typos.yml
vendored
4
.github/workflows/typos.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Actions Repository
|
- name: Checkout Actions Repository
|
||||||
uses: actions/checkout@v4.1.2
|
uses: actions/checkout@v4.1.3
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.20.3
|
uses: crate-ci/typos@v1.20.10
|
||||||
|
1444
Cargo.lock
generated
1444
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
73
Cargo.toml
73
Cargo.toml
@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.74.1"
|
rust-version = "1.77.2"
|
||||||
version = "0.92.1"
|
version = "0.93.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
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ members = [
|
|||||||
"crates/nu-cmd-base",
|
"crates/nu-cmd-base",
|
||||||
"crates/nu-cmd-extra",
|
"crates/nu-cmd-extra",
|
||||||
"crates/nu-cmd-lang",
|
"crates/nu-cmd-lang",
|
||||||
|
"crates/nu-cmd-plugin",
|
||||||
"crates/nu-cmd-dataframe",
|
"crates/nu-cmd-dataframe",
|
||||||
"crates/nu-command",
|
"crates/nu-command",
|
||||||
"crates/nu-color-config",
|
"crates/nu-color-config",
|
||||||
@ -40,6 +41,9 @@ members = [
|
|||||||
"crates/nu-pretty-hex",
|
"crates/nu-pretty-hex",
|
||||||
"crates/nu-protocol",
|
"crates/nu-protocol",
|
||||||
"crates/nu-plugin",
|
"crates/nu-plugin",
|
||||||
|
"crates/nu-plugin-core",
|
||||||
|
"crates/nu-plugin-engine",
|
||||||
|
"crates/nu-plugin-protocol",
|
||||||
"crates/nu-plugin-test-support",
|
"crates/nu-plugin-test-support",
|
||||||
"crates/nu_plugin_inc",
|
"crates/nu_plugin_inc",
|
||||||
"crates/nu_plugin_gstat",
|
"crates/nu_plugin_gstat",
|
||||||
@ -47,11 +51,14 @@ 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_plugin_polars",
|
||||||
|
"crates/nu_plugin_stress_internals",
|
||||||
"crates/nu-std",
|
"crates/nu-std",
|
||||||
"crates/nu-table",
|
"crates/nu-table",
|
||||||
"crates/nu-term-grid",
|
"crates/nu-term-grid",
|
||||||
"crates/nu-test-support",
|
"crates/nu-test-support",
|
||||||
"crates/nu-utils",
|
"crates/nu-utils",
|
||||||
|
"crates/nuon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
@ -59,6 +66,7 @@ alphanumeric-sort = "1.5"
|
|||||||
ansi-str = "0.8"
|
ansi-str = "0.8"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
bracoxide = "0.1.2"
|
bracoxide = "0.1.2"
|
||||||
|
brotli = "5.0"
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
bytesize = "1.3"
|
bytesize = "1.3"
|
||||||
calamine = "0.24.0"
|
calamine = "0.24.0"
|
||||||
@ -85,6 +93,7 @@ heck = "0.5.0"
|
|||||||
human-date-parser = "0.1.1"
|
human-date-parser = "0.1.1"
|
||||||
indexmap = "2.2"
|
indexmap = "2.2"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
|
interprocess = "1.2.1"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
@ -95,7 +104,7 @@ lscolors = { version = "0.17", default-features = false }
|
|||||||
lsp-server = "0.7.5"
|
lsp-server = "0.7.5"
|
||||||
lsp-types = "0.95.0"
|
lsp-types = "0.95.0"
|
||||||
mach2 = "0.4"
|
mach2 = "0.4"
|
||||||
md5 = { version = "0.10", package = "md-5"}
|
md5 = { version = "0.10", package = "md-5" }
|
||||||
miette = "7.2"
|
miette = "7.2"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
@ -112,6 +121,7 @@ open = "5.1"
|
|||||||
os_pipe = "1.1"
|
os_pipe = "1.1"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
|
pretty_assertions = "1.4"
|
||||||
print-positions = "0.6"
|
print-positions = "0.6"
|
||||||
procfs = "0.16.0"
|
procfs = "0.16.0"
|
||||||
pwd = "1.3"
|
pwd = "1.3"
|
||||||
@ -121,13 +131,15 @@ quickcheck_macros = "1.0"
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ratatui = "0.26"
|
ratatui = "0.26"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
reedline = "0.31.0"
|
reedline = "0.32.0"
|
||||||
regex = "1.9.5"
|
regex = "1.9.5"
|
||||||
|
rmp = "0.8"
|
||||||
|
rmp-serde = "1.2"
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
roxmltree = "0.19"
|
roxmltree = "0.19"
|
||||||
rstest = { version = "0.18", default-features = false }
|
rstest = { version = "0.18", default-features = false }
|
||||||
rusqlite = "0.31"
|
rusqlite = "0.31"
|
||||||
rust-embed = "8.2.0"
|
rust-embed = "8.3.0"
|
||||||
same-file = "1.0"
|
same-file = "1.0"
|
||||||
serde = { version = "1.0", default-features = false }
|
serde = { version = "1.0", default-features = false }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@ -162,24 +174,25 @@ windows = "0.54"
|
|||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.92.1" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.93.0" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.92.1" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.93.0" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.92.1" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.93.0" }
|
||||||
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.92.1", features = [
|
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.93.0", optional = true }
|
||||||
|
nu-cmd-dataframe = { path = "./crates/nu-cmd-dataframe", version = "0.93.0", features = [
|
||||||
"dataframe",
|
"dataframe",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.92.1" }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.93.0" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.92.1" }
|
nu-command = { path = "./crates/nu-command", version = "0.93.0" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.92.1" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.93.0" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.92.1" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.93.0" }
|
||||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.92.1" }
|
nu-lsp = { path = "./crates/nu-lsp/", version = "0.93.0" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.92.1" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.93.0" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.92.1" }
|
nu-path = { path = "./crates/nu-path", version = "0.93.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.92.1" }
|
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.93.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.93.0" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.92.1" }
|
nu-std = { path = "./crates/nu-std", version = "0.93.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.92.1" }
|
nu-system = { path = "./crates/nu-system", version = "0.93.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.92.1" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.93.0" }
|
||||||
|
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
@ -208,18 +221,21 @@ nix = { workspace = true, default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.92.1" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.93.0" }
|
||||||
|
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.93.0" }
|
||||||
|
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.93.0" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
dirs-next = { workspace = true }
|
dirs-next = { workspace = true }
|
||||||
divan = "0.1.14"
|
divan = "0.1.14"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = { workspace = true }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
serial_test = "3.0"
|
serial_test = "3.1"
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = [
|
plugin = [
|
||||||
"nu-plugin",
|
"nu-plugin-engine",
|
||||||
|
"nu-cmd-plugin",
|
||||||
"nu-cli/plugin",
|
"nu-cli/plugin",
|
||||||
"nu-parser/plugin",
|
"nu-parser/plugin",
|
||||||
"nu-command/plugin",
|
"nu-command/plugin",
|
||||||
@ -237,7 +253,6 @@ default-no-clipboard = [
|
|||||||
"mimalloc",
|
"mimalloc",
|
||||||
]
|
]
|
||||||
stable = ["default"]
|
stable = ["default"]
|
||||||
wasi = ["nu-cmd-lang/wasi"]
|
|
||||||
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
||||||
|
|
||||||
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
||||||
@ -245,7 +260,11 @@ wasi = ["nu-cmd-lang/wasi"]
|
|||||||
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
||||||
|
|
||||||
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
|
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
|
||||||
system-clipboard = ["reedline/system_clipboard", "nu-cli/system-clipboard"]
|
system-clipboard = [
|
||||||
|
"reedline/system_clipboard",
|
||||||
|
"nu-cli/system-clipboard",
|
||||||
|
"nu-cmd-lang/system-clipboard",
|
||||||
|
]
|
||||||
|
|
||||||
# Stable (Default)
|
# Stable (Default)
|
||||||
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
|
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use nu_cli::{eval_source, evaluate_commands};
|
use nu_cli::{eval_source, evaluate_commands};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_plugin::{Encoder, EncodingType, PluginCallResponse, PluginOutput};
|
use nu_plugin_core::{Encoder, EncodingType};
|
||||||
|
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
eval_const::create_nu_constant,
|
eval_const::create_nu_constant,
|
||||||
@ -85,6 +86,7 @@ fn bench_command_with_custom_stack_and_engine(
|
|||||||
&mut stack.clone(),
|
&mut stack.clone(),
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
None,
|
None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
@ -104,6 +106,7 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
|
|||||||
&mut stack,
|
&mut stack,
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
None,
|
None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -263,6 +266,7 @@ mod eval_commands {
|
|||||||
&mut nu_protocol::engine::Stack::new(),
|
&mut nu_protocol::engine::Stack::new(),
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
None,
|
None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
|
@ -5,25 +5,26 @@ 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.92.1"
|
version = "0.93.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.92.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.93.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.92.1" }
|
nu-command = { path = "../nu-command", version = "0.93.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.92.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.93.0" }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.92.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.93.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.92.1" }
|
nu-path = { path = "../nu-path", version = "0.93.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.93.0", optional = true }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.92.1" }
|
nu-utils = { path = "../nu-utils", version = "0.93.0" }
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.93.0" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
@ -44,5 +45,5 @@ uuid = { workspace = true, features = ["v4"] }
|
|||||||
which = { workspace = true }
|
which = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = ["nu-plugin-engine"]
|
||||||
system-clipboard = ["reedline/system_clipboard"]
|
system-clipboard = ["reedline/system_clipboard"]
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Commandline;
|
pub struct Commandline;
|
||||||
@ -11,45 +10,12 @@ impl Command for Commandline {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("commandline")
|
Signature::build("commandline")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||||
(Type::Nothing, Type::Nothing),
|
|
||||||
(Type::String, Type::String),
|
|
||||||
])
|
|
||||||
.switch(
|
|
||||||
"cursor",
|
|
||||||
"Set or get the current cursor position",
|
|
||||||
Some('c'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"cursor-end",
|
|
||||||
"Set the current cursor position to the end of the buffer",
|
|
||||||
Some('e'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"append",
|
|
||||||
"appends the string to the end of the buffer",
|
|
||||||
Some('a'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"insert",
|
|
||||||
"inserts the string into the buffer at the cursor position",
|
|
||||||
Some('i'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"replace",
|
|
||||||
"replaces the current contents of the buffer (default)",
|
|
||||||
Some('r'),
|
|
||||||
)
|
|
||||||
.optional(
|
|
||||||
"cmd",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the string to perform the operation with",
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"View or modify the current command line input buffer."
|
"View the current command line input buffer."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -59,126 +25,11 @@ impl Command for Commandline {
|
|||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
|
let repl = engine_state.repl_state.lock().expect("repl state mutex");
|
||||||
let span = cmd.span();
|
Ok(Value::string(repl.buffer.clone(), call.head).into_pipeline_data())
|
||||||
let cmd = cmd.coerce_into_string()?;
|
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
||||||
|
|
||||||
if call.has_flag(engine_state, stack, "cursor")? {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--cursor (-c)` is deprecated".into(),
|
|
||||||
msg: "Setting the current cursor position by `--cursor (-c)` is deprecated"
|
|
||||||
.into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline set-cursor`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
match cmd.parse::<i64>() {
|
|
||||||
Ok(n) => {
|
|
||||||
repl.cursor_pos = if n <= 0 {
|
|
||||||
0usize
|
|
||||||
} else {
|
|
||||||
repl.buffer
|
|
||||||
.grapheme_indices(true)
|
|
||||||
.map(|(i, _c)| i)
|
|
||||||
.nth(n as usize)
|
|
||||||
.unwrap_or(repl.buffer.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::CantConvert {
|
|
||||||
to_type: "int".to_string(),
|
|
||||||
from_type: "string".to_string(),
|
|
||||||
span,
|
|
||||||
help: Some(format!(r#"string "{cmd}" does not represent a valid int"#)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if call.has_flag(engine_state, stack, "append")? {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--append (-a)` is deprecated".into(),
|
|
||||||
msg: "Appending the string to the end of the buffer by `--append (-a)` is deprecated".into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline edit --append (-a)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
repl.buffer.push_str(&cmd);
|
|
||||||
} else if call.has_flag(engine_state, stack, "insert")? {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--insert (-i)` is deprecated".into(),
|
|
||||||
msg: "Inserts the string into the buffer at the cursor position by `--insert (-i)` is deprecated".into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline edit --insert (-i)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let cursor_pos = repl.cursor_pos;
|
|
||||||
repl.buffer.insert_str(cursor_pos, &cmd);
|
|
||||||
repl.cursor_pos += cmd.len();
|
|
||||||
} else {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--replace (-r)` is deprecated".into(),
|
|
||||||
msg: "Replacing the current contents of the buffer by `--replace (-p)` or positional argument is deprecated".into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline edit --replace (-r)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
repl.buffer = cmd;
|
|
||||||
repl.cursor_pos = repl.buffer.len();
|
|
||||||
}
|
|
||||||
Ok(Value::nothing(call.head).into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
|
||||||
if call.has_flag(engine_state, stack, "cursor-end")? {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--cursor-end (-e)` is deprecated".into(),
|
|
||||||
msg: "Setting the current cursor position to the end of the buffer by `--cursor-end (-e)` is deprecated".into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline set-cursor --end (-e)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
repl.cursor_pos = repl.buffer.len();
|
|
||||||
Ok(Value::nothing(call.head).into_pipeline_data())
|
|
||||||
} else if call.has_flag(engine_state, stack, "cursor")? {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "`--cursor (-c)` is deprecated".into(),
|
|
||||||
msg: "Getting the current cursor position by `--cursor (-c)` is deprecated"
|
|
||||||
.into(),
|
|
||||||
span: Some(call.arguments_span()),
|
|
||||||
help: Some("Use `commandline get-cursor`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let char_pos = repl
|
|
||||||
.buffer
|
|
||||||
.grapheme_indices(true)
|
|
||||||
.chain(std::iter::once((repl.buffer.len(), "")))
|
|
||||||
.position(|(i, _c)| i == repl.cursor_pos)
|
|
||||||
.expect("Cursor position isn't on a grapheme boundary");
|
|
||||||
Ok(Value::string(char_pos.to_string(), call.head).into_pipeline_data())
|
|
||||||
} else {
|
|
||||||
Ok(Value::string(repl.buffer.to_string(), call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ impl Command for KeybindingsDefault {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.category(Category::Platform)
|
.category(Category::Platform)
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
|
@ -14,7 +14,7 @@ impl Command for KeybindingsList {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
.switch("modifiers", "list of modifiers", Some('m'))
|
.switch("modifiers", "list of modifiers", Some('m'))
|
||||||
.switch("keycodes", "list of keycodes", Some('k'))
|
.switch("keycodes", "list of keycodes", Some('k'))
|
||||||
.switch("modes", "list of edit modes", Some('o'))
|
.switch("modes", "list of edit modes", Some('o'))
|
||||||
|
@ -7,8 +7,8 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::{flatten_pipeline_element, parse, FlatShape};
|
use nu_parser::{flatten_pipeline_element, parse, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
BlockId, PipelineData, Span, Value,
|
PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
||||||
use std::{str, sync::Arc};
|
use std::{str, sync::Arc};
|
||||||
@ -25,7 +25,7 @@ impl NuCompleter {
|
|||||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
|
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine_state,
|
engine_state,
|
||||||
stack: stack.reset_stdio().capture(),
|
stack: stack.reset_out_dest().capture(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,15 +63,15 @@ impl NuCompleter {
|
|||||||
|
|
||||||
fn external_completion(
|
fn external_completion(
|
||||||
&self,
|
&self,
|
||||||
block_id: BlockId,
|
closure: &Closure,
|
||||||
spans: &[String],
|
spans: &[String],
|
||||||
offset: usize,
|
offset: usize,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Option<Vec<SemanticSuggestion>> {
|
) -> Option<Vec<SemanticSuggestion>> {
|
||||||
let block = self.engine_state.get_block(block_id);
|
let block = self.engine_state.get_block(closure.block_id);
|
||||||
let mut callee_stack = self
|
let mut callee_stack = self
|
||||||
.stack
|
.stack
|
||||||
.gather_captures(&self.engine_state, &block.captures);
|
.captures_to_stack_preserve_out_dest(closure.captures.clone());
|
||||||
|
|
||||||
// Line
|
// Line
|
||||||
if let Some(pos_arg) = block.signature.required_positional.first() {
|
if let Some(pos_arg) = block.signature.required_positional.first() {
|
||||||
@ -210,13 +210,10 @@ impl NuCompleter {
|
|||||||
|
|
||||||
// We got no results for internal completion
|
// We got no results for internal completion
|
||||||
// now we can check if external completer is set and use it
|
// now we can check if external completer is set and use it
|
||||||
if let Some(block_id) = config.external_completer {
|
if let Some(closure) = config.external_completer.as_ref() {
|
||||||
if let Some(external_result) = self.external_completion(
|
if let Some(external_result) =
|
||||||
block_id,
|
self.external_completion(closure, &spans, fake_offset, new_span)
|
||||||
&spans,
|
{
|
||||||
fake_offset,
|
|
||||||
new_span,
|
|
||||||
) {
|
|
||||||
return external_result;
|
return external_result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,9 +357,9 @@ impl NuCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to complete using an external completer (if set)
|
// Try to complete using an external completer (if set)
|
||||||
if let Some(block_id) = config.external_completer {
|
if let Some(closure) = config.external_completer.as_ref() {
|
||||||
if let Some(external_result) = self.external_completion(
|
if let Some(external_result) = self.external_completion(
|
||||||
block_id,
|
closure,
|
||||||
&spans,
|
&spans,
|
||||||
fake_offset,
|
fake_offset,
|
||||||
new_span,
|
new_span,
|
||||||
|
@ -24,7 +24,7 @@ impl CustomCompletion {
|
|||||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack, decl_id: usize, line: String) -> Self {
|
pub fn new(engine_state: Arc<EngineState>, stack: Stack, decl_id: usize, line: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine_state,
|
engine_state,
|
||||||
stack: stack.reset_stdio().capture(),
|
stack: stack.reset_out_dest().capture(),
|
||||||
decl_id,
|
decl_id,
|
||||||
line,
|
line,
|
||||||
sort_by: SortBy::None,
|
sort_by: SortBy::None,
|
||||||
|
@ -68,9 +68,7 @@ impl Completer for VariableCompletion {
|
|||||||
self.var_context.1.clone().into_iter().skip(1).collect();
|
self.var_context.1.clone().into_iter().skip(1).collect();
|
||||||
|
|
||||||
if let Some(val) = env_vars.get(&target_var_str) {
|
if let Some(val) = env_vars.get(&target_var_str) {
|
||||||
for suggestion in
|
for suggestion in nested_suggestions(val, &nested_levels, current_span) {
|
||||||
nested_suggestions(val.clone(), nested_levels, current_span)
|
|
||||||
{
|
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8_insensitive(
|
||||||
options.case_sensitive,
|
options.case_sensitive,
|
||||||
suggestion.suggestion.value.as_bytes(),
|
suggestion.suggestion.value.as_bytes(),
|
||||||
@ -117,8 +115,7 @@ impl Completer for VariableCompletion {
|
|||||||
nu_protocol::NU_VARIABLE_ID,
|
nu_protocol::NU_VARIABLE_ID,
|
||||||
nu_protocol::Span::new(current_span.start, current_span.end),
|
nu_protocol::Span::new(current_span.start, current_span.end),
|
||||||
) {
|
) {
|
||||||
for suggestion in
|
for suggestion in nested_suggestions(&nuval, &self.var_context.1, current_span)
|
||||||
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
|
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8_insensitive(
|
||||||
options.case_sensitive,
|
options.case_sensitive,
|
||||||
@ -140,8 +137,7 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
// If the value exists and it's of type Record
|
// If the value exists and it's of type Record
|
||||||
if let Ok(value) = var {
|
if let Ok(value) = var {
|
||||||
for suggestion in
|
for suggestion in nested_suggestions(&value, &self.var_context.1, current_span)
|
||||||
nested_suggestions(value, self.var_context.1.clone(), current_span)
|
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8_insensitive(
|
||||||
options.case_sensitive,
|
options.case_sensitive,
|
||||||
@ -244,21 +240,21 @@ impl Completer for VariableCompletion {
|
|||||||
// Find recursively the values for sublevels
|
// Find recursively the values for sublevels
|
||||||
// if no sublevels are set it returns the current value
|
// if no sublevels are set it returns the current value
|
||||||
fn nested_suggestions(
|
fn nested_suggestions(
|
||||||
val: Value,
|
val: &Value,
|
||||||
sublevels: Vec<Vec<u8>>,
|
sublevels: &[Vec<u8>],
|
||||||
current_span: reedline::Span,
|
current_span: reedline::Span,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
let mut output: Vec<SemanticSuggestion> = vec![];
|
let mut output: Vec<SemanticSuggestion> = vec![];
|
||||||
let value = recursive_value(val, sublevels);
|
let value = recursive_value(val, sublevels).unwrap_or_else(Value::nothing);
|
||||||
|
|
||||||
let kind = SuggestionKind::Type(value.get_type());
|
let kind = SuggestionKind::Type(value.get_type());
|
||||||
match value {
|
match value {
|
||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
// Add all the columns as completion
|
// Add all the columns as completion
|
||||||
for (col, _) in val.into_iter() {
|
for col in val.columns() {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: col,
|
value: col.clone(),
|
||||||
description: None,
|
description: None,
|
||||||
style: None,
|
style: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
@ -311,56 +307,47 @@ fn nested_suggestions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extracts the recursive value (e.g: $var.a.b.c)
|
// Extracts the recursive value (e.g: $var.a.b.c)
|
||||||
fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, Span> {
|
||||||
// Go to next sublevel
|
// Go to next sublevel
|
||||||
if let Some(next_sublevel) = sublevels.clone().into_iter().next() {
|
if let Some((sublevel, next_sublevels)) = sublevels.split_first() {
|
||||||
let span = val.span();
|
let span = val.span();
|
||||||
match val {
|
match val {
|
||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
for item in *val {
|
if let Some((_, value)) = val.iter().find(|(key, _)| key.as_bytes() == sublevel) {
|
||||||
// Check if index matches with sublevel
|
// If matches try to fetch recursively the next
|
||||||
if item.0.as_bytes().to_vec() == next_sublevel {
|
recursive_value(value, next_sublevels)
|
||||||
// If matches try to fetch recursively the next
|
} else {
|
||||||
return recursive_value(item.1, sublevels.into_iter().skip(1).collect());
|
// Current sublevel value not found
|
||||||
}
|
Err(span)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current sublevel value not found
|
|
||||||
return Value::nothing(span);
|
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
Value::LazyRecord { val, .. } => {
|
||||||
for col in val.column_names() {
|
for col in val.column_names() {
|
||||||
if col.as_bytes().to_vec() == next_sublevel {
|
if col.as_bytes() == *sublevel {
|
||||||
return recursive_value(
|
let val = val.get_column_value(col).map_err(|_| span)?;
|
||||||
val.get_column_value(col).unwrap_or_default(),
|
return recursive_value(&val, next_sublevels);
|
||||||
sublevels.into_iter().skip(1).collect(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current sublevel value not found
|
// Current sublevel value not found
|
||||||
return Value::nothing(span);
|
Err(span)
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for col in get_columns(vals.as_slice()) {
|
for col in get_columns(vals.as_slice()) {
|
||||||
if col.as_bytes().to_vec() == next_sublevel {
|
if col.as_bytes() == *sublevel {
|
||||||
return recursive_value(
|
let val = val.get_data_by_key(&col).ok_or(span)?;
|
||||||
Value::list(vals, span)
|
return recursive_value(&val, next_sublevels);
|
||||||
.get_data_by_key(&col)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
sublevels.into_iter().skip(1).collect(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current sublevel value not found
|
// Current sublevel value not found
|
||||||
return Value::nothing(span);
|
Err(span)
|
||||||
}
|
}
|
||||||
_ => return val,
|
_ => Ok(val.clone()),
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Ok(val.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchAlgorithm {
|
impl MatchAlgorithm {
|
||||||
|
@ -6,13 +6,15 @@ use nu_protocol::{
|
|||||||
report_error, HistoryFileFormat, PipelineData,
|
report_error, HistoryFileFormat, PipelineData,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_protocol::{ParseError, Spanned};
|
use nu_protocol::{ParseError, PluginRegistryFile, Spanned};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::utils::perf;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
const PLUGIN_FILE: &str = "plugin.nu";
|
const PLUGIN_FILE: &str = "plugin.msgpackz";
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
const OLD_PLUGIN_FILE: &str = "plugin.nu";
|
||||||
|
|
||||||
const HISTORY_FILE_TXT: &str = "history.txt";
|
const HISTORY_FILE_TXT: &str = "history.txt";
|
||||||
const HISTORY_FILE_SQLITE: &str = "history.sqlite3";
|
const HISTORY_FILE_SQLITE: &str = "history.sqlite3";
|
||||||
@ -20,40 +22,150 @@ const HISTORY_FILE_SQLITE: &str = "history.sqlite3";
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub fn read_plugin_file(
|
pub fn read_plugin_file(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
|
||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
) {
|
) {
|
||||||
let start_time = std::time::Instant::now();
|
use std::path::Path;
|
||||||
let mut plug_path = String::new();
|
|
||||||
// Reading signatures from signature file
|
|
||||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
|
||||||
add_plugin_file(engine_state, plugin_file, storage_path);
|
|
||||||
|
|
||||||
let plugin_path = engine_state.plugin_signatures.clone();
|
use nu_protocol::{report_error_new, ShellError};
|
||||||
if let Some(plugin_path) = plugin_path {
|
|
||||||
let plugin_filename = plugin_path.to_string_lossy();
|
let span = plugin_file.as_ref().map(|s| s.span);
|
||||||
plug_path = plugin_filename.to_string();
|
|
||||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
// Check and warn + abort if this is a .nu plugin file
|
||||||
eval_source(
|
if plugin_file
|
||||||
engine_state,
|
.as_ref()
|
||||||
stack,
|
.and_then(|p| Path::new(&p.item).extension())
|
||||||
&contents,
|
.is_some_and(|ext| ext == "nu")
|
||||||
&plugin_filename,
|
{
|
||||||
PipelineData::empty(),
|
report_error_new(
|
||||||
false,
|
engine_state,
|
||||||
);
|
&ShellError::GenericError {
|
||||||
}
|
error: "Wrong plugin file format".into(),
|
||||||
|
msg: ".nu plugin files are no longer supported".into(),
|
||||||
|
span,
|
||||||
|
help: Some("please recreate this file in the new .msgpackz format".into()),
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut start_time = std::time::Instant::now();
|
||||||
|
// Reading signatures from plugin registry file
|
||||||
|
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin
|
||||||
|
add_plugin_file(engine_state, plugin_file.clone(), storage_path);
|
||||||
perf(
|
perf(
|
||||||
&format!("read_plugin_file {}", &plug_path),
|
"add plugin file to engine_state",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
column!(),
|
column!(),
|
||||||
engine_state.get_config().use_ansi_coloring,
|
engine_state.get_config().use_ansi_coloring,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
|
let plugin_path = engine_state.plugin_path.clone();
|
||||||
|
if let Some(plugin_path) = plugin_path {
|
||||||
|
// Open the plugin file
|
||||||
|
let mut file = match std::fs::File::open(&plugin_path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
log::warn!("Plugin file not found: {}", plugin_path.display());
|
||||||
|
|
||||||
|
// Try migration of an old plugin file if this wasn't a custom plugin file
|
||||||
|
if plugin_file.is_none() && migrate_old_plugin_file(engine_state, storage_path)
|
||||||
|
{
|
||||||
|
let Ok(file) = std::fs::File::open(&plugin_path) else {
|
||||||
|
log::warn!("Failed to load newly migrated plugin file");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: format!(
|
||||||
|
"Error while opening plugin registry file: {}",
|
||||||
|
plugin_path.display()
|
||||||
|
),
|
||||||
|
msg: "plugin path defined here".into(),
|
||||||
|
span,
|
||||||
|
help: None,
|
||||||
|
inner: vec![err.into()],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abort if the file is empty.
|
||||||
|
if file.metadata().is_ok_and(|m| m.len() == 0) {
|
||||||
|
log::warn!(
|
||||||
|
"Not reading plugin file because it's empty: {}",
|
||||||
|
plugin_path.display()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the contents of the plugin file
|
||||||
|
let contents = match PluginRegistryFile::read_from(&mut file, span) {
|
||||||
|
Ok(contents) => contents,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to read plugin registry file: {err:?}");
|
||||||
|
report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: format!(
|
||||||
|
"Error while reading plugin registry file: {}",
|
||||||
|
plugin_path.display()
|
||||||
|
),
|
||||||
|
msg: "plugin path defined here".into(),
|
||||||
|
span,
|
||||||
|
help: Some(
|
||||||
|
"you might try deleting the file and registering all of your \
|
||||||
|
plugins again"
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
perf(
|
||||||
|
&format!("read plugin file {}", plugin_path.display()),
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
engine_state.get_config().use_ansi_coloring,
|
||||||
|
);
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
nu_plugin_engine::load_plugin_file(&mut working_set, &contents, span);
|
||||||
|
|
||||||
|
if let Err(err) = engine_state.merge_delta(working_set.render()) {
|
||||||
|
report_error_new(engine_state, &err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
perf(
|
||||||
|
&format!("load plugin file {}", plugin_path.display()),
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
engine_state.get_config().use_ansi_coloring,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@ -62,15 +174,30 @@ pub fn add_plugin_file(
|
|||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
) {
|
) {
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
if let Some(plugin_file) = plugin_file {
|
if let Some(plugin_file) = plugin_file {
|
||||||
if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
|
let path = Path::new(&plugin_file.item);
|
||||||
engine_state.plugin_signatures = Some(path)
|
let path_dir = path.parent().unwrap_or(path);
|
||||||
|
// Just try to canonicalize the directory of the plugin file first.
|
||||||
|
if let Ok(path_dir) = canonicalize_with(path_dir, &cwd) {
|
||||||
|
// Try to canonicalize the actual filename, but it's ok if that fails. The file doesn't
|
||||||
|
// have to exist.
|
||||||
|
let path = path_dir.join(path.file_name().unwrap_or(path.as_os_str()));
|
||||||
|
let path = canonicalize_with(&path, &cwd).unwrap_or(path);
|
||||||
|
engine_state.plugin_path = Some(path)
|
||||||
} else {
|
} else {
|
||||||
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
// It's an error if the directory for the plugin file doesn't exist.
|
||||||
report_error(&working_set, &e);
|
report_error(
|
||||||
|
&working_set,
|
||||||
|
&ParseError::FileNotFound(
|
||||||
|
path_dir.to_string_lossy().into_owned(),
|
||||||
|
plugin_file.span,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
@ -78,7 +205,7 @@ pub fn add_plugin_file(
|
|||||||
let mut plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let mut plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
||||||
plugin_path.push(PLUGIN_FILE);
|
plugin_path.push(PLUGIN_FILE);
|
||||||
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
||||||
engine_state.plugin_signatures = Some(plugin_path);
|
engine_state.plugin_path = Some(plugin_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +218,10 @@ pub fn eval_config_contents(
|
|||||||
let config_filename = config_path.to_string_lossy();
|
let config_filename = config_path.to_string_lossy();
|
||||||
|
|
||||||
if let Ok(contents) = std::fs::read(&config_path) {
|
if let Ok(contents) = std::fs::read(&config_path) {
|
||||||
|
// Set the current active file to the config file.
|
||||||
|
let prev_file = engine_state.file.take();
|
||||||
|
engine_state.file = Some(config_path.clone());
|
||||||
|
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
@ -100,6 +231,9 @@ pub fn eval_config_contents(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Restore the current active file.
|
||||||
|
engine_state.file = prev_file;
|
||||||
|
|
||||||
// Merge the environment in case env vars changed in the config
|
// Merge the environment in case env vars changed in the config
|
||||||
match nu_engine::env::current_dir(engine_state, stack) {
|
match nu_engine::env::current_dir(engine_state, stack) {
|
||||||
Ok(cwd) => {
|
Ok(cwd) => {
|
||||||
@ -127,3 +261,129 @@ pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> O
|
|||||||
history_path
|
history_path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -> bool {
|
||||||
|
use nu_protocol::{
|
||||||
|
report_error_new, PluginExample, PluginIdentity, PluginRegistryItem,
|
||||||
|
PluginRegistryItemData, PluginSignature, ShellError,
|
||||||
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
|
let cwd = engine_state.current_work_dir();
|
||||||
|
|
||||||
|
let Some(config_dir) = nu_path::config_dir().and_then(|mut dir| {
|
||||||
|
dir.push(storage_path);
|
||||||
|
nu_path::canonicalize_with(dir, &cwd).ok()
|
||||||
|
}) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(old_plugin_file_path) = nu_path::canonicalize_with(OLD_PLUGIN_FILE, &config_dir) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_contents = match std::fs::read(&old_plugin_file_path) {
|
||||||
|
Ok(old_contents) => old_contents,
|
||||||
|
Err(err) => {
|
||||||
|
report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Can't read old plugin file to migrate".into(),
|
||||||
|
msg: "".into(),
|
||||||
|
span: None,
|
||||||
|
help: Some(err.to_string()),
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a copy of the engine state, because we'll read the newly generated file
|
||||||
|
let mut engine_state = engine_state.clone();
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
if !eval_source(
|
||||||
|
&mut engine_state,
|
||||||
|
&mut stack,
|
||||||
|
&old_contents,
|
||||||
|
&old_plugin_file_path.to_string_lossy(),
|
||||||
|
PipelineData::Empty,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that the plugin commands are loaded, we just have to generate the file
|
||||||
|
let mut contents = PluginRegistryFile::new();
|
||||||
|
|
||||||
|
let mut groups = BTreeMap::<PluginIdentity, Vec<PluginSignature>>::new();
|
||||||
|
|
||||||
|
for decl in engine_state.plugin_decls() {
|
||||||
|
if let Some(identity) = decl.plugin_identity() {
|
||||||
|
groups
|
||||||
|
.entry(identity.clone())
|
||||||
|
.or_default()
|
||||||
|
.push(PluginSignature {
|
||||||
|
sig: decl.signature(),
|
||||||
|
examples: decl
|
||||||
|
.examples()
|
||||||
|
.into_iter()
|
||||||
|
.map(PluginExample::from)
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (identity, commands) in groups {
|
||||||
|
contents.upsert_plugin(PluginRegistryItem {
|
||||||
|
name: identity.name().to_owned(),
|
||||||
|
filename: identity.filename().to_owned(),
|
||||||
|
shell: identity.shell().map(|p| p.to_owned()),
|
||||||
|
data: PluginRegistryItemData::Valid { commands },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new file
|
||||||
|
let new_plugin_file_path = config_dir.join(PLUGIN_FILE);
|
||||||
|
if let Err(err) = std::fs::File::create(&new_plugin_file_path)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
.and_then(|file| contents.write_to(file, None))
|
||||||
|
{
|
||||||
|
report_error_new(
|
||||||
|
&engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Failed to save migrated plugin file".into(),
|
||||||
|
msg: "".into(),
|
||||||
|
span: None,
|
||||||
|
help: Some("ensure `$nu.plugin-path` is writable".into()),
|
||||||
|
inner: vec![err],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if engine_state.is_interactive {
|
||||||
|
eprintln!(
|
||||||
|
"Your old plugin.nu file has been migrated to the new format: {}",
|
||||||
|
new_plugin_file_path.display()
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"The plugin.nu file has not been removed. If `plugin list` looks okay, \
|
||||||
|
you may do so manually."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"migrate old plugin file",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
engine_state.get_config().use_ansi_coloring,
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ pub fn evaluate_commands(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
table_mode: Option<Value>,
|
table_mode: Option<Value>,
|
||||||
|
no_newline: bool,
|
||||||
) -> Result<Option<i64>> {
|
) -> Result<Option<i64>> {
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
@ -60,7 +61,13 @@ pub fn evaluate_commands(
|
|||||||
if let Some(t_mode) = table_mode {
|
if let Some(t_mode) = table_mode {
|
||||||
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default();
|
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default();
|
||||||
}
|
}
|
||||||
crate::eval_file::print_table_or_error(engine_state, stack, pipeline_data, &mut config)
|
crate::eval_file::print_table_or_error(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
pipeline_data,
|
||||||
|
&mut config,
|
||||||
|
no_newline,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
@ -5,15 +5,16 @@ use nu_engine::{convert_env_values, current_dir, eval_block};
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error, Config, PipelineData, ShellError, Span, Value,
|
report_error, Config, PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::stdout_write_all_and_flush;
|
use std::{io::Write, sync::Arc};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
/// Main function used when a file path is found as argument for nu
|
/// Entry point for evaluating a file.
|
||||||
|
///
|
||||||
|
/// If the file contains a main command, it is invoked with `args` and the pipeline data from `input`;
|
||||||
|
/// otherwise, the pipeline data is forwarded to the first command in the file, and `args` are ignored.
|
||||||
pub fn evaluate_file(
|
pub fn evaluate_file(
|
||||||
path: String,
|
path: String,
|
||||||
args: &[String],
|
args: &[String],
|
||||||
@ -21,7 +22,7 @@ pub fn evaluate_file(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Translate environment variables from Strings to Values
|
// Convert environment variables from Strings to Values and store them in the engine state.
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
@ -74,8 +75,7 @@ pub fn evaluate_file(
|
|||||||
);
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
engine_state.file = Some(file_path.clone());
|
||||||
engine_state.start_in_file(Some(file_path_str));
|
|
||||||
|
|
||||||
let parent = file_path.parent().unwrap_or_else(|| {
|
let parent = file_path.parent().unwrap_or_else(|| {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
@ -104,17 +104,19 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let source_filename = file_path
|
let source_filename = file_path
|
||||||
.file_name()
|
.file_name()
|
||||||
.expect("internal error: script missing filename");
|
.expect("internal error: missing filename");
|
||||||
|
|
||||||
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 block = parse(&mut working_set, Some(file_path_str), &file, false);
|
let block = parse(&mut working_set, Some(file_path_str), &file, false);
|
||||||
|
|
||||||
|
// If any parse errors were found, report the first error and exit.
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
report_error(&working_set, err);
|
report_error(&working_set, err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Look for blocks whose name starts with "main" and replace it with the filename.
|
||||||
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
|
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
|
||||||
if block.signature.name == "main" {
|
if block.signature.name == "main" {
|
||||||
block.signature.name = source_filename.to_string_lossy().to_string();
|
block.signature.name = source_filename.to_string_lossy().to_string();
|
||||||
@ -124,19 +126,21 @@ pub fn evaluate_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = engine_state.merge_delta(working_set.delta);
|
// Merge the changes into the engine state.
|
||||||
|
engine_state
|
||||||
|
.merge_delta(working_set.delta)
|
||||||
|
.expect("merging delta into engine_state should succeed");
|
||||||
|
|
||||||
|
// Check if the file contains a main command.
|
||||||
if engine_state.find_decl(b"main", &[]).is_some() {
|
if engine_state.find_decl(b"main", &[]).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
// Evaluate the file, but don't run main yet.
|
||||||
|
|
||||||
let pipeline_data =
|
let pipeline_data =
|
||||||
eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::empty());
|
eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::empty());
|
||||||
let pipeline_data = match pipeline_data {
|
let pipeline_data = match pipeline_data {
|
||||||
Err(ShellError::Return { .. }) => {
|
Err(ShellError::Return { .. }) => {
|
||||||
// allows early exists before `main` is run.
|
// Allow early return before main is run.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
x => x,
|
x => x,
|
||||||
}
|
}
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
@ -145,12 +149,12 @@ pub fn evaluate_file(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Print the pipeline output of the file.
|
||||||
|
// The pipeline output of a file is the pipeline output of its last command.
|
||||||
let result = pipeline_data.print(engine_state, stack, true, false);
|
let result = pipeline_data.print(engine_state, stack, true, false);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@ -161,6 +165,9 @@ pub fn evaluate_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invoke the main command with arguments.
|
||||||
|
// Arguments with whitespaces are quoted, thus can be safely concatenated by whitespace.
|
||||||
|
let args = format!("main {}", args.join(" "));
|
||||||
if !eval_source(
|
if !eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
@ -185,6 +192,7 @@ pub(crate) fn print_table_or_error(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
mut pipeline_data: PipelineData,
|
mut pipeline_data: PipelineData,
|
||||||
config: &mut Config,
|
config: &mut Config,
|
||||||
|
no_newline: bool,
|
||||||
) -> Option<i64> {
|
) -> Option<i64> {
|
||||||
let exit_code = match &mut pipeline_data {
|
let exit_code = match &mut pipeline_data {
|
||||||
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
||||||
@ -200,29 +208,8 @@ pub(crate) fn print_table_or_error(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
|
// We don't need to do anything special to print a table because print() handles it
|
||||||
let command = engine_state.get_decl(decl_id);
|
print_or_exit(pipeline_data, engine_state, stack, no_newline);
|
||||||
if command.get_block_id().is_some() {
|
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
|
||||||
} else {
|
|
||||||
// The final call on table command, it's ok to set redirect_output to false.
|
|
||||||
let call = Call::new(Span::new(0, 0));
|
|
||||||
let table = command.run(engine_state, stack, &call, pipeline_data);
|
|
||||||
|
|
||||||
match table {
|
|
||||||
Ok(table) => {
|
|
||||||
print_or_exit(table, engine_state, config);
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &error);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure everything has finished
|
// Make sure everything has finished
|
||||||
if let Some(exit_code) = exit_code {
|
if let Some(exit_code) = exit_code {
|
||||||
@ -238,17 +225,21 @@ pub(crate) fn print_table_or_error(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, config: &Config) {
|
fn print_or_exit(
|
||||||
for item in pipeline_data {
|
pipeline_data: PipelineData,
|
||||||
if let Value::Error { error, .. } = item {
|
engine_state: &EngineState,
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
stack: &mut Stack,
|
||||||
|
no_newline: bool,
|
||||||
|
) {
|
||||||
|
let result = pipeline_data.print(engine_state, stack, no_newline, false);
|
||||||
|
|
||||||
report_error(&working_set, &*error);
|
let _ = std::io::stdout().flush();
|
||||||
|
let _ = std::io::stderr().flush();
|
||||||
|
|
||||||
std::process::exit(1);
|
if let Err(error) = result {
|
||||||
}
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
report_error(&working_set, &error);
|
||||||
let out = item.to_expanded_string("\n", config) + "\n";
|
let _ = std::io::stderr().flush();
|
||||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,6 @@ pub use validation::NuValidator;
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub use config_files::add_plugin_file;
|
pub use config_files::add_plugin_file;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
pub use config_files::migrate_old_plugin_file;
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
pub use config_files::read_plugin_file;
|
pub use config_files::read_plugin_file;
|
||||||
|
@ -28,7 +28,7 @@ impl NuMenuCompleter {
|
|||||||
Self {
|
Self {
|
||||||
block_id,
|
block_id,
|
||||||
span,
|
span,
|
||||||
stack: stack.reset_stdio().capture(),
|
stack: stack.reset_out_dest().capture(),
|
||||||
engine_state,
|
engine_state,
|
||||||
only_buffer_difference,
|
only_buffer_difference,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::NushellPrompt;
|
use crate::NushellPrompt;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_engine::get_eval_subexpression;
|
use nu_engine::ClosureEvalOnce;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error, Config, PipelineData, Value,
|
report_error, Config, PipelineData, Value,
|
||||||
@ -34,17 +34,13 @@ fn get_prompt_string(
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let eval_subexpression = get_eval_subexpression(engine_state);
|
|
||||||
|
|
||||||
stack
|
stack
|
||||||
.get_env_var(engine_state, prompt)
|
.get_env_var(engine_state, prompt)
|
||||||
.and_then(|v| match v {
|
.and_then(|v| match v {
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
let block = engine_state.get_block(val.block_id);
|
let result = ClosureEvalOnce::new(engine_state, stack, val)
|
||||||
let mut stack = stack.captures_to_stack(val.captures);
|
.run_with_input(PipelineData::Empty);
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
|
||||||
let ret_val =
|
|
||||||
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
|
|
||||||
trace!(
|
trace!(
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
file!(),
|
file!(),
|
||||||
@ -52,25 +48,7 @@ fn get_prompt_string(
|
|||||||
column!()
|
column!()
|
||||||
);
|
);
|
||||||
|
|
||||||
ret_val
|
result
|
||||||
.map_err(|err| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &err);
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
Value::Block { val: block_id, .. } => {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
|
||||||
let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
|
|
||||||
trace!(
|
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
|
||||||
|
|
||||||
ret_val
|
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
|
@ -23,7 +23,10 @@ use nu_protocol::{
|
|||||||
report_error_new, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
report_error_new, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
||||||
Value, NU_VARIABLE_ID,
|
Value, NU_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::{
|
||||||
|
filesystem::{have_permission, PermissionResult},
|
||||||
|
utils::perf,
|
||||||
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
|
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
|
||||||
HistorySessionId, Reedline, SqliteBackedHistory, Vi,
|
HistorySessionId, Reedline, SqliteBackedHistory, Vi,
|
||||||
@ -71,7 +74,8 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let mut entry_num = 0;
|
let mut entry_num = 0;
|
||||||
|
|
||||||
let nu_prompt = NushellPrompt::new(config.shell_integration);
|
let shell_integration = config.shell_integration;
|
||||||
|
let nu_prompt = NushellPrompt::new(shell_integration);
|
||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
@ -111,6 +115,11 @@ pub fn evaluate_repl(
|
|||||||
engine_state.merge_env(&mut unique_stack, cwd)?;
|
engine_state.merge_env(&mut unique_stack, cwd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hostname = System::host_name();
|
||||||
|
if shell_integration {
|
||||||
|
shell_integration_osc_7_633_2(hostname.as_deref(), engine_state, &mut unique_stack);
|
||||||
|
}
|
||||||
|
|
||||||
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
||||||
|
|
||||||
// Regenerate the $nu constant to contain the startup time and any other potential updates
|
// Regenerate the $nu constant to contain the startup time and any other potential updates
|
||||||
@ -144,7 +153,7 @@ pub fn evaluate_repl(
|
|||||||
let temp_file_cloned = temp_file.clone();
|
let temp_file_cloned = temp_file.clone();
|
||||||
let mut nu_prompt_cloned = nu_prompt.clone();
|
let mut nu_prompt_cloned = nu_prompt.clone();
|
||||||
|
|
||||||
let iteration_panic_state = catch_unwind(AssertUnwindSafe(move || {
|
let iteration_panic_state = catch_unwind(AssertUnwindSafe(|| {
|
||||||
let (continue_loop, current_stack, line_editor) = loop_iteration(LoopContext {
|
let (continue_loop, current_stack, line_editor) = loop_iteration(LoopContext {
|
||||||
engine_state: &mut current_engine_state,
|
engine_state: &mut current_engine_state,
|
||||||
stack: current_stack,
|
stack: current_stack,
|
||||||
@ -153,6 +162,7 @@ pub fn evaluate_repl(
|
|||||||
temp_file: &temp_file_cloned,
|
temp_file: &temp_file_cloned,
|
||||||
use_color,
|
use_color,
|
||||||
entry_num: &mut entry_num,
|
entry_num: &mut entry_num,
|
||||||
|
hostname: hostname.as_deref(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// pass the most recent version of the line_editor back
|
// pass the most recent version of the line_editor back
|
||||||
@ -229,6 +239,7 @@ struct LoopContext<'a> {
|
|||||||
temp_file: &'a Path,
|
temp_file: &'a Path,
|
||||||
use_color: bool,
|
use_color: bool,
|
||||||
entry_num: &'a mut usize,
|
entry_num: &'a mut usize,
|
||||||
|
hostname: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform one iteration of the REPL loop
|
/// Perform one iteration of the REPL loop
|
||||||
@ -247,6 +258,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
temp_file,
|
temp_file,
|
||||||
use_color,
|
use_color,
|
||||||
entry_num,
|
entry_num,
|
||||||
|
hostname,
|
||||||
} = ctx;
|
} = ctx;
|
||||||
|
|
||||||
let cwd = get_guaranteed_cwd(engine_state, &stack);
|
let cwd = get_guaranteed_cwd(engine_state, &stack);
|
||||||
@ -361,6 +373,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
.with_partial_completions(config.partial_completions)
|
.with_partial_completions(config.partial_completions)
|
||||||
.with_ansi_colors(config.use_ansi_coloring)
|
.with_ansi_colors(config.use_ansi_coloring)
|
||||||
.with_cursor_config(cursor_config);
|
.with_cursor_config(cursor_config);
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"reedline builder",
|
"reedline builder",
|
||||||
start_time,
|
start_time,
|
||||||
@ -382,6 +395,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
} else {
|
} else {
|
||||||
line_editor.disable_hints()
|
line_editor.disable_hints()
|
||||||
};
|
};
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"reedline coloring/style_computer",
|
"reedline coloring/style_computer",
|
||||||
start_time,
|
start_time,
|
||||||
@ -398,8 +412,9 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
report_error_new(engine_state, &e);
|
report_error_new(engine_state, &e);
|
||||||
Reedline::create()
|
Reedline::create()
|
||||||
});
|
});
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"reedline menus",
|
"reedline adding menus",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
@ -421,6 +436,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
} else {
|
} else {
|
||||||
line_editor
|
line_editor
|
||||||
};
|
};
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"reedline buffer_editor",
|
"reedline buffer_editor",
|
||||||
start_time,
|
start_time,
|
||||||
@ -437,6 +453,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
warn!("Failed to sync history: {}", e);
|
warn!("Failed to sync history: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"sync_history",
|
"sync_history",
|
||||||
start_time,
|
start_time,
|
||||||
@ -450,6 +467,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Changing the line editor based on the found keybindings
|
// Changing the line editor based on the found keybindings
|
||||||
line_editor = setup_keybindings(engine_state, line_editor);
|
line_editor = setup_keybindings(engine_state, line_editor);
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"keybindings",
|
"keybindings",
|
||||||
start_time,
|
start_time,
|
||||||
@ -473,6 +491,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
&mut Stack::with_parent(stack_arc.clone()),
|
&mut Stack::with_parent(stack_arc.clone()),
|
||||||
nu_prompt,
|
nu_prompt,
|
||||||
);
|
);
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"update_prompt",
|
"update_prompt",
|
||||||
start_time,
|
start_time,
|
||||||
@ -498,18 +517,30 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
|
|
||||||
let mut stack = Stack::unwrap_unique(stack_arc);
|
let mut stack = Stack::unwrap_unique(stack_arc);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"line_editor setup",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
|
||||||
|
let line_editor_input_time = std::time::Instant::now();
|
||||||
match input {
|
match input {
|
||||||
Ok(Signal::Success(s)) => {
|
Ok(Signal::Success(s)) => {
|
||||||
let hostname = System::host_name();
|
|
||||||
let history_supports_meta = matches!(
|
let history_supports_meta = matches!(
|
||||||
engine_state.history_config().map(|h| h.file_format),
|
engine_state.history_config().map(|h| h.file_format),
|
||||||
Some(HistoryFileFormat::Sqlite)
|
Some(HistoryFileFormat::Sqlite)
|
||||||
);
|
);
|
||||||
|
|
||||||
if history_supports_meta {
|
if history_supports_meta {
|
||||||
prepare_history_metadata(&s, &hostname, engine_state, &mut line_editor);
|
prepare_history_metadata(&s, hostname, engine_state, &mut line_editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For pre_exec_hook
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
// Right before we start running the code the user gave us, fire the `pre_execution`
|
// Right before we start running the code the user gave us, fire the `pre_execution`
|
||||||
// hook
|
// hook
|
||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||||
@ -530,22 +561,57 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"pre_execution_hook",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
||||||
repl.cursor_pos = line_editor.current_insertion_point();
|
repl.cursor_pos = line_editor.current_insertion_point();
|
||||||
repl.buffer = line_editor.current_buffer_contents().to_string();
|
repl.buffer = line_editor.current_buffer_contents().to_string();
|
||||||
drop(repl);
|
drop(repl);
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(PRE_EXECUTE_MARKER);
|
run_ansi_sequence(PRE_EXECUTE_MARKER);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"pre_execute_marker (133;C) ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual command execution logic starts from here
|
// Actual command execution logic starts from here
|
||||||
let start_time = Instant::now();
|
let cmd_execution_start_time = Instant::now();
|
||||||
|
|
||||||
match parse_operation(s.clone(), engine_state, &stack) {
|
match parse_operation(s.clone(), engine_state, &stack) {
|
||||||
Ok(operation) => match operation {
|
Ok(operation) => match operation {
|
||||||
ReplOperation::AutoCd { cwd, target, span } => {
|
ReplOperation::AutoCd { cwd, target, span } => {
|
||||||
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
||||||
|
|
||||||
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ReplOperation::RunCommand(cmd) => {
|
ReplOperation::RunCommand(cmd) => {
|
||||||
line_editor = do_run_cmd(
|
line_editor = do_run_cmd(
|
||||||
@ -555,14 +621,30 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
line_editor,
|
line_editor,
|
||||||
shell_integration,
|
shell_integration,
|
||||||
*entry_num,
|
*entry_num,
|
||||||
)
|
use_color,
|
||||||
|
);
|
||||||
|
|
||||||
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// as the name implies, we do nothing in this case
|
// as the name implies, we do nothing in this case
|
||||||
ReplOperation::DoNothing => {}
|
ReplOperation::DoNothing => {}
|
||||||
},
|
},
|
||||||
Err(ref e) => error!("Error parsing operation: {e}"),
|
Err(ref e) => error!("Error parsing operation: {e}"),
|
||||||
}
|
}
|
||||||
let cmd_duration = start_time.elapsed();
|
let cmd_duration = cmd_execution_start_time.elapsed();
|
||||||
|
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"CMD_DURATION_MS".into(),
|
"CMD_DURATION_MS".into(),
|
||||||
@ -582,7 +664,18 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
do_shell_integration_finalize_command(hostname, engine_state, &mut stack);
|
start_time = Instant::now();
|
||||||
|
|
||||||
|
shell_integration_osc_7_633_2(hostname, engine_state, &mut stack);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"shell_integration_finalize ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
|
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
|
||||||
@ -590,13 +683,35 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
Ok(Signal::CtrlC) => {
|
Ok(Signal::CtrlC) => {
|
||||||
// `Reedline` clears the line content. New prompt is shown
|
// `Reedline` clears the line content. New prompt is shown
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"command_finished_marker ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlD) => {
|
Ok(Signal::CtrlD) => {
|
||||||
// When exiting clear to a new line
|
// When exiting clear to a new line
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"command_finished_marker ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
return (false, stack, line_editor);
|
return (false, stack, line_editor);
|
||||||
@ -611,13 +726,24 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
// Alternatively only allow that expected failures let the REPL loop
|
// Alternatively only allow that expected failures let the REPL loop
|
||||||
}
|
}
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"command_finished_marker ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
perf(
|
||||||
"processing line editor input",
|
"processing line editor input",
|
||||||
start_time,
|
line_editor_input_time,
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
column!(),
|
column!(),
|
||||||
@ -625,7 +751,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"finished repl loop",
|
"time between prompts in line editor loop",
|
||||||
loop_start_time,
|
loop_start_time,
|
||||||
file!(),
|
file!(),
|
||||||
line!(),
|
line!(),
|
||||||
@ -641,7 +767,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
///
|
///
|
||||||
fn prepare_history_metadata(
|
fn prepare_history_metadata(
|
||||||
s: &str,
|
s: &str,
|
||||||
hostname: &Option<String>,
|
hostname: Option<&str>,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
line_editor: &mut Reedline,
|
line_editor: &mut Reedline,
|
||||||
) {
|
) {
|
||||||
@ -649,7 +775,7 @@ fn prepare_history_metadata(
|
|||||||
let result = line_editor
|
let result = line_editor
|
||||||
.update_last_command_context(&|mut c| {
|
.update_last_command_context(&|mut c| {
|
||||||
c.start_timestamp = Some(chrono::Utc::now());
|
c.start_timestamp = Some(chrono::Utc::now());
|
||||||
c.hostname.clone_from(hostname);
|
c.hostname = hostname.map(str::to_string);
|
||||||
|
|
||||||
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
||||||
c
|
c
|
||||||
@ -761,6 +887,16 @@ fn do_auto_cd(
|
|||||||
path.to_string_lossy().to_string()
|
path.to_string_lossy().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let PermissionResult::PermissionDenied(reason) = have_permission(path.clone()) {
|
||||||
|
report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::IOError {
|
||||||
|
msg: format!("Cannot change directory to {path}: {reason}"),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
stack.add_env_var("OLDPWD".into(), Value::string(cwd.clone(), Span::unknown()));
|
stack.add_env_var("OLDPWD".into(), Value::string(cwd.clone(), Span::unknown()));
|
||||||
|
|
||||||
//FIXME: this only changes the current scope, but instead this environment variable
|
//FIXME: this only changes the current scope, but instead this environment variable
|
||||||
@ -812,6 +948,7 @@ fn do_run_cmd(
|
|||||||
line_editor: Reedline,
|
line_editor: Reedline,
|
||||||
shell_integration: bool,
|
shell_integration: bool,
|
||||||
entry_num: usize,
|
entry_num: usize,
|
||||||
|
use_color: bool,
|
||||||
) -> Reedline {
|
) -> Reedline {
|
||||||
trace!("eval source: {}", s);
|
trace!("eval source: {}", s);
|
||||||
|
|
||||||
@ -837,6 +974,7 @@ fn do_run_cmd(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration {
|
||||||
|
let start_time = Instant::now();
|
||||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
||||||
match cwd.coerce_into_string() {
|
match cwd.coerce_into_string() {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
@ -859,6 +997,15 @@ fn do_run_cmd(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"set title with command ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_source(
|
eval_source(
|
||||||
@ -875,14 +1022,14 @@ fn do_run_cmd(
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// Output some things and set environment variables so shells with the right integration
|
/// Output some things and set environment variables so shells with the right integration
|
||||||
/// can have more information about what is going on (after we have run a command)
|
/// can have more information about what is going on (both on startup and after we have
|
||||||
|
/// run a command)
|
||||||
///
|
///
|
||||||
fn do_shell_integration_finalize_command(
|
fn shell_integration_osc_7_633_2(
|
||||||
hostname: Option<String>,
|
hostname: Option<&str>,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) {
|
) {
|
||||||
run_ansi_sequence(&get_command_finished_marker(stack, engine_state));
|
|
||||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
||||||
match cwd.coerce_into_string() {
|
match cwd.coerce_into_string() {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
@ -899,7 +1046,7 @@ fn do_shell_integration_finalize_command(
|
|||||||
run_ansi_sequence(&format!(
|
run_ansi_sequence(&format!(
|
||||||
"\x1b]7;file://{}{}{}\x1b\\",
|
"\x1b]7;file://{}{}{}\x1b\\",
|
||||||
percent_encoding::utf8_percent_encode(
|
percent_encoding::utf8_percent_encode(
|
||||||
&hostname.unwrap_or_else(|| "localhost".to_string()),
|
hostname.unwrap_or("localhost"),
|
||||||
percent_encoding::CONTROLS
|
percent_encoding::CONTROLS
|
||||||
),
|
),
|
||||||
if path.starts_with('/') { "" } else { "/" },
|
if path.starts_with('/') { "" } else { "/" },
|
||||||
|
@ -360,9 +360,8 @@ fn find_matching_block_end_in_expr(
|
|||||||
Expr::MatchBlock(_) => None,
|
Expr::MatchBlock(_) => None,
|
||||||
Expr::Nothing => None,
|
Expr::Nothing => None,
|
||||||
Expr::Garbage => None,
|
Expr::Garbage => None,
|
||||||
Expr::Spread(_) => None,
|
|
||||||
|
|
||||||
Expr::Table(hdr, rows) => {
|
Expr::Table(table) => {
|
||||||
if expr_last == global_cursor_offset {
|
if expr_last == global_cursor_offset {
|
||||||
// cursor is at table end
|
// cursor is at table end
|
||||||
Some(expr_first)
|
Some(expr_first)
|
||||||
@ -371,11 +370,11 @@ fn find_matching_block_end_in_expr(
|
|||||||
Some(expr_last)
|
Some(expr_last)
|
||||||
} else {
|
} else {
|
||||||
// cursor is inside table
|
// cursor is inside table
|
||||||
for inner_expr in hdr {
|
for inner_expr in table.columns.as_ref() {
|
||||||
find_in_expr_or_continue!(inner_expr);
|
find_in_expr_or_continue!(inner_expr);
|
||||||
}
|
}
|
||||||
for row in rows {
|
for row in table.rows.as_ref() {
|
||||||
for inner_expr in row {
|
for inner_expr in row.as_ref() {
|
||||||
find_in_expr_or_continue!(inner_expr);
|
find_in_expr_or_continue!(inner_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,7 +467,7 @@ fn find_matching_block_end_in_expr(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::List(inner_expr) => {
|
Expr::List(list) => {
|
||||||
if expr_last == global_cursor_offset {
|
if expr_last == global_cursor_offset {
|
||||||
// cursor is at list end
|
// cursor is at list end
|
||||||
Some(expr_first)
|
Some(expr_first)
|
||||||
@ -477,8 +476,9 @@ fn find_matching_block_end_in_expr(
|
|||||||
Some(expr_last)
|
Some(expr_last)
|
||||||
} else {
|
} else {
|
||||||
// cursor is inside list
|
// cursor is inside list
|
||||||
for inner_expr in inner_expr {
|
for item in list {
|
||||||
find_in_expr_or_continue!(inner_expr);
|
let expr = item.expr();
|
||||||
|
find_in_expr_or_continue!(expr);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
pub mod support;
|
pub mod support;
|
||||||
|
|
||||||
use nu_cli::NuCompleter;
|
use nu_cli::NuCompleter;
|
||||||
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@ -177,7 +178,7 @@ fn dotnu_completions() {
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn external_completer_trailing_space() {
|
fn external_completer_trailing_space() {
|
||||||
// https://github.com/nushell/nushell/issues/6378
|
// https://github.com/nushell/nushell/issues/6378
|
||||||
let block = "let external_completer = {|spans| $spans}";
|
let block = "{|spans| $spans}";
|
||||||
let input = "gh alias ".to_string();
|
let input = "gh alias ".to_string();
|
||||||
|
|
||||||
let suggestions = run_external_completion(block, &input);
|
let suggestions = run_external_completion(block, &input);
|
||||||
@ -847,12 +848,14 @@ fn alias_of_another_alias() {
|
|||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(expected_paths, suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||||
|
let completer = format!("$env.config.completions.external.completer = {completer}");
|
||||||
|
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, _, mut engine_state, mut stack) = new_engine();
|
let (dir, _, mut engine_state, mut stack) = new_engine();
|
||||||
let (_, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let block = parse(&mut working_set, None, block.as_bytes(), false);
|
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
||||||
assert!(working_set.parse_errors.is_empty());
|
assert!(working_set.parse_errors.is_empty());
|
||||||
|
|
||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
@ -860,16 +863,13 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
|||||||
|
|
||||||
assert!(engine_state.merge_delta(delta).is_ok());
|
assert!(engine_state.merge_delta(delta).is_ok());
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::Empty).is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
// Merge environment into the permanent state
|
||||||
assert!(engine_state.merge_env(&mut stack, &dir).is_ok());
|
assert!(engine_state.merge_env(&mut stack, &dir).is_ok());
|
||||||
|
|
||||||
let latest_block_id = engine_state.num_blocks() - 1;
|
|
||||||
|
|
||||||
// Change config adding the external completer
|
|
||||||
let mut config = engine_state.get_config().clone();
|
|
||||||
config.external_completer = Some(latest_block_id);
|
|
||||||
engine_state.set_config(config);
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instantiate a new completer
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
||||||
|
|
||||||
|
@ -5,15 +5,15 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-base"
|
name = "nu-cmd-base"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||||
version = "0.92.1"
|
version = "0.93.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.92.1" }
|
nu-path = { path = "../nu-path", version = "0.93.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
|
@ -5,8 +5,8 @@ use nu_parser::parse;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
cli_error::{report_error, report_error_new},
|
cli_error::{report_error, report_error_new},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
BlockId, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -153,11 +153,11 @@ pub fn eval_hook(
|
|||||||
// If it returns true (the default if a condition block is not specified), the hook should be run.
|
// If it returns true (the default if a condition block is not specified), the hook should be run.
|
||||||
let do_run_hook = if let Some(condition) = val.get("condition") {
|
let do_run_hook = if let Some(condition) = val.get("condition") {
|
||||||
let other_span = condition.span();
|
let other_span = condition.span();
|
||||||
if let Ok(block_id) = condition.coerce_block() {
|
if let Ok(closure) = condition.as_closure() {
|
||||||
match run_hook_block(
|
match run_hook(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
block_id,
|
closure,
|
||||||
None,
|
None,
|
||||||
arguments.clone(),
|
arguments.clone(),
|
||||||
other_span,
|
other_span,
|
||||||
@ -259,25 +259,8 @@ pub fn eval_hook(
|
|||||||
stack.remove_var(*var_id);
|
stack.remove_var(*var_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Block { val: block_id, .. } => {
|
|
||||||
run_hook_block(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
*block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
source_span,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
run_hook_block(
|
run_hook(engine_state, stack, val, input, arguments, source_span)?;
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
val.block_id,
|
|
||||||
input,
|
|
||||||
arguments,
|
|
||||||
source_span,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue {
|
return Err(ShellError::UnsupportedConfigValue {
|
||||||
@ -289,11 +272,8 @@ pub fn eval_hook(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Block { val: block_id, .. } => {
|
|
||||||
output = run_hook_block(engine_state, stack, *block_id, input, arguments, span)?;
|
|
||||||
}
|
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
output = run_hook_block(engine_state, stack, val.block_id, input, arguments, span)?;
|
output = run_hook(engine_state, stack, val, input, arguments, span)?;
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedConfigValue {
|
return Err(ShellError::UnsupportedConfigValue {
|
||||||
@ -310,20 +290,20 @@ pub fn eval_hook(
|
|||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_hook_block(
|
fn run_hook(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
block_id: BlockId,
|
closure: &Closure,
|
||||||
optional_input: Option<PipelineData>,
|
optional_input: Option<PipelineData>,
|
||||||
arguments: Vec<(String, Value)>,
|
arguments: Vec<(String, Value)>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(closure.block_id);
|
||||||
|
|
||||||
let input = optional_input.unwrap_or_else(PipelineData::empty);
|
let input = optional_input.unwrap_or_else(PipelineData::empty);
|
||||||
|
|
||||||
let mut callee_stack = stack
|
let mut callee_stack = stack
|
||||||
.gather_captures(engine_state, &block.captures)
|
.captures_to_stack_preserve_out_dest(closure.captures.clone())
|
||||||
.reset_pipes();
|
.reset_pipes();
|
||||||
|
|
||||||
for (idx, PositionalArg { var_id, .. }) in
|
for (idx, PositionalArg { var_id, .. }) in
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::RangeInclusion,
|
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_error, Range, ShellError, Span, Value,
|
report_error, Range, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::{ops::Bound, path::PathBuf};
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
pub fn get_init_cwd() -> PathBuf {
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
std::env::current_dir().unwrap_or_else(|_| {
|
||||||
@ -24,35 +23,21 @@ pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf
|
|||||||
type MakeRangeError = fn(&str, Span) -> ShellError;
|
type MakeRangeError = fn(&str, Span) -> ShellError;
|
||||||
|
|
||||||
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
||||||
let start = match &range.from {
|
match range {
|
||||||
Value::Int { val, .. } => isize::try_from(*val).unwrap_or_default(),
|
Range::IntRange(range) => {
|
||||||
Value::Nothing { .. } => 0,
|
let start = range.start().try_into().unwrap_or(0);
|
||||||
_ => {
|
let end = match range.end() {
|
||||||
return Err(|msg, span| ShellError::TypeMismatch {
|
Bound::Included(v) => v as isize,
|
||||||
err_message: msg.to_string(),
|
Bound::Excluded(v) => (v - 1) as isize,
|
||||||
span,
|
Bound::Unbounded => isize::MAX,
|
||||||
})
|
};
|
||||||
|
Ok((start, end))
|
||||||
}
|
}
|
||||||
};
|
Range::FloatRange(_) => Err(|msg, span| ShellError::TypeMismatch {
|
||||||
|
err_message: msg.to_string(),
|
||||||
let end = match &range.to {
|
span,
|
||||||
Value::Int { val, .. } => {
|
}),
|
||||||
if matches!(range.inclusion, RangeInclusion::Inclusive) {
|
}
|
||||||
isize::try_from(*val).unwrap_or(isize::max_value())
|
|
||||||
} else {
|
|
||||||
isize::try_from(*val).unwrap_or(isize::max_value()) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Nothing { .. } => isize::max_value(),
|
|
||||||
_ => {
|
|
||||||
return Err(|msg, span| ShellError::TypeMismatch {
|
|
||||||
err_message: msg.to_string(),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((start, end))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HELP_MSG: &str = "Nushell's config file can be found with the command: $nu.config-path. \
|
const HELP_MSG: &str = "Nushell's config file can be found with the command: $nu.config-path. \
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-dataframe"
|
name = "nu-cmd-dataframe"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-dataframe"
|
||||||
version = "0.92.1"
|
version = "0.93.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -13,9 +13,9 @@ version = "0.92.1"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
chrono = { workspace = true, features = ["std", "unstable-locales"], default-features = false }
|
chrono = { workspace = true, features = ["std", "unstable-locales"], default-features = false }
|
||||||
@ -25,12 +25,12 @@ indexmap = { workspace = true }
|
|||||||
num = { version = "0.4", optional = true }
|
num = { version = "0.4", optional = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
# keep sqlparser at 0.39.0 until we can update polars
|
# keep sqlparser at 0.39.0 until we can update polars
|
||||||
sqlparser = { version = "0.39.0", optional = true }
|
sqlparser = { version = "0.45", optional = true }
|
||||||
polars-io = { version = "0.37", features = ["avro"], optional = true }
|
polars-io = { version = "0.39", features = ["avro"], optional = true }
|
||||||
polars-arrow = { version = "0.37", optional = true }
|
polars-arrow = { version = "0.39", optional = true }
|
||||||
polars-ops = { version = "0.37", optional = true }
|
polars-ops = { version = "0.39", optional = true }
|
||||||
polars-plan = { version = "0.37", features = ["regex"], optional = true }
|
polars-plan = { version = "0.39", features = ["regex"], optional = true }
|
||||||
polars-utils = { version = "0.37", optional = true }
|
polars-utils = { version = "0.39", optional = true }
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
features = [
|
features = [
|
||||||
@ -65,11 +65,11 @@ features = [
|
|||||||
]
|
]
|
||||||
default-features = false
|
default-features = false
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.37"
|
version = "0.39"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "polars-utils", "sqlparser"]
|
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "polars-utils", "sqlparser"]
|
||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.92.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.93.0" }
|
||||||
|
@ -5,7 +5,7 @@ use polars::prelude::{
|
|||||||
CsvEncoding, CsvReader, IpcReader, JsonFormat, JsonReader, LazyCsvReader, LazyFileListReader,
|
CsvEncoding, CsvReader, IpcReader, JsonFormat, JsonReader, LazyCsvReader, LazyFileListReader,
|
||||||
LazyFrame, ParallelStrategy, ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
|
LazyFrame, ParallelStrategy, ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
|
||||||
};
|
};
|
||||||
use polars_io::avro::AvroReader;
|
use polars_io::{avro::AvroReader, HiveOptions};
|
||||||
use std::{fs::File, io::BufReader, path::PathBuf};
|
use std::{fs::File, io::BufReader, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -121,7 +121,9 @@ fn command(
|
|||||||
"jsonl" => from_jsonl(engine_state, stack, call),
|
"jsonl" => from_jsonl(engine_state, stack, call),
|
||||||
"avro" => from_avro(engine_state, stack, call),
|
"avro" => from_avro(engine_state, stack, call),
|
||||||
_ => Err(ShellError::FileNotFoundCustom {
|
_ => Err(ShellError::FileNotFoundCustom {
|
||||||
msg: format!("{msg}. Supported values: csv, tsv, parquet, ipc, arrow, json"),
|
msg: format!(
|
||||||
|
"{msg}. Supported values: csv, tsv, parquet, ipc, arrow, json, jsonl, avro"
|
||||||
|
),
|
||||||
span: blamed,
|
span: blamed,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -149,7 +151,7 @@ fn from_parquet(
|
|||||||
low_memory: false,
|
low_memory: false,
|
||||||
cloud_options: None,
|
cloud_options: None,
|
||||||
use_statistics: false,
|
use_statistics: false,
|
||||||
hive_partitioning: false,
|
hive_options: HiveOptions::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let df: NuLazyFrame = LazyFrame::scan_parquet(file, args)
|
let df: NuLazyFrame = LazyFrame::scan_parquet(file, args)
|
||||||
@ -244,7 +246,8 @@ fn from_ipc(
|
|||||||
cache: true,
|
cache: true,
|
||||||
rechunk: false,
|
rechunk: false,
|
||||||
row_index: None,
|
row_index: None,
|
||||||
memmap: true,
|
memory_map: true,
|
||||||
|
cloud_options: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let df: NuLazyFrame = LazyFrame::scan_ipc(file, args)
|
let df: NuLazyFrame = LazyFrame::scan_ipc(file, args)
|
||||||
|
@ -24,7 +24,7 @@ impl Command for ToNu {
|
|||||||
.switch("tail", "shows tail rows", Some('t'))
|
.switch("tail", "shows tail rows", Some('t'))
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Custom("expression".into()), Type::Any),
|
(Type::Custom("expression".into()), Type::Any),
|
||||||
(Type::Custom("dataframe".into()), Type::Table(vec![])),
|
(Type::Custom("dataframe".into()), Type::table()),
|
||||||
])
|
])
|
||||||
//.input_output_type(Type::Any, Type::Any)
|
//.input_output_type(Type::Any, Type::Any)
|
||||||
.category(Category::Custom("dataframe".into()))
|
.category(Category::Custom("dataframe".into()))
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::dataframe::values::{Column, NuDataFrame, NuExpression, NuLazyFrame};
|
use crate::dataframe::values::{Column, NuDataFrame, NuExpression, NuLazyFrame};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
use polars::chunked_array::ops::SortMultipleOptions;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LazySortBy;
|
pub struct LazySortBy;
|
||||||
@ -126,11 +127,17 @@ impl Command for LazySortBy {
|
|||||||
None => expressions.iter().map(|_| false).collect::<Vec<bool>>(),
|
None => expressions.iter().map(|_| false).collect::<Vec<bool>>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sort_options = SortMultipleOptions {
|
||||||
|
descending: reverse,
|
||||||
|
nulls_last,
|
||||||
|
multithreaded: true,
|
||||||
|
maintain_order,
|
||||||
|
};
|
||||||
|
|
||||||
let lazy = NuLazyFrame::try_from_pipeline(input, call.head)?;
|
let lazy = NuLazyFrame::try_from_pipeline(input, call.head)?;
|
||||||
let lazy = NuLazyFrame::new(
|
let lazy = NuLazyFrame::new(
|
||||||
lazy.from_eager,
|
lazy.from_eager,
|
||||||
lazy.into_polars()
|
lazy.into_polars().sort_by_exprs(&expressions, sort_options),
|
||||||
.sort_by_exprs(&expressions, reverse, nulls_last, maintain_order),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
Ok(PipelineData::Value(
|
||||||
|
@ -48,7 +48,6 @@ impl Command for ToLazyFrame {
|
|||||||
let df = NuDataFrame::try_from_iter(input.into_iter(), maybe_schema)?;
|
let df = NuDataFrame::try_from_iter(input.into_iter(), maybe_schema)?;
|
||||||
let lazy = NuLazyFrame::from_dataframe(df);
|
let lazy = NuLazyFrame::from_dataframe(df);
|
||||||
let value = Value::custom(Box::new(lazy), call.head);
|
let value = Value::custom(Box::new(lazy), call.head);
|
||||||
|
|
||||||
Ok(PipelineData::Value(value, None))
|
Ok(PipelineData::Value(value, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,19 @@ pub use operations::Axis;
|
|||||||
use super::{nu_schema::NuSchema, utils::DEFAULT_ROWS, NuLazyFrame};
|
use super::{nu_schema::NuSchema, utils::DEFAULT_ROWS, NuLazyFrame};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_protocol::{did_you_mean, PipelineData, Record, ShellError, Span, Value};
|
use nu_protocol::{did_you_mean, PipelineData, Record, ShellError, Span, Value};
|
||||||
use polars::prelude::{DataFrame, DataType, IntoLazy, LazyFrame, PolarsObject, Series};
|
use polars::{
|
||||||
|
chunked_array::ops::SortMultipleOptions,
|
||||||
|
prelude::{DataFrame, DataType, IntoLazy, LazyFrame, PolarsObject, Series},
|
||||||
|
};
|
||||||
use polars_plan::prelude::{lit, Expr, Null};
|
use polars_plan::prelude::{lit, Expr, Null};
|
||||||
use polars_utils::total_ord::TotalEq;
|
use polars_utils::total_ord::{TotalEq, TotalHash};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{cmp::Ordering, collections::HashSet, fmt::Display, hash::Hasher};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::HashSet,
|
||||||
|
fmt::Display,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
// DataFrameValue is an encapsulation of Nushell Value that can be used
|
// DataFrameValue is an encapsulation of Nushell Value that can be used
|
||||||
// to define the PolarsObject Trait. The polars object trait allows to
|
// to define the PolarsObject Trait. The polars object trait allows to
|
||||||
@ -31,6 +39,15 @@ impl DataFrameValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TotalHash for DataFrameValue {
|
||||||
|
fn tot_hash<H>(&self, state: &mut H)
|
||||||
|
where
|
||||||
|
H: Hasher,
|
||||||
|
{
|
||||||
|
(*self).hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for DataFrameValue {
|
impl Display for DataFrameValue {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.0.get_type())
|
write!(f, "{}", self.0.get_type())
|
||||||
@ -50,7 +67,7 @@ impl PartialEq for DataFrameValue {
|
|||||||
}
|
}
|
||||||
impl Eq for DataFrameValue {}
|
impl Eq for DataFrameValue {}
|
||||||
|
|
||||||
impl std::hash::Hash for DataFrameValue {
|
impl Hash for DataFrameValue {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Value::Nothing { .. } => 0.hash(state),
|
Value::Nothing { .. } => 0.hash(state),
|
||||||
@ -162,9 +179,11 @@ impl NuDataFrame {
|
|||||||
|
|
||||||
conversion::insert_record(&mut column_values, record, &maybe_schema)?
|
conversion::insert_record(&mut column_values, record, &maybe_schema)?
|
||||||
}
|
}
|
||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => conversion::insert_record(
|
||||||
conversion::insert_record(&mut column_values, *record, &maybe_schema)?
|
&mut column_values,
|
||||||
}
|
record.into_owned(),
|
||||||
|
&maybe_schema,
|
||||||
|
)?,
|
||||||
_ => {
|
_ => {
|
||||||
let key = "0".to_string();
|
let key = "0".to_string();
|
||||||
conversion::insert_value(value, key, &mut column_values, &maybe_schema)?
|
conversion::insert_value(value, key, &mut column_values, &maybe_schema)?
|
||||||
@ -472,12 +491,18 @@ impl NuDataFrame {
|
|||||||
.expect("already checked that dataframe is different than 0");
|
.expect("already checked that dataframe is different than 0");
|
||||||
|
|
||||||
// if unable to sort, then unable to compare
|
// if unable to sort, then unable to compare
|
||||||
let lhs = match self.as_ref().sort(vec![*first_col], false, false) {
|
let lhs = match self
|
||||||
|
.as_ref()
|
||||||
|
.sort(vec![*first_col], SortMultipleOptions::default())
|
||||||
|
{
|
||||||
Ok(df) => df,
|
Ok(df) => df,
|
||||||
Err(_) => return None,
|
Err(_) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let rhs = match other.as_ref().sort(vec![*first_col], false, false) {
|
let rhs = match other
|
||||||
|
.as_ref()
|
||||||
|
.sort(vec![*first_col], SortMultipleOptions::default())
|
||||||
|
{
|
||||||
Ok(df) => df,
|
Ok(df) => df,
|
||||||
Err(_) => return None,
|
Err(_) => return None,
|
||||||
};
|
};
|
||||||
|
@ -313,11 +313,15 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Result<Value, ShellError> {
|
|||||||
Expr::SortBy {
|
Expr::SortBy {
|
||||||
expr,
|
expr,
|
||||||
by,
|
by,
|
||||||
descending,
|
sort_options,
|
||||||
} => {
|
} => {
|
||||||
let by: Result<Vec<Value>, ShellError> =
|
let by: Result<Vec<Value>, ShellError> =
|
||||||
by.iter().map(|b| expr_to_value(b, span)).collect();
|
by.iter().map(|b| expr_to_value(b, span)).collect();
|
||||||
let descending: Vec<Value> = descending.iter().map(|r| Value::bool(*r, span)).collect();
|
let descending: Vec<Value> = sort_options
|
||||||
|
.descending
|
||||||
|
.iter()
|
||||||
|
.map(|r| Value::bool(*r, span))
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(Value::record(
|
Ok(Value::record(
|
||||||
record! {
|
record! {
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-extra"
|
name = "nu-cmd-extra"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||||
version = "0.92.1"
|
version = "0.93.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,13 +13,13 @@ version = "0.92.1"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.92.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.93.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-json = { version = "0.92.1", path = "../nu-json" }
|
nu-json = { version = "0.93.0", path = "../nu-json" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-pretty-hex = { version = "0.92.1", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.93.0", path = "../nu-pretty-hex" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.92.1" }
|
nu-utils = { path = "../nu-utils", version = "0.93.0" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
@ -37,6 +37,6 @@ extra = ["default"]
|
|||||||
default = []
|
default = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.92.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.93.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.92.1" }
|
nu-command = { path = "../nu-command", version = "0.93.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.92.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.93.0" }
|
||||||
|
@ -30,8 +30,8 @@ impl Command for BitsInto {
|
|||||||
(Type::Duration, Type::String),
|
(Type::Duration, Type::String),
|
||||||
(Type::String, Type::String),
|
(Type::String, Type::String),
|
||||||
(Type::Bool, Type::String),
|
(Type::Bool, Type::String),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
||||||
.rest(
|
.rest(
|
||||||
@ -64,7 +64,7 @@ impl Command for BitsInto {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "convert a binary value into a string, padded to 8 places with 0s",
|
description: "convert a binary value into a string, padded to 8 places with 0s",
|
||||||
example: "01b | into bits",
|
example: "0x[1] | into bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001",
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
|
@ -15,7 +15,7 @@ impl Command for Fmt {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("fmt")
|
Signature::build("fmt")
|
||||||
.input_output_types(vec![(Type::Number, Type::Record(vec![]))])
|
.input_output_types(vec![(Type::Number, Type::record())])
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
|
use nu_engine::{command_prelude::*, ClosureEval, ClosureEvalOnce};
|
||||||
use nu_protocol::engine::Closure;
|
use nu_protocol::engine::Closure;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -67,116 +67,56 @@ impl Command for EachWhile {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
let head = call.head;
|
||||||
|
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
let metadata = input.metadata();
|
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 eval_block_with_early_return = get_eval_block_with_early_return(&engine_state);
|
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
PipelineData::Value(Value::Range { .. }, ..)
|
PipelineData::Value(Value::Range { .. }, ..)
|
||||||
| PipelineData::Value(Value::List { .. }, ..)
|
| PipelineData::Value(Value::List { .. }, ..)
|
||||||
| PipelineData::ListStream { .. } => Ok(input
|
| PipelineData::ListStream(..) => {
|
||||||
// TODO: Could this be changed to .into_interruptible_iter(ctrlc) ?
|
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||||
.into_iter()
|
Ok(input
|
||||||
.map_while(move |x| {
|
.into_iter()
|
||||||
// with_env() is used here to ensure that each iteration uses
|
.map_while(move |value| match closure.run_with_value(value) {
|
||||||
// a different set of environment variables.
|
Ok(data) => {
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
let value = data.into_value(head);
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
(!value.is_nothing()).then_some(value)
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, x.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match eval_block_with_early_return(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
x.into_pipeline_data(),
|
|
||||||
) {
|
|
||||||
Ok(v) => {
|
|
||||||
let value = v.into_value(span);
|
|
||||||
if value.is_nothing() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
})
|
||||||
})
|
.fuse()
|
||||||
.fuse()
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
.into_pipeline_data(ctrlc)),
|
}
|
||||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::empty()),
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::empty()),
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: Some(stream),
|
stdout: Some(stream),
|
||||||
..
|
..
|
||||||
} => Ok(stream
|
} => {
|
||||||
.into_iter()
|
let mut closure = ClosureEval::new(engine_state, stack, closure);
|
||||||
.map_while(move |x| {
|
Ok(stream
|
||||||
// with_env() is used here to ensure that each iteration uses
|
.into_iter()
|
||||||
// a different set of environment variables.
|
.map_while(move |value| {
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
let value = value.ok()?;
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
match closure.run_with_value(value) {
|
||||||
|
Ok(data) => {
|
||||||
let x = match x {
|
let value = data.into_value(head);
|
||||||
Ok(x) => x,
|
(!value.is_nothing()).then_some(value)
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, x.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match eval_block_with_early_return(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
x.into_pipeline_data(),
|
|
||||||
) {
|
|
||||||
Ok(v) => {
|
|
||||||
let value = v.into_value(span);
|
|
||||||
if value.is_nothing() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value)
|
|
||||||
}
|
}
|
||||||
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
Err(_) => None,
|
})
|
||||||
}
|
.fuse()
|
||||||
})
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
.fuse()
|
}
|
||||||
.into_pipeline_data(ctrlc)),
|
|
||||||
// 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(value, ..) => {
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
ClosureEvalOnce::new(engine_state, stack, closure).run_with_value(value)
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, x.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eval_block_with_early_return(
|
|
||||||
&engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
x.into_pipeline_data(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map(|x| x.set_metadata(metadata))
|
.map(|data| data.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ fn horizontal_rotate_value(
|
|||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => {
|
||||||
let rotations = by.map(|n| n % record.len()).unwrap_or(1);
|
let rotations = by.map(|n| n % record.len()).unwrap_or(1);
|
||||||
|
|
||||||
let (mut cols, mut vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
|
let (mut cols, mut vals): (Vec<_>, Vec<_>) = record.into_owned().into_iter().unzip();
|
||||||
if !cells_only {
|
if !cells_only {
|
||||||
match direction {
|
match direction {
|
||||||
HorizontalDirection::Right => cols.rotate_right(rotations),
|
HorizontalDirection::Right => cols.rotate_right(rotations),
|
||||||
|
@ -16,7 +16,7 @@ impl Command for RollDown {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
// TODO: It also operates on List
|
// TODO: It also operates on List
|
||||||
.input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))])
|
.input_output_types(vec![(Type::table(), Type::table())])
|
||||||
.named("by", SyntaxShape::Int, "Number of rows to roll", Some('b'))
|
.named("by", SyntaxShape::Int, "Number of rows to roll", Some('b'))
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ impl Command for RollLeft {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
])
|
])
|
||||||
.named(
|
.named(
|
||||||
"by",
|
"by",
|
||||||
|
@ -16,8 +16,8 @@ impl Command for RollRight {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
])
|
])
|
||||||
.named(
|
.named(
|
||||||
"by",
|
"by",
|
||||||
|
@ -16,7 +16,7 @@ impl Command for RollUp {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
// TODO: It also operates on List
|
// TODO: It also operates on List
|
||||||
.input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))])
|
.input_output_types(vec![(Type::table(), Type::table())])
|
||||||
.named("by", SyntaxShape::Int, "Number of rows to roll", Some('b'))
|
.named("by", SyntaxShape::Int, "Number of rows to roll", Some('b'))
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ impl Command for Rotate {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("rotate")
|
Signature::build("rotate")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Record(vec![]), Type::Table(vec![])),
|
(Type::record(), Type::table()),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
])
|
])
|
||||||
.switch("ccw", "rotate counter clockwise", None)
|
.switch("ccw", "rotate counter clockwise", None)
|
||||||
.rest(
|
.rest(
|
||||||
@ -171,7 +171,7 @@ pub fn rotate(
|
|||||||
let span = val.span();
|
let span = val.span();
|
||||||
match val {
|
match val {
|
||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => {
|
||||||
let (cols, vals): (Vec<_>, Vec<_>) = record.into_iter().unzip();
|
let (cols, vals): (Vec<_>, Vec<_>) = record.into_owned().into_iter().unzip();
|
||||||
old_column_names = cols;
|
old_column_names = cols;
|
||||||
new_values.extend_from_slice(&vals);
|
new_values.extend_from_slice(&vals);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, EvalBlockFn};
|
use nu_engine::{command_prelude::*, ClosureEval};
|
||||||
use nu_protocol::{ast::Block, engine::Closure, PipelineIterator};
|
use nu_protocol::{engine::Closure, PipelineIterator};
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UpdateCells;
|
pub struct UpdateCells;
|
||||||
@ -12,7 +12,7 @@ impl Command for UpdateCells {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("update cells")
|
Signature::build("update cells")
|
||||||
.input_output_types(vec![(Type::Table(vec![]), Type::Table(vec![]))])
|
.input_output_types(vec![(Type::table(), Type::table())])
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"closure",
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||||
@ -87,24 +87,9 @@ impl Command for UpdateCells {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
// the block to run on each cell
|
let head = call.head;
|
||||||
let engine_state = engine_state.clone();
|
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||||
let block: Closure = call.req(&engine_state, stack, 0)?;
|
let columns: Option<Value> = call.get_flag(engine_state, stack, "columns")?;
|
||||||
let mut stack = stack.captures_to_stack(block.captures);
|
|
||||||
let orig_env_vars = stack.env_vars.clone();
|
|
||||||
let orig_env_hidden = stack.env_hidden.clone();
|
|
||||||
|
|
||||||
let metadata = input.metadata();
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
let block: Arc<Block> = engine_state.get_block(block.block_id).clone();
|
|
||||||
let eval_block_fn = get_eval_block(&engine_state);
|
|
||||||
|
|
||||||
let span = call.head;
|
|
||||||
|
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
|
||||||
|
|
||||||
// the columns to update
|
|
||||||
let columns: Option<Value> = call.get_flag(&engine_state, &mut stack, "columns")?;
|
|
||||||
let columns: Option<HashSet<String>> = match columns {
|
let columns: Option<HashSet<String>> = match columns {
|
||||||
Some(val) => Some(
|
Some(val) => Some(
|
||||||
val.into_list()?
|
val.into_list()?
|
||||||
@ -115,27 +100,23 @@ impl Command for UpdateCells {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let metadata = input.metadata();
|
||||||
|
|
||||||
Ok(UpdateCellIterator {
|
Ok(UpdateCellIterator {
|
||||||
input: input.into_iter(),
|
iter: input.into_iter(),
|
||||||
engine_state,
|
closure: ClosureEval::new(engine_state, stack, closure),
|
||||||
stack,
|
|
||||||
block,
|
|
||||||
columns,
|
columns,
|
||||||
span,
|
span: head,
|
||||||
eval_block_fn,
|
|
||||||
}
|
}
|
||||||
.into_pipeline_data(ctrlc)
|
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||||
.set_metadata(metadata))
|
.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UpdateCellIterator {
|
struct UpdateCellIterator {
|
||||||
input: PipelineIterator,
|
iter: PipelineIterator,
|
||||||
|
closure: ClosureEval,
|
||||||
columns: Option<HashSet<String>>,
|
columns: Option<HashSet<String>>,
|
||||||
engine_state: EngineState,
|
|
||||||
stack: Stack,
|
|
||||||
block: Arc<Block>,
|
|
||||||
eval_block_fn: EvalBlockFn,
|
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,69 +124,36 @@ impl Iterator for UpdateCellIterator {
|
|||||||
type Item = Value;
|
type Item = Value;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.input.next() {
|
let mut value = self.iter.next()?;
|
||||||
Some(val) => {
|
|
||||||
if let Some(ref cols) = self.columns {
|
let value = if let Value::Record { val, .. } = &mut value {
|
||||||
if !val.columns().any(|c| cols.contains(c)) {
|
let val = val.to_mut();
|
||||||
return Some(val);
|
if let Some(columns) = &self.columns {
|
||||||
|
for (col, val) in val.iter_mut() {
|
||||||
|
if columns.contains(col) {
|
||||||
|
*val = eval_value(&mut self.closure, self.span, std::mem::take(val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
let span = val.span();
|
for (_, val) in val.iter_mut() {
|
||||||
match val {
|
*val = eval_value(&mut self.closure, self.span, std::mem::take(val))
|
||||||
Value::Record { val, .. } => Some(Value::record(
|
|
||||||
val.into_iter()
|
|
||||||
.map(|(col, val)| match &self.columns {
|
|
||||||
Some(cols) if !cols.contains(&col) => (col, val),
|
|
||||||
_ => (
|
|
||||||
col,
|
|
||||||
process_cell(
|
|
||||||
val,
|
|
||||||
&self.engine_state,
|
|
||||||
&mut self.stack,
|
|
||||||
&self.block,
|
|
||||||
span,
|
|
||||||
self.eval_block_fn,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
span,
|
|
||||||
)),
|
|
||||||
val => Some(process_cell(
|
|
||||||
val,
|
|
||||||
&self.engine_state,
|
|
||||||
&mut self.stack,
|
|
||||||
&self.block,
|
|
||||||
self.span,
|
|
||||||
self.eval_block_fn,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => None,
|
|
||||||
}
|
value
|
||||||
|
} else {
|
||||||
|
eval_value(&mut self.closure, self.span, value)
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
fn eval_value(closure: &mut ClosureEval, span: Span, value: Value) -> Value {
|
||||||
fn process_cell(
|
closure
|
||||||
val: Value,
|
.run_with_value(value)
|
||||||
engine_state: &EngineState,
|
.map(|data| data.into_value(span))
|
||||||
stack: &mut Stack,
|
.unwrap_or_else(|err| Value::error(err, span))
|
||||||
block: &Block,
|
|
||||||
span: Span,
|
|
||||||
eval_block_fn: EvalBlockFn,
|
|
||||||
) -> Value {
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, val.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match eval_block_fn(engine_state, stack, block, val.into_pipeline_data()) {
|
|
||||||
Ok(pd) => pd.into_value(span),
|
|
||||||
Err(e) => Value::error(e, span),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -10,7 +10,7 @@ impl Command for FromUrl {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("from url")
|
Signature::build("from url")
|
||||||
.input_output_types(vec![(Type::String, Type::Record(vec![]))])
|
.input_output_types(vec![(Type::String, Type::record())])
|
||||||
.category(Category::Formats)
|
.category(Category::Formats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ impl Command for SubCommand {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.category(Category::Platform)
|
.category(Category::Platform)
|
||||||
|
@ -17,8 +17,8 @@ impl Command for DecodeHex {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -17,8 +17,8 @@ impl Command for EncodeHex {
|
|||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -13,8 +13,8 @@ impl Command for FormatPattern {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("format pattern")
|
Signature::build("format pattern")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
|
(Type::table(), Type::List(Box::new(Type::String))),
|
||||||
(Type::Record(vec![]), Type::Any),
|
(Type::record(), Type::Any),
|
||||||
])
|
])
|
||||||
.required(
|
.required(
|
||||||
"pattern",
|
"pattern",
|
||||||
|
@ -18,8 +18,8 @@ impl Command for SubCommand {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -14,8 +14,8 @@ impl Command for SubCommand {
|
|||||||
Signature::build("str kebab-case")
|
Signature::build("str kebab-case")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::String, Type::String),
|
(Type::String, Type::String),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
(
|
(
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
|
@ -14,8 +14,8 @@ impl Command for SubCommand {
|
|||||||
Signature::build("str pascal-case")
|
Signature::build("str pascal-case")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::String, Type::String),
|
(Type::String, Type::String),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
(
|
(
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
|
@ -18,8 +18,8 @@ impl Command for SubCommand {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -18,8 +18,8 @@ impl Command for SubCommand {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -18,8 +18,8 @@ impl Command for SubCommand {
|
|||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
Type::List(Box::new(Type::String)),
|
Type::List(Box::new(Type::String)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -6,22 +6,22 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.92.1"
|
version = "0.93.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.92.1" }
|
nu-utils = { path = "../nu-utils", version = "0.93.0" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
shadow-rs = { version = "0.26", default-features = false }
|
shadow-rs = { version = "0.27", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.26", default-features = false }
|
shadow-rs = { version = "0.27", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mimalloc = []
|
mimalloc = []
|
||||||
@ -30,4 +30,4 @@ trash-support = []
|
|||||||
sqlite = []
|
sqlite = []
|
||||||
dataframe = []
|
dataframe = []
|
||||||
static-link-openssl = []
|
static-link-openssl = []
|
||||||
wasi = []
|
system-clipboard = []
|
||||||
|
@ -19,14 +19,14 @@ impl Command for Collect {
|
|||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"keep-env",
|
"keep-env",
|
||||||
"let the block affect environment variables",
|
"let the closure affect environment variables",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Collect the stream and pass it to a block."
|
"Collect a stream into a value and then run a closure with the collected value as input."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -36,14 +36,14 @@ impl Command for Collect {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let capture_block: Closure = call.req(engine_state, stack, 0)?;
|
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
let block = engine_state.get_block(capture_block.block_id).clone();
|
let block = engine_state.get_block(closure.block_id);
|
||||||
let mut stack_captures =
|
let mut stack_captures =
|
||||||
stack.captures_to_stack_preserve_stdio(capture_block.captures.clone());
|
stack.captures_to_stack_preserve_out_dest(closure.captures.clone());
|
||||||
|
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
let input: Value = input.into_value(call.head);
|
let input = input.into_value(call.head);
|
||||||
|
|
||||||
let mut saved_positional = None;
|
let mut saved_positional = None;
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
@ -58,7 +58,7 @@ impl Command for Collect {
|
|||||||
let result = eval_block(
|
let result = eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut stack_captures,
|
&mut stack_captures,
|
||||||
&block,
|
block,
|
||||||
input.into_pipeline_data(),
|
input.into_pipeline_data(),
|
||||||
)
|
)
|
||||||
.map(|x| x.set_metadata(metadata));
|
.map(|x| x.set_metadata(metadata));
|
||||||
@ -67,7 +67,7 @@ impl Command for Collect {
|
|||||||
redirect_env(engine_state, stack, &stack_captures);
|
redirect_env(engine_state, stack, &stack_captures);
|
||||||
// for when we support `data | let x = $in;`
|
// for when we support `data | let x = $in;`
|
||||||
// remove the variables added earlier
|
// remove the variables added earlier
|
||||||
for (var_id, _) in capture_block.captures {
|
for (var_id, _) in closure.captures {
|
||||||
stack_captures.remove_var(var_id);
|
stack_captures.remove_var(var_id);
|
||||||
}
|
}
|
||||||
if let Some(u) = saved_positional {
|
if let Some(u) = saved_positional {
|
||||||
@ -80,11 +80,18 @@ impl Command for Collect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Use the second value in the stream",
|
Example {
|
||||||
example: "[1 2 3] | collect { |x| $x.1 }",
|
description: "Use the second value in the stream",
|
||||||
result: Some(Value::test_int(2)),
|
example: "[1 2 3] | collect { |x| $x.1 }",
|
||||||
}]
|
result: Some(Value::test_int(2)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Read and write to the same file",
|
||||||
|
example: "open file.txt | collect { save -f file.txt }",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{
|
use nu_protocol::{engine::StateWorkingSet, PipelineMetadata};
|
||||||
engine::{Closure, StateWorkingSet},
|
|
||||||
PipelineMetadata,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Describe;
|
pub struct Describe;
|
||||||
@ -49,6 +46,19 @@ impl Command for Describe {
|
|||||||
detailed: call.has_flag(engine_state, stack, "detailed")?,
|
detailed: call.has_flag(engine_state, stack, "detailed")?,
|
||||||
collect_lazyrecords: call.has_flag(engine_state, stack, "collect-lazyrecords")?,
|
collect_lazyrecords: call.has_flag(engine_state, stack, "collect-lazyrecords")?,
|
||||||
};
|
};
|
||||||
|
if options.collect_lazyrecords {
|
||||||
|
nu_protocol::report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Deprecated flag".into(),
|
||||||
|
msg: "the `--collect-lazyrecords` flag is deprecated, since lazy records will be removed in 0.94.0"
|
||||||
|
.into(),
|
||||||
|
span: Some(call.head),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
run(Some(engine_state), call, input, options)
|
run(Some(engine_state), call, input, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +277,7 @@ fn compact_primitive_description(mut value: Value) -> Value {
|
|||||||
if val.len() != 1 {
|
if val.len() != 1 {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if let Some(type_name) = val.get_mut("type") {
|
if let Some(type_name) = val.to_mut().get_mut("type") {
|
||||||
return std::mem::take(type_name);
|
return std::mem::take(type_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,7 +313,8 @@ fn describe_value(
|
|||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
Value::Record { mut val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
|
let mut val = val.into_owned();
|
||||||
for (_k, v) in val.iter_mut() {
|
for (_k, v) in val.iter_mut() {
|
||||||
*v = compact_primitive_description(describe_value(
|
*v = compact_primitive_description(describe_value(
|
||||||
std::mem::take(v),
|
std::mem::take(v),
|
||||||
@ -317,7 +328,7 @@ fn describe_value(
|
|||||||
record!(
|
record!(
|
||||||
"type" => Value::string("record", head),
|
"type" => Value::string("record", head),
|
||||||
"lazy" => Value::bool(false, head),
|
"lazy" => Value::bool(false, head),
|
||||||
"columns" => Value::record(*val, head),
|
"columns" => Value::record(val, head),
|
||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
)
|
)
|
||||||
@ -335,16 +346,12 @@ fn describe_value(
|
|||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
Value::Block { val, .. }
|
Value::Closure { val, .. } => {
|
||||||
| Value::Closure {
|
let block = engine_state.map(|engine_state| engine_state.get_block(val.block_id));
|
||||||
val: Closure { block_id: val, .. },
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let block = engine_state.map(|engine_state| engine_state.get_block(val));
|
|
||||||
|
|
||||||
if let Some(block) = block {
|
if let Some(block) = block {
|
||||||
let mut record = Record::new();
|
let mut record = Record::new();
|
||||||
record.push("type", Value::string(value.get_type().to_string(), head));
|
record.push("type", Value::string("closure", head));
|
||||||
record.push(
|
record.push(
|
||||||
"signature",
|
"signature",
|
||||||
Value::record(
|
Value::record(
|
||||||
@ -395,10 +402,11 @@ fn describe_value(
|
|||||||
|
|
||||||
if options.collect_lazyrecords {
|
if options.collect_lazyrecords {
|
||||||
let collected = val.collect()?;
|
let collected = val.collect()?;
|
||||||
if let Value::Record { mut val, .. } =
|
if let Value::Record { val, .. } =
|
||||||
describe_value(collected, head, engine_state, options)?
|
describe_value(collected, head, engine_state, options)?
|
||||||
{
|
{
|
||||||
record.push("length", Value::int(val.len() as i64, head));
|
let mut val = Record::clone(&val);
|
||||||
|
|
||||||
for (_k, v) in val.iter_mut() {
|
for (_k, v) in val.iter_mut() {
|
||||||
*v = compact_primitive_description(describe_value(
|
*v = compact_primitive_description(describe_value(
|
||||||
std::mem::take(v),
|
std::mem::take(v),
|
||||||
@ -408,7 +416,8 @@ fn describe_value(
|
|||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
record.push("columns", Value::record(*val, head));
|
record.push("length", Value::int(val.len() as i64, head));
|
||||||
|
record.push("columns", Value::record(val, head));
|
||||||
} else {
|
} else {
|
||||||
let cols = val.column_names();
|
let cols = val.column_names();
|
||||||
record.push("length", Value::int(cols.len() as i64, head));
|
record.push("length", Value::int(cols.len() as i64, head));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_env};
|
use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_env};
|
||||||
use nu_protocol::{engine::Closure, IoStream, ListStream, RawStream};
|
use nu_protocol::{engine::Closure, ListStream, OutDest, RawStream};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -72,7 +72,7 @@ impl Command for Do {
|
|||||||
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
|
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
|
||||||
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
|
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
|
||||||
|
|
||||||
let mut callee_stack = caller_stack.captures_to_stack_preserve_stdio(block.captures);
|
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(block.captures);
|
||||||
let block = engine_state.get_block(block.block_id);
|
let block = engine_state.get_block(block.block_id);
|
||||||
|
|
||||||
bind_args_to(&mut callee_stack, &block.signature, rest, call.head)?;
|
bind_args_to(&mut callee_stack, &block.signature, rest, call.head)?;
|
||||||
@ -117,7 +117,7 @@ impl Command for Do {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_err(|e| e.into_spanned(call.head))
|
.err_span(call.head)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ impl Command for Do {
|
|||||||
metadata,
|
metadata,
|
||||||
trim_end_newline,
|
trim_end_newline,
|
||||||
}) if ignore_program_errors
|
}) if ignore_program_errors
|
||||||
&& !matches!(caller_stack.stdout(), IoStream::Pipe | IoStream::Capture) =>
|
&& !matches!(caller_stack.stdout(), OutDest::Pipe | OutDest::Capture) =>
|
||||||
{
|
{
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout,
|
stdout,
|
||||||
|
@ -20,8 +20,10 @@ impl Command for Echo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"When given no arguments, it returns an empty string. When given one argument,
|
r#"Unlike `print`, which prints unstructured text to stdout, `echo` is like an
|
||||||
it returns it. Otherwise, it returns a list of the arguments. There is usually
|
identity function and simply returns its arguments. When given no arguments,
|
||||||
|
it returns an empty string. When given one argument, it returns it as a
|
||||||
|
nushell value. Otherwise, it returns a list of the arguments. There is usually
|
||||||
little reason to use this over just writing the values as-is."#
|
little reason to use this over just writing the values as-is."#
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
||||||
use nu_protocol::{engine::Block, ListStream};
|
use nu_protocol::ListStream;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct For;
|
pub struct For;
|
||||||
@ -66,22 +66,27 @@ impl Command for For {
|
|||||||
.as_keyword()
|
.as_keyword()
|
||||||
.expect("internal error: missing keyword");
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
|
let block_id = call
|
||||||
|
.positional_nth(2)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
let eval_expression = get_eval_expression(engine_state);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
|
||||||
let values = eval_expression(engine_state, stack, keyword_expr)?;
|
let value = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
|
|
||||||
let block: Block = call.req(engine_state, stack, 2)?;
|
|
||||||
|
|
||||||
let numbered = call.has_flag(engine_state, stack, "numbered")?;
|
let numbered = call.has_flag(engine_state, stack, "numbered")?;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = engine_state.clone();
|
let engine_state = engine_state.clone();
|
||||||
let block = engine_state.get_block(block.block_id).clone();
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let stack = &mut stack.push_redirection(None, None);
|
let stack = &mut stack.push_redirection(None, None);
|
||||||
|
|
||||||
match values {
|
let span = value.span();
|
||||||
|
match value {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for (idx, x) in ListStream::from_stream(vals.into_iter(), ctrlc).enumerate() {
|
for (idx, x) in ListStream::from_stream(vals.into_iter(), ctrlc).enumerate() {
|
||||||
// with_env() is used here to ensure that each iteration uses
|
// with_env() is used here to ensure that each iteration uses
|
||||||
@ -103,7 +108,7 @@ impl Command for For {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, &block, PipelineData::empty()) {
|
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -125,7 +130,7 @@ impl Command for For {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
for (idx, x) in val.into_range_iter(ctrlc)?.enumerate() {
|
for (idx, x) in val.into_range_iter(span, ctrlc).enumerate() {
|
||||||
stack.add_var(
|
stack.add_var(
|
||||||
var_id,
|
var_id,
|
||||||
if numbered {
|
if numbered {
|
||||||
@ -141,7 +146,7 @@ impl Command for For {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, &block, PipelineData::empty()) {
|
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -165,7 +170,7 @@ impl Command for For {
|
|||||||
x => {
|
x => {
|
||||||
stack.add_var(var_id, x);
|
stack.add_var(var_id, x);
|
||||||
|
|
||||||
eval_block(&engine_state, stack, &block, PipelineData::empty())?.into_value(head);
|
eval_block(&engine_state, stack, block, PipelineData::empty())?.into_value(head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
|
@ -41,11 +41,7 @@ impl Command for HideEnv {
|
|||||||
|
|
||||||
for name in env_var_names {
|
for name in env_var_names {
|
||||||
if !stack.remove_env_var(engine_state, &name.item) && !ignore_errors {
|
if !stack.remove_env_var(engine_state, &name.item) && !ignore_errors {
|
||||||
let all_names: Vec<String> = stack
|
let all_names = stack.get_env_var_names(engine_state);
|
||||||
.get_env_var_names(engine_state)
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
if let Some(closest_match) = did_you_mean(&all_names, &name.item) {
|
if let Some(closest_match) = did_you_mean(&all_names, &name.item) {
|
||||||
return Err(ShellError::DidYouMeanCustom {
|
return Err(ShellError::DidYouMeanCustom {
|
||||||
msg: format!("Environment variable '{}' not found", name.item),
|
msg: format!("Environment variable '{}' not found", name.item),
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::{
|
|||||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Block, StateWorkingSet},
|
engine::StateWorkingSet,
|
||||||
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
|
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,46 +52,34 @@ impl Command for If {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let then_block: Block = call.req_const(working_set, 1)?;
|
let then_block = call
|
||||||
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
let else_case = call.positional_nth(2);
|
let else_case = call.positional_nth(2);
|
||||||
|
|
||||||
let result = eval_constant(working_set, cond)?;
|
if eval_constant(working_set, cond)?.as_bool()? {
|
||||||
match &result {
|
let block = working_set.get_block(then_block);
|
||||||
Value::Bool { val, .. } => {
|
eval_const_subexpression(working_set, block, input, block.span.unwrap_or(call.head))
|
||||||
if *val {
|
} else if let Some(else_case) = else_case {
|
||||||
let block = working_set.get_block(then_block.block_id);
|
if let Some(else_expr) = else_case.as_keyword() {
|
||||||
|
if let Some(block_id) = else_expr.as_block() {
|
||||||
|
let block = working_set.get_block(block_id);
|
||||||
eval_const_subexpression(
|
eval_const_subexpression(
|
||||||
working_set,
|
working_set,
|
||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
block.span.unwrap_or(call.head),
|
block.span.unwrap_or(call.head),
|
||||||
)
|
)
|
||||||
} else if let Some(else_case) = else_case {
|
|
||||||
if let Some(else_expr) = else_case.as_keyword() {
|
|
||||||
if let Some(block_id) = else_expr.as_block() {
|
|
||||||
let block = working_set.get_block(block_id);
|
|
||||||
eval_const_subexpression(
|
|
||||||
working_set,
|
|
||||||
block,
|
|
||||||
input,
|
|
||||||
block.span.unwrap_or(call.head),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
eval_constant_with_input(working_set, else_expr, input)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eval_constant_with_input(working_set, else_case, input)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(PipelineData::empty())
|
eval_constant_with_input(working_set, else_expr, input)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
eval_constant_with_input(working_set, else_case, input)
|
||||||
}
|
}
|
||||||
x => Err(ShellError::CantConvert {
|
} else {
|
||||||
to_type: "bool".into(),
|
Ok(PipelineData::empty())
|
||||||
from_type: x.get_type().to_string(),
|
|
||||||
span: result.span(),
|
|
||||||
help: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,41 +91,34 @@ impl Command for If {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let then_block: Block = call.req(engine_state, stack, 1)?;
|
let then_block = call
|
||||||
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
let else_case = call.positional_nth(2);
|
let else_case = call.positional_nth(2);
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
let eval_expression = get_eval_expression(engine_state);
|
||||||
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
|
||||||
let result = eval_expression(engine_state, stack, cond)?;
|
if eval_expression(engine_state, stack, cond)?.as_bool()? {
|
||||||
match &result {
|
let block = engine_state.get_block(then_block);
|
||||||
Value::Bool { val, .. } => {
|
eval_block(engine_state, stack, block, input)
|
||||||
if *val {
|
} else if let Some(else_case) = else_case {
|
||||||
let block = engine_state.get_block(then_block.block_id);
|
if let Some(else_expr) = else_case.as_keyword() {
|
||||||
|
if let Some(block_id) = else_expr.as_block() {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
eval_block(engine_state, stack, block, input)
|
eval_block(engine_state, stack, block, input)
|
||||||
} else if let Some(else_case) = else_case {
|
|
||||||
if let Some(else_expr) = else_case.as_keyword() {
|
|
||||||
if let Some(block_id) = else_expr.as_block() {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
eval_block(engine_state, stack, block, input)
|
|
||||||
} else {
|
|
||||||
eval_expression_with_input(engine_state, stack, else_expr, input)
|
|
||||||
.map(|res| res.0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eval_expression_with_input(engine_state, stack, else_case, input)
|
|
||||||
.map(|res| res.0)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Ok(PipelineData::empty())
|
eval_expression_with_input(engine_state, stack, else_expr, input)
|
||||||
|
.map(|res| res.0)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
eval_expression_with_input(engine_state, stack, else_case, input).map(|res| res.0)
|
||||||
}
|
}
|
||||||
x => Err(ShellError::CantConvert {
|
} else {
|
||||||
to_type: "bool".into(),
|
Ok(PipelineData::empty())
|
||||||
from_type: x.get_type().to_string(),
|
|
||||||
span: result.span(),
|
|
||||||
help: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{engine::StateWorkingSet, IoStream};
|
use nu_protocol::{engine::StateWorkingSet, OutDest};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Ignore;
|
pub struct Ignore;
|
||||||
@ -56,8 +56,8 @@ impl Command for Ignore {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
|
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
|
||||||
(Some(IoStream::Null), None)
|
(Some(OutDest::Null), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ impl Command for LazyMake {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("lazy make")
|
Signature::build("lazy make")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
|
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||||
.required_named(
|
.required_named(
|
||||||
"columns",
|
"columns",
|
||||||
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
||||||
@ -54,6 +54,18 @@ impl Command for LazyMake {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
nu_protocol::report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Deprecated command".into(),
|
||||||
|
msg: "warning: lazy records and the `lazy make` command will be removed in 0.94.0"
|
||||||
|
.into(),
|
||||||
|
span: Some(call.head),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let columns: Vec<Spanned<String>> = call
|
let columns: Vec<Spanned<String>> = call
|
||||||
.get_flag(engine_state, stack, "columns")?
|
.get_flag(engine_state, stack, "columns")?
|
||||||
@ -80,7 +92,7 @@ impl Command for LazyMake {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stack = stack.clone().reset_stdio().capture();
|
let stack = stack.clone().reset_out_dest().capture();
|
||||||
|
|
||||||
Ok(Value::lazy_record(
|
Ok(Value::lazy_record(
|
||||||
Box::new(NuLazyRecord {
|
Box::new(NuLazyRecord {
|
||||||
|
@ -61,7 +61,7 @@ impl Command for Let {
|
|||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
let stack = &mut stack.start_capture();
|
let stack = &mut stack.start_capture();
|
||||||
let pipeline_data = eval_block(engine_state, stack, block, input)?;
|
let pipeline_data = eval_block(engine_state, stack, block, input)?;
|
||||||
let mut value = pipeline_data.into_value(call.head);
|
let value = pipeline_data.into_value(call.head);
|
||||||
|
|
||||||
// if given variable type is Glob, and our result is string
|
// if given variable type is Glob, and our result is string
|
||||||
// then nushell need to convert from Value::String to Value::Glob
|
// then nushell need to convert from Value::String to Value::Glob
|
||||||
@ -69,12 +69,12 @@ impl Command for Let {
|
|||||||
// if we pass it to other commands.
|
// if we pass it to other commands.
|
||||||
let var_type = &engine_state.get_var(var_id).ty;
|
let var_type = &engine_state.get_var(var_id).ty;
|
||||||
let val_span = value.span();
|
let val_span = value.span();
|
||||||
match value {
|
let value = match value {
|
||||||
Value::String { val, .. } if var_type == &Type::Glob => {
|
Value::String { val, .. } if var_type == &Type::Glob => {
|
||||||
value = Value::glob(val, false, val_span);
|
Value::glob(val, false, val_span)
|
||||||
}
|
}
|
||||||
_ => {}
|
value => value,
|
||||||
}
|
};
|
||||||
|
|
||||||
stack.add_var(var_id, value);
|
stack.add_var(var_id, value);
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block};
|
use nu_engine::{command_prelude::*, get_eval_block};
|
||||||
use nu_protocol::engine::Block;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Loop;
|
pub struct Loop;
|
||||||
@ -28,7 +27,13 @@ impl Command for Loop {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let block: Block = call.req(engine_state, stack, 0)?;
|
let block_id = call
|
||||||
|
.positional_nth(0)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
|
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
|
||||||
let stack = &mut stack.push_redirection(None, None);
|
let stack = &mut stack.push_redirection(None, None);
|
||||||
@ -38,8 +43,6 @@ impl Command for Loop {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let block = engine_state.get_block(block.block_id);
|
|
||||||
|
|
||||||
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
break;
|
break;
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use nu_engine::{
|
use nu_engine::{
|
||||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::engine::Matcher;
|
||||||
ast::{Expr, Expression},
|
|
||||||
engine::Matcher,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Match;
|
pub struct Match;
|
||||||
@ -38,45 +35,45 @@ impl Command for Match {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
let block = call.positional_nth(1);
|
let matches = call
|
||||||
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_match_block()
|
||||||
|
.expect("missing match block");
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
let eval_expression = get_eval_expression(engine_state);
|
||||||
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
|
||||||
if let Some(Expression {
|
let mut match_variables = vec![];
|
||||||
expr: Expr::MatchBlock(matches),
|
for (pattern, expr) in matches {
|
||||||
..
|
if pattern.match_value(&value, &mut match_variables) {
|
||||||
}) = block
|
// This case does match, go ahead and return the evaluated expression
|
||||||
{
|
for (id, value) in match_variables.drain(..) {
|
||||||
for match_ in matches {
|
stack.add_var(id, value);
|
||||||
let mut match_variables = vec![];
|
}
|
||||||
if match_.0.match_value(&value, &mut match_variables) {
|
|
||||||
// This case does match, go ahead and return the evaluated expression
|
|
||||||
for match_variable in match_variables {
|
|
||||||
stack.add_var(match_variable.0, match_variable.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let guard_matches = if let Some(guard) = &match_.0.guard {
|
let guard_matches = if let Some(guard) = &pattern.guard {
|
||||||
let Value::Bool { val, .. } = eval_expression(engine_state, stack, guard)?
|
let Value::Bool { val, .. } = eval_expression(engine_state, stack, guard)?
|
||||||
else {
|
else {
|
||||||
return Err(ShellError::MatchGuardNotBool { span: guard.span });
|
return Err(ShellError::MatchGuardNotBool { span: guard.span });
|
||||||
};
|
|
||||||
|
|
||||||
val
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if guard_matches {
|
val
|
||||||
return if let Some(block_id) = match_.1.as_block() {
|
} else {
|
||||||
let block = engine_state.get_block(block_id);
|
true
|
||||||
eval_block(engine_state, stack, block, input)
|
};
|
||||||
} else {
|
|
||||||
eval_expression_with_input(engine_state, stack, &match_.1, input)
|
if guard_matches {
|
||||||
.map(|x| x.0)
|
return if let Some(block_id) = expr.as_block() {
|
||||||
};
|
let block = engine_state.get_block(block_id);
|
||||||
}
|
eval_block(engine_state, stack, block, input)
|
||||||
|
} else {
|
||||||
|
eval_expression_with_input(engine_state, stack, expr, input).map(|x| x.0)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
match_variables.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +71,3 @@ pub use try_::Try;
|
|||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
pub use version::Version;
|
pub use version::Version;
|
||||||
pub use while_::While;
|
pub use while_::While;
|
||||||
|
|
||||||
mod plugin;
|
|
||||||
mod plugin_list;
|
|
||||||
mod plugin_stop;
|
|
||||||
mod register;
|
|
||||||
|
|
||||||
pub use plugin::PluginCommand;
|
|
||||||
pub use plugin_list::PluginList;
|
|
||||||
pub use plugin_stop::PluginStop;
|
|
||||||
pub use register::Register;
|
|
||||||
|
@ -61,7 +61,7 @@ impl Command for Mut {
|
|||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
let stack = &mut stack.start_capture();
|
let stack = &mut stack.start_capture();
|
||||||
let pipeline_data = eval_block(engine_state, stack, block, input)?;
|
let pipeline_data = eval_block(engine_state, stack, block, input)?;
|
||||||
let mut value = pipeline_data.into_value(call.head);
|
let value = pipeline_data.into_value(call.head);
|
||||||
|
|
||||||
// if given variable type is Glob, and our result is string
|
// if given variable type is Glob, and our result is string
|
||||||
// then nushell need to convert from Value::String to Value::Glob
|
// then nushell need to convert from Value::String to Value::Glob
|
||||||
@ -69,12 +69,12 @@ impl Command for Mut {
|
|||||||
// if we pass it to other commands.
|
// if we pass it to other commands.
|
||||||
let var_type = &engine_state.get_var(var_id).ty;
|
let var_type = &engine_state.get_var(var_id).ty;
|
||||||
let val_span = value.span();
|
let val_span = value.span();
|
||||||
match value {
|
let value = match value {
|
||||||
Value::String { val, .. } if var_type == &Type::Glob => {
|
Value::String { val, .. } if var_type == &Type::Glob => {
|
||||||
value = Value::glob(val, false, val_span);
|
Value::glob(val, false, val_span)
|
||||||
}
|
}
|
||||||
_ => {}
|
value => value,
|
||||||
}
|
};
|
||||||
|
|
||||||
stack.add_var(var_id, value);
|
stack.add_var(var_id, value);
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
|
@ -40,17 +40,11 @@ impl Command for Return {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let return_value: Option<Value> = call.opt(engine_state, stack, 0)?;
|
let return_value: Option<Value> = call.opt(engine_state, stack, 0)?;
|
||||||
if let Some(value) = return_value {
|
let value = return_value.unwrap_or(Value::nothing(call.head));
|
||||||
Err(ShellError::Return {
|
Err(ShellError::Return {
|
||||||
span: call.head,
|
span: call.head,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Err(ShellError::Return {
|
|
||||||
span: call.head,
|
|
||||||
value: Box::new(Value::nothing(call.head)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, EvalBlockFn};
|
use nu_engine::{command_prelude::*, get_eval_block, EvalBlockFn};
|
||||||
use nu_protocol::engine::{Block, Closure};
|
use nu_protocol::engine::Closure;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Try;
|
pub struct Try;
|
||||||
@ -38,15 +38,18 @@ impl Command for Try {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let try_block: Block = call.req(engine_state, stack, 0)?;
|
let try_block = call
|
||||||
|
.positional_nth(0)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
|
|
||||||
let catch_block: Option<Closure> = call.opt(engine_state, stack, 1)?;
|
let catch_block: Option<Closure> = call.opt(engine_state, stack, 1)?;
|
||||||
|
|
||||||
let try_block = engine_state.get_block(try_block.block_id);
|
let try_block = engine_state.get_block(try_block);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
|
||||||
let result = eval_block(engine_state, stack, try_block, input);
|
match eval_block(engine_state, stack, try_block, input) {
|
||||||
|
|
||||||
match result {
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = intercept_block_control(error)?;
|
let error = intercept_block_control(error)?;
|
||||||
let err_record = err_to_record(error, call.head);
|
let err_record = err_to_record(error, call.head);
|
||||||
@ -61,9 +64,8 @@ impl Command for Try {
|
|||||||
Ok(pipeline) => {
|
Ok(pipeline) => {
|
||||||
let (pipeline, external_failed) = pipeline.check_external_failed();
|
let (pipeline, external_failed) = pipeline.check_external_failed();
|
||||||
if external_failed {
|
if external_failed {
|
||||||
// Because external command errors aren't "real" errors,
|
let exit_code = pipeline.drain_with_exit_code()?;
|
||||||
// (unless do -c is in effect)
|
stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(exit_code, call.head));
|
||||||
// they can't be passed in as Nushell values.
|
|
||||||
let err_value = Value::nothing(call.head);
|
let err_value = Value::nothing(call.head);
|
||||||
handle_catch(err_value, catch_block, engine_state, stack, eval_block)
|
handle_catch(err_value, catch_block, engine_state, stack, eval_block)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use shadow_rs::shadow;
|
use shadow_rs::shadow;
|
||||||
@ -14,7 +16,7 @@ impl Command for Version {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("version")
|
Signature::build("version")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Record(vec![]))])
|
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
@ -34,7 +36,7 @@ impl Command for Version {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
version(engine_state, call)
|
version(engine_state, call.head)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_const(
|
fn run_const(
|
||||||
@ -43,7 +45,7 @@ impl Command for Version {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
version(working_set.permanent(), call)
|
version(working_set.permanent(), call.head)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -55,76 +57,63 @@ impl Command for Version {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(engine_state: &EngineState, call: &Call) -> Result<PipelineData, ShellError> {
|
fn push_non_empty(record: &mut Record, name: &str, value: &str, span: Span) {
|
||||||
// Pre-allocate the arrays in the worst case (12 items):
|
if !value.is_empty() {
|
||||||
|
record.push(name, Value::string(value, span))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, ShellError> {
|
||||||
|
// Pre-allocate the arrays in the worst case (17 items):
|
||||||
// - version
|
// - version
|
||||||
|
// - major
|
||||||
|
// - minor
|
||||||
|
// - patch
|
||||||
|
// - pre
|
||||||
// - branch
|
// - branch
|
||||||
// - commit_hash
|
// - commit_hash
|
||||||
// - build_os
|
// - build_os
|
||||||
// - build_target
|
// - build_target
|
||||||
// - rust_version
|
// - rust_version
|
||||||
|
// - rust_channel
|
||||||
// - cargo_version
|
// - cargo_version
|
||||||
// - build_time
|
// - build_time
|
||||||
// - build_rust_channel
|
// - build_rust_channel
|
||||||
|
// - allocator
|
||||||
// - features
|
// - features
|
||||||
// - installed_plugins
|
// - installed_plugins
|
||||||
let mut record = Record::with_capacity(12);
|
let mut record = Record::with_capacity(17);
|
||||||
|
|
||||||
record.push(
|
record.push("version", Value::string(env!("CARGO_PKG_VERSION"), span));
|
||||||
"version",
|
|
||||||
Value::string(env!("CARGO_PKG_VERSION"), call.head),
|
push_version_numbers(&mut record, span);
|
||||||
|
|
||||||
|
push_non_empty(&mut record, "pre", build::PKG_VERSION_PRE, span);
|
||||||
|
|
||||||
|
record.push("branch", Value::string(build::BRANCH, span));
|
||||||
|
|
||||||
|
if let Some(commit_hash) = option_env!("NU_COMMIT_HASH") {
|
||||||
|
record.push("commit_hash", Value::string(commit_hash, span));
|
||||||
|
}
|
||||||
|
|
||||||
|
push_non_empty(&mut record, "build_os", build::BUILD_OS, span);
|
||||||
|
push_non_empty(&mut record, "build_target", build::BUILD_TARGET, span);
|
||||||
|
push_non_empty(&mut record, "rust_version", build::RUST_VERSION, span);
|
||||||
|
push_non_empty(&mut record, "rust_channel", build::RUST_CHANNEL, span);
|
||||||
|
push_non_empty(&mut record, "cargo_version", build::CARGO_VERSION, span);
|
||||||
|
push_non_empty(&mut record, "build_time", build::BUILD_TIME, span);
|
||||||
|
push_non_empty(
|
||||||
|
&mut record,
|
||||||
|
"build_rust_channel",
|
||||||
|
build::BUILD_RUST_CHANNEL,
|
||||||
|
span,
|
||||||
);
|
);
|
||||||
|
|
||||||
record.push("branch", Value::string(build::BRANCH, call.head));
|
record.push("allocator", Value::string(global_allocator(), span));
|
||||||
|
|
||||||
let commit_hash = option_env!("NU_COMMIT_HASH");
|
|
||||||
if let Some(commit_hash) = commit_hash {
|
|
||||||
record.push("commit_hash", Value::string(commit_hash, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_os = Some(build::BUILD_OS).filter(|x| !x.is_empty());
|
|
||||||
if let Some(build_os) = build_os {
|
|
||||||
record.push("build_os", Value::string(build_os, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_target = Some(build::BUILD_TARGET).filter(|x| !x.is_empty());
|
|
||||||
if let Some(build_target) = build_target {
|
|
||||||
record.push("build_target", Value::string(build_target, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let rust_version = Some(build::RUST_VERSION).filter(|x| !x.is_empty());
|
|
||||||
if let Some(rust_version) = rust_version {
|
|
||||||
record.push("rust_version", Value::string(rust_version, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let rust_channel = Some(build::RUST_CHANNEL).filter(|x| !x.is_empty());
|
|
||||||
if let Some(rust_channel) = rust_channel {
|
|
||||||
record.push("rust_channel", Value::string(rust_channel, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let cargo_version = Some(build::CARGO_VERSION).filter(|x| !x.is_empty());
|
|
||||||
if let Some(cargo_version) = cargo_version {
|
|
||||||
record.push("cargo_version", Value::string(cargo_version, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_time = Some(build::BUILD_TIME).filter(|x| !x.is_empty());
|
|
||||||
if let Some(build_time) = build_time {
|
|
||||||
record.push("build_time", Value::string(build_time, call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
let build_rust_channel = Some(build::BUILD_RUST_CHANNEL).filter(|x| !x.is_empty());
|
|
||||||
if let Some(build_rust_channel) = build_rust_channel {
|
|
||||||
record.push(
|
|
||||||
"build_rust_channel",
|
|
||||||
Value::string(build_rust_channel, call.head),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
record.push("allocator", Value::string(global_allocator(), call.head));
|
|
||||||
|
|
||||||
record.push(
|
record.push(
|
||||||
"features",
|
"features",
|
||||||
Value::string(features_enabled().join(", "), call.head),
|
Value::string(features_enabled().join(", "), span),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get a list of plugin names
|
// Get a list of plugin names
|
||||||
@ -136,10 +125,26 @@ pub fn version(engine_state: &EngineState, call: &Call) -> Result<PipelineData,
|
|||||||
|
|
||||||
record.push(
|
record.push(
|
||||||
"installed_plugins",
|
"installed_plugins",
|
||||||
Value::string(installed_plugins.join(", "), call.head),
|
Value::string(installed_plugins.join(", "), span),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Value::record(record, call.head).into_pipeline_data())
|
Ok(Value::record(record, span).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add version numbers as integers to the given record
|
||||||
|
fn push_version_numbers(record: &mut Record, head: Span) {
|
||||||
|
static VERSION_NUMBERS: OnceLock<(u8, u8, u8)> = OnceLock::new();
|
||||||
|
|
||||||
|
let &(major, minor, patch) = VERSION_NUMBERS.get_or_init(|| {
|
||||||
|
(
|
||||||
|
build::PKG_VERSION_MAJOR.parse().expect("Always set"),
|
||||||
|
build::PKG_VERSION_MINOR.parse().expect("Always set"),
|
||||||
|
build::PKG_VERSION_PATCH.parse().expect("Always set"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
record.push("major", Value::int(major.into(), head));
|
||||||
|
record.push("minor", Value::int(minor.into(), head));
|
||||||
|
record.push("patch", Value::int(patch.into(), head));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_allocator() -> &'static str {
|
fn global_allocator() -> &'static str {
|
||||||
@ -180,9 +185,9 @@ fn features_enabled() -> Vec<String> {
|
|||||||
names.push("static-link-openssl".to_string());
|
names.push("static-link-openssl".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wasi")]
|
#[cfg(feature = "system-clipboard")]
|
||||||
{
|
{
|
||||||
names.push("wasi".to_string());
|
names.push("system-clipboard".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
names.sort();
|
names.sort();
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
||||||
use nu_protocol::engine::Block;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct While;
|
pub struct While;
|
||||||
@ -38,7 +37,11 @@ impl Command for While {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let block: Block = call.req(engine_state, stack, 1)?;
|
let block_id = call
|
||||||
|
.positional_nth(1)
|
||||||
|
.expect("checked through parser")
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: missing block");
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
let eval_expression = get_eval_expression(engine_state);
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
@ -55,7 +58,7 @@ impl Command for While {
|
|||||||
match &result {
|
match &result {
|
||||||
Value::Bool { val, .. } => {
|
Value::Bool { val, .. } => {
|
||||||
if *val {
|
if *val {
|
||||||
let block = engine_state.get_block(block.block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
|
@ -63,9 +63,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
While,
|
While,
|
||||||
};
|
};
|
||||||
|
|
||||||
//#[cfg(feature = "plugin")]
|
|
||||||
bind_command!(PluginCommand, PluginList, PluginStop, Register,);
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Block, RangeInclusion},
|
ast::Block,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{StateDelta, StateWorkingSet},
|
engine::{StateDelta, StateWorkingSet},
|
||||||
|
Range,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
{collections::HashSet, ops::Bound},
|
||||||
};
|
};
|
||||||
use std::{collections::HashSet, sync::Arc};
|
|
||||||
|
|
||||||
pub fn check_example_input_and_output_types_match_command_signature(
|
pub fn check_example_input_and_output_types_match_command_signature(
|
||||||
example: &Example,
|
example: &Example,
|
||||||
@ -219,17 +223,45 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
|||||||
Value::Date { val, .. } => {
|
Value::Date { val, .. } => {
|
||||||
write!(f, "Date({:?})", val)
|
write!(f, "Date({:?})", val)
|
||||||
}
|
}
|
||||||
Value::Range { val, .. } => match val.inclusion {
|
Value::Range { val, .. } => match val {
|
||||||
RangeInclusion::Inclusive => write!(
|
Range::IntRange(range) => match range.end() {
|
||||||
f,
|
Bound::Included(end) => write!(
|
||||||
"Range({:?}..{:?}, step: {:?})",
|
f,
|
||||||
val.from, val.to, val.incr
|
"Range({:?}..{:?}, step: {:?})",
|
||||||
),
|
range.start(),
|
||||||
RangeInclusion::RightExclusive => write!(
|
end,
|
||||||
f,
|
range.step(),
|
||||||
"Range({:?}..<{:?}, step: {:?})",
|
),
|
||||||
val.from, val.to, val.incr
|
Bound::Excluded(end) => write!(
|
||||||
),
|
f,
|
||||||
|
"Range({:?}..<{:?}, step: {:?})",
|
||||||
|
range.start(),
|
||||||
|
end,
|
||||||
|
range.step(),
|
||||||
|
),
|
||||||
|
Bound::Unbounded => {
|
||||||
|
write!(f, "Range({:?}.., step: {:?})", range.start(), range.step())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Range::FloatRange(range) => match range.end() {
|
||||||
|
Bound::Included(end) => write!(
|
||||||
|
f,
|
||||||
|
"Range({:?}..{:?}, step: {:?})",
|
||||||
|
range.start(),
|
||||||
|
end,
|
||||||
|
range.step(),
|
||||||
|
),
|
||||||
|
Bound::Excluded(end) => write!(
|
||||||
|
f,
|
||||||
|
"Range({:?}..<{:?}, step: {:?})",
|
||||||
|
range.start(),
|
||||||
|
end,
|
||||||
|
range.step(),
|
||||||
|
),
|
||||||
|
Bound::Unbounded => {
|
||||||
|
write!(f, "Range({:?}.., step: {:?})", range.start(), range.step())
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Value::String { val, .. } | Value::Glob { val, .. } => {
|
Value::String { val, .. } | Value::Glob { val, .. } => {
|
||||||
write!(f, "{:?}", val)
|
write!(f, "{:?}", val)
|
||||||
@ -256,9 +288,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
|||||||
}
|
}
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
Value::Block { val, .. } => {
|
|
||||||
write!(f, "Block({:?})", val)
|
|
||||||
}
|
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { val, .. } => {
|
||||||
write!(f, "Closure({:?})", val)
|
write!(f, "Closure({:?})", val)
|
||||||
}
|
}
|
||||||
|
20
crates/nu-cmd-plugin/Cargo.toml
Normal file
20
crates/nu-cmd-plugin/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["The Nushell Project Developers"]
|
||||||
|
description = "Commands for managing Nushell plugins."
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu-cmd-plugin"
|
||||||
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
|
||||||
|
version = "0.93.0"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
|
nu-path = { path = "../nu-path", version = "0.93.0" }
|
||||||
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0", features = ["plugin"] }
|
||||||
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.93.0" }
|
||||||
|
|
||||||
|
itertools = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
21
crates/nu-cmd-plugin/LICENSE
Normal file
21
crates/nu-cmd-plugin/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
3
crates/nu-cmd-plugin/README.md
Normal file
3
crates/nu-cmd-plugin/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# nu-cmd-plugin
|
||||||
|
|
||||||
|
This crate implements Nushell commands related to plugin management.
|
5
crates/nu-cmd-plugin/src/commands/mod.rs
Normal file
5
crates/nu-cmd-plugin/src/commands/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod plugin;
|
||||||
|
mod register;
|
||||||
|
|
||||||
|
pub use plugin::*;
|
||||||
|
pub use register::Register;
|
132
crates/nu-cmd-plugin/src/commands/plugin/add.rs
Normal file
132
crates/nu-cmd-plugin/src/commands/plugin/add.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
|
use nu_plugin_engine::{GetPlugin, PersistentPlugin};
|
||||||
|
use nu_protocol::{PluginGcConfig, PluginIdentity, PluginRegistryItem, RegisteredPlugin};
|
||||||
|
|
||||||
|
use crate::util::{get_plugin_dirs, modify_plugin_file};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PluginAdd;
|
||||||
|
|
||||||
|
impl Command for PluginAdd {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"plugin add"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_output_type(Type::Nothing, Type::Nothing)
|
||||||
|
// This matches the option to `nu`
|
||||||
|
.named(
|
||||||
|
"plugin-config",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"Use a plugin registry file other than the one set in `$nu.plugin-path`",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"shell",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"Use an additional shell program (cmd, sh, python, etc.) to run the plugin",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"filename",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"Path to the executable for the plugin",
|
||||||
|
)
|
||||||
|
.category(Category::Plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Add a plugin to the plugin registry file."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"
|
||||||
|
This does not load the plugin commands into the scope - see `register` for that.
|
||||||
|
|
||||||
|
Instead, it runs the plugin to get its command signatures, and then edits the
|
||||||
|
plugin registry file (by default, `$nu.plugin-path`). The changes will be
|
||||||
|
apparent the next time `nu` is next launched with that plugin registry file.
|
||||||
|
"#
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["load", "register", "signature"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
example: "plugin add nu_plugin_inc",
|
||||||
|
description: "Run the `nu_plugin_inc` plugin from the current directory or $env.NU_PLUGIN_DIRS and install its signatures.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin add --plugin-config polars.msgpackz nu_plugin_polars",
|
||||||
|
description: "Run the `nu_plugin_polars` plugin from the current directory or $env.NU_PLUGIN_DIRS, and install its signatures to the \"polars.msgpackz\" plugin registry file.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let filename: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
let shell: Option<Spanned<String>> = call.get_flag(engine_state, stack, "shell")?;
|
||||||
|
|
||||||
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
|
// Check the current directory, or fall back to NU_PLUGIN_DIRS
|
||||||
|
let filename_expanded = nu_path::locate_in_dirs(&filename.item, &cwd, || {
|
||||||
|
get_plugin_dirs(engine_state, stack)
|
||||||
|
})
|
||||||
|
.err_span(filename.span)?;
|
||||||
|
|
||||||
|
let shell_expanded = shell
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| nu_path::canonicalize_with(&s.item, &cwd).err_span(s.span))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
// Parse the plugin filename so it can be used to spawn the plugin
|
||||||
|
let identity = PluginIdentity::new(filename_expanded, shell_expanded).map_err(|_| {
|
||||||
|
ShellError::GenericError {
|
||||||
|
error: "Plugin filename is invalid".into(),
|
||||||
|
msg: "plugin executable files must start with `nu_plugin_`".into(),
|
||||||
|
span: Some(filename.span),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let custom_path = call.get_flag(engine_state, stack, "plugin-config")?;
|
||||||
|
|
||||||
|
// Start the plugin manually, to get the freshest signatures and to not affect engine
|
||||||
|
// state. Provide a GC config that will stop it ASAP
|
||||||
|
let plugin = Arc::new(PersistentPlugin::new(
|
||||||
|
identity,
|
||||||
|
PluginGcConfig {
|
||||||
|
enabled: true,
|
||||||
|
stop_after: 0,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
let interface = plugin.clone().get_plugin(Some((engine_state, stack)))?;
|
||||||
|
let commands = interface.get_signature()?;
|
||||||
|
|
||||||
|
modify_plugin_file(engine_state, stack, call.head, custom_path, |contents| {
|
||||||
|
// Update the file with the received signatures
|
||||||
|
let item = PluginRegistryItem::new(plugin.identity(), commands);
|
||||||
|
contents.upsert_plugin(item);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::nothing(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
@ -13,22 +13,29 @@ impl Command for PluginList {
|
|||||||
Signature::build("plugin list")
|
Signature::build("plugin list")
|
||||||
.input_output_type(
|
.input_output_type(
|
||||||
Type::Nothing,
|
Type::Nothing,
|
||||||
Type::Table(vec![
|
Type::Table(
|
||||||
("name".into(), Type::String),
|
[
|
||||||
("is_running".into(), Type::Bool),
|
("name".into(), Type::String),
|
||||||
("pid".into(), Type::Int),
|
("is_running".into(), Type::Bool),
|
||||||
("filename".into(), Type::String),
|
("pid".into(), Type::Int),
|
||||||
("shell".into(), Type::String),
|
("filename".into(), Type::String),
|
||||||
("commands".into(), Type::List(Type::String.into())),
|
("shell".into(), Type::String),
|
||||||
]),
|
("commands".into(), Type::List(Type::String.into())),
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"List installed plugins."
|
"List installed plugins."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["scope"]
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<nu_protocol::Example> {
|
fn examples(&self) -> Vec<nu_protocol::Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
@ -1,5 +1,17 @@
|
|||||||
use nu_engine::{command_prelude::*, get_full_help};
|
use nu_engine::{command_prelude::*, get_full_help};
|
||||||
|
|
||||||
|
mod add;
|
||||||
|
mod list;
|
||||||
|
mod rm;
|
||||||
|
mod stop;
|
||||||
|
mod use_;
|
||||||
|
|
||||||
|
pub use add::PluginAdd;
|
||||||
|
pub use list::PluginList;
|
||||||
|
pub use rm::PluginRm;
|
||||||
|
pub use stop::PluginStop;
|
||||||
|
pub use use_::PluginUse;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PluginCommand;
|
pub struct PluginCommand;
|
||||||
|
|
||||||
@ -11,17 +23,13 @@ impl Command for PluginCommand {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("plugin")
|
Signature::build("plugin")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
.category(Category::Core)
|
.category(Category::Plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Commands for managing plugins."
|
"Commands for managing plugins."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"To load a plugin, see `register`."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -44,6 +52,21 @@ impl Command for PluginCommand {
|
|||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
|
Example {
|
||||||
|
example: "plugin add nu_plugin_inc",
|
||||||
|
description: "Run the `nu_plugin_inc` plugin from the current directory and install its signatures.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin use inc",
|
||||||
|
description: "
|
||||||
|
Load (or reload) the `inc` plugin from the plugin registry file and put its
|
||||||
|
commands in scope. The plugin must already be in the registry file at parse
|
||||||
|
time.
|
||||||
|
"
|
||||||
|
.trim(),
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
example: "plugin list",
|
example: "plugin list",
|
||||||
description: "List installed plugins",
|
description: "List installed plugins",
|
||||||
@ -54,6 +77,11 @@ impl Command for PluginCommand {
|
|||||||
description: "Stop the plugin named `inc`.",
|
description: "Stop the plugin named `inc`.",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin rm inc",
|
||||||
|
description: "Remove the installed signatures for the `inc` plugin.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
113
crates/nu-cmd-plugin/src/commands/plugin/rm.rs
Normal file
113
crates/nu-cmd-plugin/src/commands/plugin/rm.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use crate::util::{canonicalize_possible_filename_arg, modify_plugin_file};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PluginRm;
|
||||||
|
|
||||||
|
impl Command for PluginRm {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"plugin rm"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_output_type(Type::Nothing, Type::Nothing)
|
||||||
|
// This matches the option to `nu`
|
||||||
|
.named(
|
||||||
|
"plugin-config",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"Use a plugin registry file other than the one set in `$nu.plugin-path`",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"force",
|
||||||
|
"Don't cause an error if the plugin name wasn't found in the file",
|
||||||
|
Some('f'),
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"name",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"The name, or filename, of the plugin to remove",
|
||||||
|
)
|
||||||
|
.category(Category::Plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Remove a plugin from the plugin registry file."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"
|
||||||
|
This does not remove the plugin commands from the current scope or from `plugin
|
||||||
|
list` in the current shell. It instead removes the plugin from the plugin
|
||||||
|
registry file (by default, `$nu.plugin-path`). The changes will be apparent the
|
||||||
|
next time `nu` is launched with that plugin registry file.
|
||||||
|
|
||||||
|
This can be useful for removing an invalid plugin signature, if it can't be
|
||||||
|
fixed with `plugin add`.
|
||||||
|
"#
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["remove", "delete", "signature"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
example: "plugin rm inc",
|
||||||
|
description: "Remove the installed signatures for the `inc` plugin.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin rm ~/.cargo/bin/nu_plugin_inc",
|
||||||
|
description: "Remove the installed signatures for the plugin with the filename `~/.cargo/bin/nu_plugin_inc`.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin rm --plugin-config polars.msgpackz polars",
|
||||||
|
description: "Remove the installed signatures for the `polars` plugin from the \"polars.msgpackz\" plugin registry file.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
let custom_path = call.get_flag(engine_state, stack, "plugin-config")?;
|
||||||
|
let force = call.has_flag(engine_state, stack, "force")?;
|
||||||
|
|
||||||
|
let filename = canonicalize_possible_filename_arg(engine_state, stack, &name.item);
|
||||||
|
|
||||||
|
modify_plugin_file(engine_state, stack, call.head, custom_path, |contents| {
|
||||||
|
if let Some(index) = contents
|
||||||
|
.plugins
|
||||||
|
.iter()
|
||||||
|
.position(|p| p.name == name.item || p.filename == filename)
|
||||||
|
{
|
||||||
|
contents.plugins.remove(index);
|
||||||
|
Ok(())
|
||||||
|
} else if force {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ShellError::GenericError {
|
||||||
|
error: format!("Failed to remove the `{}` plugin", name.item),
|
||||||
|
msg: "couldn't find a plugin with this name in the registry file".into(),
|
||||||
|
span: Some(name.span),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Value::nothing(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use crate::util::canonicalize_possible_filename_arg;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PluginStop;
|
pub struct PluginStop;
|
||||||
|
|
||||||
@ -14,9 +16,9 @@ impl Command for PluginStop {
|
|||||||
.required(
|
.required(
|
||||||
"name",
|
"name",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
"The name of the plugin to stop.",
|
"The name, or filename, of the plugin to stop",
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -30,6 +32,11 @@ impl Command for PluginStop {
|
|||||||
description: "Stop the plugin named `inc`.",
|
description: "Stop the plugin named `inc`.",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
example: "plugin stop ~/.cargo/bin/nu_plugin_inc",
|
||||||
|
description: "Stop the plugin with the filename `~/.cargo/bin/nu_plugin_inc`.",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
example: "plugin list | each { |p| plugin stop $p.name }",
|
example: "plugin list | each { |p| plugin stop $p.name }",
|
||||||
description: "Stop all plugins.",
|
description: "Stop all plugins.",
|
||||||
@ -47,9 +54,12 @@ impl Command for PluginStop {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
let filename = canonicalize_possible_filename_arg(engine_state, stack, &name.item);
|
||||||
|
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for plugin in engine_state.plugins() {
|
for plugin in engine_state.plugins() {
|
||||||
if plugin.identity().name() == name.item {
|
let id = &plugin.identity();
|
||||||
|
if id.name() == name.item || id.filename() == filename {
|
||||||
plugin.stop()?;
|
plugin.stop()?;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
89
crates/nu-cmd-plugin/src/commands/plugin/use_.rs
Normal file
89
crates/nu-cmd-plugin/src/commands/plugin/use_.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PluginUse;
|
||||||
|
|
||||||
|
impl Command for PluginUse {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"plugin use"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Load a plugin from the plugin registry file into scope."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build(self.name())
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.named(
|
||||||
|
"plugin-config",
|
||||||
|
SyntaxShape::Filepath,
|
||||||
|
"Use a plugin registry file other than the one set in `$nu.plugin-path`",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.required(
|
||||||
|
"name",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"The name, or filename, of the plugin to load",
|
||||||
|
)
|
||||||
|
.category(Category::Plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"
|
||||||
|
This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html
|
||||||
|
|
||||||
|
The plugin definition must be available in the plugin registry file at parse
|
||||||
|
time. Run `plugin add` first in the REPL to do this, or from a script consider
|
||||||
|
preparing a plugin registry file and passing `--plugin-config`, or using the
|
||||||
|
`--plugin` option to `nu` instead.
|
||||||
|
|
||||||
|
If the plugin was already loaded, this will reload the latest definition from
|
||||||
|
the registry file into scope.
|
||||||
|
|
||||||
|
Note that even if the plugin filename is specified, it will only be loaded if
|
||||||
|
it was already previously registered with `plugin add`.
|
||||||
|
"#
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["add", "register", "scope"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_parser_keyword(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
_call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(PipelineData::empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Load the commands for the `query` plugin from $nu.plugin-path",
|
||||||
|
example: r#"plugin use query"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Load the commands for the plugin with the filename `~/.cargo/bin/nu_plugin_query` from $nu.plugin-path",
|
||||||
|
example: r#"plugin use ~/.cargo/bin/nu_plugin_query"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Load the commands for the `query` plugin from a custom plugin registry file",
|
||||||
|
example: r#"plugin use --plugin-config local-plugins.msgpackz query"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -31,12 +31,21 @@ impl Command for Register {
|
|||||||
"path of shell used to run plugin (cmd, sh, python, etc)",
|
"path of shell used to run plugin (cmd, sh, python, etc)",
|
||||||
Some('s'),
|
Some('s'),
|
||||||
)
|
)
|
||||||
.category(Category::Core)
|
.category(Category::Plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"This command is a parser keyword. For details, check:
|
r#"
|
||||||
https://www.nushell.sh/book/thinking_in_nu.html"#
|
Deprecated in favor of `plugin add` and `plugin use`.
|
||||||
|
|
||||||
|
This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html
|
||||||
|
"#
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["add"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_parser_keyword(&self) -> bool {
|
fn is_parser_keyword(&self) -> bool {
|
32
crates/nu-cmd-plugin/src/default_context.rs
Normal file
32
crates/nu-cmd-plugin/src/default_context.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use crate::*;
|
||||||
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
|
|
||||||
|
pub fn add_plugin_command_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!(
|
||||||
|
PluginAdd,
|
||||||
|
PluginCommand,
|
||||||
|
PluginList,
|
||||||
|
PluginRm,
|
||||||
|
PluginStop,
|
||||||
|
PluginUse,
|
||||||
|
Register,
|
||||||
|
);
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = engine_state.merge_delta(delta) {
|
||||||
|
eprintln!("Error creating default context: {err:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
}
|
8
crates/nu-cmd-plugin/src/lib.rs
Normal file
8
crates/nu-cmd-plugin/src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//! Nushell commands for managing plugins.
|
||||||
|
|
||||||
|
mod commands;
|
||||||
|
mod default_context;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
pub use commands::*;
|
||||||
|
pub use default_context::*;
|
89
crates/nu-cmd-plugin/src/util.rs
Normal file
89
crates/nu-cmd-plugin/src/util.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
|
use nu_protocol::{engine::StateWorkingSet, PluginRegistryFile};
|
||||||
|
|
||||||
|
pub(crate) fn modify_plugin_file(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
span: Span,
|
||||||
|
custom_path: Option<Spanned<String>>,
|
||||||
|
operate: impl FnOnce(&mut PluginRegistryFile) -> Result<(), ShellError>,
|
||||||
|
) -> Result<(), ShellError> {
|
||||||
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
|
let plugin_registry_file_path = if let Some(ref custom_path) = custom_path {
|
||||||
|
nu_path::expand_path_with(&custom_path.item, cwd, true)
|
||||||
|
} else {
|
||||||
|
engine_state
|
||||||
|
.plugin_path
|
||||||
|
.clone()
|
||||||
|
.ok_or_else(|| ShellError::GenericError {
|
||||||
|
error: "Plugin registry file not set".into(),
|
||||||
|
msg: "pass --plugin-config explicitly here".into(),
|
||||||
|
span: Some(span),
|
||||||
|
help: Some("you may be running `nu` with --no-config-file".into()),
|
||||||
|
inner: vec![],
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to read the plugin file if it exists
|
||||||
|
let mut contents = if fs::metadata(&plugin_registry_file_path).is_ok_and(|m| m.len() > 0) {
|
||||||
|
PluginRegistryFile::read_from(
|
||||||
|
File::open(&plugin_registry_file_path).err_span(span)?,
|
||||||
|
Some(span),
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
PluginRegistryFile::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do the operation
|
||||||
|
operate(&mut contents)?;
|
||||||
|
|
||||||
|
// Save the modified file on success
|
||||||
|
contents.write_to(
|
||||||
|
File::create(&plugin_registry_file_path).err_span(span)?,
|
||||||
|
Some(span),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn canonicalize_possible_filename_arg(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &Stack,
|
||||||
|
arg: &str,
|
||||||
|
) -> PathBuf {
|
||||||
|
// This results in the best possible chance of a match with the plugin item
|
||||||
|
if let Ok(cwd) = nu_engine::current_dir(engine_state, stack) {
|
||||||
|
let path = nu_path::expand_path_with(arg, &cwd, true);
|
||||||
|
// Try to canonicalize
|
||||||
|
nu_path::locate_in_dirs(&path, &cwd, || get_plugin_dirs(engine_state, stack))
|
||||||
|
// If we couldn't locate it, return the expanded path alone
|
||||||
|
.unwrap_or(path)
|
||||||
|
} else {
|
||||||
|
arg.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_plugin_dirs(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &Stack,
|
||||||
|
) -> impl Iterator<Item = String> {
|
||||||
|
// Get the NU_PLUGIN_DIRS constant or env var
|
||||||
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
let value = working_set
|
||||||
|
.find_variable(b"$NU_PLUGIN_DIRS")
|
||||||
|
.and_then(|var_id| working_set.get_constant(var_id).ok().cloned())
|
||||||
|
.or_else(|| stack.get_env_var(engine_state, "NU_PLUGIN_DIRS"));
|
||||||
|
|
||||||
|
// Get all of the strings in the list, if possible
|
||||||
|
value
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|value| value.into_list().ok())
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|list_item| list_item.coerce_into_string().ok())
|
||||||
|
}
|
@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.92.1"
|
version = "0.93.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.92.1" }
|
nu-json = { path = "../nu-json", version = "0.93.0" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.92.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.93.0" }
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle};
|
use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle};
|
||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use nu_engine::{env::get_config, eval_block};
|
use nu_engine::{env::get_config, ClosureEvalOnce};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
cli_error::CliError,
|
cli_error::CliError,
|
||||||
debugger::WithoutDebug,
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
Span, Value,
|
||||||
IntoPipelineData, Value,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -18,7 +17,7 @@ use std::{
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ComputableStyle {
|
pub enum ComputableStyle {
|
||||||
Static(Style),
|
Static(Style),
|
||||||
Closure(Value),
|
Closure(Closure, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
// An alias for the mapping used internally by StyleComputer.
|
// An alias for the mapping used internally by StyleComputer.
|
||||||
@ -56,51 +55,31 @@ impl<'a> StyleComputer<'a> {
|
|||||||
// Static values require no computation.
|
// Static values require no computation.
|
||||||
Some(ComputableStyle::Static(s)) => *s,
|
Some(ComputableStyle::Static(s)) => *s,
|
||||||
// Closures are run here.
|
// Closures are run here.
|
||||||
Some(ComputableStyle::Closure(v)) => {
|
Some(ComputableStyle::Closure(closure, span)) => {
|
||||||
let span = v.span();
|
let result = ClosureEvalOnce::new(self.engine_state, self.stack, closure.clone())
|
||||||
match v {
|
.debug(false)
|
||||||
Value::Closure { val, .. } => {
|
.run_with_value(value.clone());
|
||||||
let block = self.engine_state.get_block(val.block_id).clone();
|
|
||||||
// Because captures_to_stack() clones, we don't need to use with_env() here
|
|
||||||
// (contrast with_env() usage in `each` or `do`).
|
|
||||||
let mut stack = self.stack.captures_to_stack(val.captures.clone());
|
|
||||||
|
|
||||||
// Support 1-argument blocks as well as 0-argument blocks.
|
match result {
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
Ok(v) => {
|
||||||
if let Some(var_id) = &var.var_id {
|
let value = v.into_value(*span);
|
||||||
stack.add_var(*var_id, value.clone());
|
// These should be the same color data forms supported by color_config.
|
||||||
}
|
match value {
|
||||||
}
|
Value::Record { .. } => color_record_to_nustyle(&value),
|
||||||
|
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
||||||
// Run the block.
|
_ => Style::default(),
|
||||||
match eval_block::<WithoutDebug>(
|
|
||||||
self.engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
value.clone().into_pipeline_data(),
|
|
||||||
) {
|
|
||||||
Ok(v) => {
|
|
||||||
let value = v.into_value(span);
|
|
||||||
// These should be the same color data forms supported by color_config.
|
|
||||||
match value {
|
|
||||||
Value::Record { .. } => color_record_to_nustyle(&value),
|
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
|
||||||
_ => Style::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
|
|
||||||
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
|
|
||||||
// currently hook closure errors behave roughly the same.
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!(
|
|
||||||
"Error: {:?}",
|
|
||||||
CliError(&e, &StateWorkingSet::new(self.engine_state))
|
|
||||||
);
|
|
||||||
Style::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Style::default(),
|
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
|
||||||
|
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
|
||||||
|
// currently hook closure errors behave roughly the same.
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"Error: {:?}",
|
||||||
|
CliError(&e, &StateWorkingSet::new(self.engine_state))
|
||||||
|
);
|
||||||
|
Style::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
|
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
|
||||||
@ -126,9 +105,7 @@ impl<'a> StyleComputer<'a> {
|
|||||||
Value::Nothing { .. } => TextStyle::with_style(Left, s),
|
Value::Nothing { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::Binary { .. } => TextStyle::with_style(Left, s),
|
Value::Binary { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::CellPath { .. } => TextStyle::with_style(Left, s),
|
Value::CellPath { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
|
Value::Record { .. } | Value::List { .. } => TextStyle::with_style(Left, s),
|
||||||
TextStyle::with_style(Left, s)
|
|
||||||
}
|
|
||||||
Value::Closure { .. }
|
Value::Closure { .. }
|
||||||
| Value::Custom { .. }
|
| Value::Custom { .. }
|
||||||
| Value::Error { .. }
|
| Value::Error { .. }
|
||||||
@ -167,9 +144,10 @@ impl<'a> StyleComputer<'a> {
|
|||||||
].into_iter().collect();
|
].into_iter().collect();
|
||||||
|
|
||||||
for (key, value) in &config.color_config {
|
for (key, value) in &config.color_config {
|
||||||
|
let span = value.span();
|
||||||
match value {
|
match value {
|
||||||
Value::Closure { .. } => {
|
Value::Closure { val, .. } => {
|
||||||
map.insert(key.to_string(), ComputableStyle::Closure(value.clone()));
|
map.insert(key.to_string(), ComputableStyle::Closure(val.clone(), span));
|
||||||
}
|
}
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
map.insert(
|
map.insert(
|
||||||
|
@ -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.92.1"
|
version = "0.93.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,24 +13,26 @@ version = "0.92.1"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.92.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.93.0" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.92.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.93.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.92.1" }
|
nu-engine = { path = "../nu-engine", version = "0.93.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.92.1" }
|
nu-glob = { path = "../nu-glob", version = "0.93.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.92.1" }
|
nu-json = { path = "../nu-json", version = "0.93.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.92.1" }
|
nu-parser = { path = "../nu-parser", version = "0.93.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.92.1" }
|
nu-path = { path = "../nu-path", version = "0.93.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.92.1" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.93.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.92.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.93.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.92.1" }
|
nu-system = { path = "../nu-system", version = "0.93.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.92.1" }
|
nu-table = { path = "../nu-table", version = "0.93.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.92.1" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.93.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.92.1" }
|
nu-utils = { path = "../nu-utils", version = "0.93.0" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
|
nuon = { path = "../nuon", version = "0.93.0" }
|
||||||
|
|
||||||
alphanumeric-sort = { workspace = true }
|
alphanumeric-sort = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
bracoxide = { workspace = true }
|
bracoxide = { workspace = true }
|
||||||
|
brotli = { workspace = true }
|
||||||
byteorder = { workspace = true }
|
byteorder = { workspace = true }
|
||||||
bytesize = { workspace = true }
|
bytesize = { workspace = true }
|
||||||
calamine = { workspace = true, features = ["dates"] }
|
calamine = { workspace = true, features = ["dates"] }
|
||||||
@ -73,6 +75,7 @@ rayon = { workspace = true }
|
|||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
roxmltree = { workspace = true }
|
roxmltree = { workspace = true }
|
||||||
rusqlite = { workspace = true, features = ["bundled", "backup", "chrono"], optional = true }
|
rusqlite = { workspace = true, features = ["bundled", "backup", "chrono"], optional = true }
|
||||||
|
rmp = { workspace = true }
|
||||||
same-file = { workspace = true }
|
same-file = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true, features = ["preserve_order"] }
|
serde_json = { workspace = true, features = ["preserve_order"] }
|
||||||
@ -134,11 +137,12 @@ trash-support = ["trash"]
|
|||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.92.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.93.0" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.92.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.93.0" }
|
||||||
|
|
||||||
dirs-next = { workspace = true }
|
dirs-next = { workspace = true }
|
||||||
mockito = { workspace = true, default-features = false }
|
mockito = { workspace = true, default-features = false }
|
||||||
quickcheck = { workspace = true }
|
quickcheck = { workspace = true }
|
||||||
quickcheck_macros = { workspace = true }
|
quickcheck_macros = { workspace = true }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
|
pretty_assertions = { workspace = true }
|
||||||
|
@ -31,8 +31,8 @@ impl Command for BytesAdd {
|
|||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.required("data", SyntaxShape::Binary, "The binary to add.")
|
.required("data", SyntaxShape::Binary, "The binary to add.")
|
||||||
|
@ -41,8 +41,8 @@ impl Command for BytesAt {
|
|||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
Type::List(Box::new(Type::Binary)),
|
Type::List(Box::new(Type::Binary)),
|
||||||
),
|
),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.required("range", SyntaxShape::Range, "The range to get bytes.")
|
.required("range", SyntaxShape::Range, "The range to get bytes.")
|
||||||
.rest(
|
.rest(
|
||||||
|
@ -24,8 +24,8 @@ impl Command for BytesEndsWith {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes ends-with")
|
Signature::build("bytes ends-with")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Bool),
|
.input_output_types(vec![(Type::Binary, Type::Bool),
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.required("pattern", SyntaxShape::Binary, "The pattern to match.")
|
.required("pattern", SyntaxShape::Binary, "The pattern to match.")
|
||||||
|
@ -28,8 +28,8 @@ impl Command for BytesIndexOf {
|
|||||||
(Type::Binary, Type::Any),
|
(Type::Binary, Type::Any),
|
||||||
// FIXME: this shouldn't be needed, cell paths should work with the two
|
// FIXME: this shouldn't be needed, cell paths should work with the two
|
||||||
// above
|
// above
|
||||||
(Type::Table(vec![]), Type::Table(vec![])),
|
(Type::table(), Type::table()),
|
||||||
(Type::Record(vec![]), Type::Record(vec![])),
|
(Type::record(), Type::record()),
|
||||||
])
|
])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.required(
|
.required(
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user