mirror of
https://github.com/nushell/nushell.git
synced 2025-08-15 08:13:16 +02:00
Compare commits
3 Commits
main
...
release/0.
Author | SHA1 | Date | |
---|---|---|---|
98521100ed | |||
985211a924 | |||
a3388b00ae |
42
.github/pull_request_template.md
vendored
42
.github/pull_request_template.md
vendored
@ -1,16 +1,40 @@
|
||||
<!--
|
||||
Thank you for improving Nushell!
|
||||
Please, read our contributing guide: https://github.com/nushell/nushell/blob/main/CONTRIBUTING.md
|
||||
if this PR closes one or more issues, you can automatically link the PR with
|
||||
them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g.
|
||||
- this PR should close #xxxx
|
||||
- fixes #xxxx
|
||||
|
||||
Use the following space to include the motivation and any technical details behind this PR.
|
||||
you can also mention related issues, PRs or discussions!
|
||||
-->
|
||||
|
||||
## Release notes summary - What our users need to know
|
||||
# Description
|
||||
<!--
|
||||
This section will be included as part of our release notes. See the contributing guide for more details.
|
||||
If you're not confident about this, a core team member would be glad to help!
|
||||
Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.
|
||||
|
||||
Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.
|
||||
-->
|
||||
|
||||
## Tasks after submitting
|
||||
<!-- Remove any tasks which aren't relevant for your PR, or add your own -->
|
||||
- [ ] Update the [documentation](https://github.com/nushell/nushell.github.io)
|
||||
# User-Facing Changes
|
||||
<!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. -->
|
||||
|
||||
# Tests + Formatting
|
||||
<!--
|
||||
Don't forget to add tests that cover your changes.
|
||||
|
||||
Make sure you've run and fixed any issues with these commands:
|
||||
|
||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style
|
||||
- `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
|
||||
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library
|
||||
|
||||
> **Note**
|
||||
> from `nushell` you can also use the `toolkit` as follows
|
||||
> ```bash
|
||||
> use toolkit.nu # or use an `env_change` hook to activate it automatically
|
||||
> toolkit check pr
|
||||
> ```
|
||||
-->
|
||||
|
||||
# After Submitting
|
||||
<!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
|
||||
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
|
||||
- name: cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
@ -65,7 +65,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
|
||||
- name: Tests
|
||||
run: cargo test --workspace --profile ci --exclude nu_plugin_*
|
||||
@ -94,7 +94,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
|
||||
- name: Install Nushell
|
||||
run: cargo install --path . --locked --force
|
||||
@ -145,7 +145,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
||||
@ -186,7 +186,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
|
||||
- name: Add wasm32-unknown-unknown target
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
|
25
.github/workflows/friendly-config-reminder.yml
vendored
25
.github/workflows/friendly-config-reminder.yml
vendored
@ -1,25 +0,0 @@
|
||||
name: Comment on changes to the config
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'crates/nu-protocol/src/config/**'
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if there is already a bot comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Hey, just a bot checking in!
|
||||
- name: Create comment if there is not
|
||||
if: steps.fc.outputs.comment-id == ''
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
Hey, just a bot checking in! You edited files related to the configuration.
|
||||
If you changed any of the default values or added a new config option, don't forget to update the [`doc_config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/doc_config.nu) which documents the options for our users including the defaults provided by the Rust implementation.
|
||||
If you didn't make a change here, you can just ignore me.
|
27
.github/workflows/nightly-build.yml
vendored
27
.github/workflows/nightly-build.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
uses: hustcer/setup-nu@v3
|
||||
if: github.repository == 'nushell/nightly'
|
||||
with:
|
||||
version: 0.105.1
|
||||
version: 0.103.0
|
||||
|
||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
||||
- name: Prepare for Nightly Release
|
||||
@ -127,7 +127,6 @@ jobs:
|
||||
- armv7-unknown-linux-musleabihf
|
||||
- riscv64gc-unknown-linux-gnu
|
||||
- loongarch64-unknown-linux-gnu
|
||||
- loongarch64-unknown-linux-musl
|
||||
include:
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
@ -153,8 +152,6 @@ jobs:
|
||||
os: ubuntu-22.04
|
||||
- target: loongarch64-unknown-linux-gnu
|
||||
os: ubuntu-22.04
|
||||
- target: loongarch64-unknown-linux-musl
|
||||
os: ubuntu-22.04
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
@ -182,22 +179,36 @@ jobs:
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
cache: false
|
||||
rustflags: ''
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||
with:
|
||||
version: 0.105.1
|
||||
version: 0.103.0
|
||||
|
||||
- name: Release Nu Binary
|
||||
id: nu
|
||||
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||
run: nu .github/workflows/release-pkg.nu
|
||||
env:
|
||||
OS: ${{ matrix.os }}
|
||||
REF: ${{ github.ref }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Build Nu for Windows ARM64
|
||||
id: nu0
|
||||
shell: pwsh
|
||||
if: ${{ matrix.os == 'windows-11-arm' }}
|
||||
run: |
|
||||
$env:OS = 'windows'
|
||||
$env:REF = '${{ github.ref }}'
|
||||
$env:TARGET = '${{ matrix.target }}'
|
||||
cargo build --release --all --target aarch64-pc-windows-msvc
|
||||
cp ./target/${{ matrix.target }}/release/nu.exe .
|
||||
./nu.exe -c 'version'
|
||||
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
|
||||
|
||||
- name: Create an Issue for Release Failure
|
||||
if: ${{ failure() }}
|
||||
uses: JasonEtco/create-an-issue@v2
|
||||
@ -217,7 +228,9 @@ jobs:
|
||||
prerelease: true
|
||||
files: |
|
||||
${{ steps.nu.outputs.msi }}
|
||||
${{ steps.nu0.outputs.msi }}
|
||||
${{ steps.nu.outputs.archive }}
|
||||
${{ steps.nu0.outputs.archive }}
|
||||
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
|
||||
name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
|
||||
env:
|
||||
@ -263,7 +276,7 @@ jobs:
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
with:
|
||||
version: 0.105.1
|
||||
version: 0.103.0
|
||||
|
||||
# Keep the last a few releases
|
||||
- name: Delete Older Releases
|
||||
|
44
.github/workflows/pre-release-checkup.yml
vendored
44
.github/workflows/pre-release-checkup.yml
vendored
@ -1,44 +0,0 @@
|
||||
name: Checks to perform pre-release (manual)
|
||||
on:
|
||||
- workflow_dispatch
|
||||
|
||||
|
||||
env:
|
||||
NUSHELL_CARGO_PROFILE: ci
|
||||
NU_LOG_LEVEL: DEBUG
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
platform: [windows-latest, macos-latest, ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
|
||||
- name: Feature power set
|
||||
run: |
|
||||
cargo hack --all --feature-powerset --at-least-one-of rustls-tls,native-tls --mutually-exclusive-features rustls-tls,native-tls --mutually-exclusive-features rustls-tls,static-link-openssl --skip default-no-clipboard,stable,mimalloc check
|
||||
# Don't build fully for now as it will run out of disk space
|
||||
# - name: Build all crates
|
||||
# run: cargo hack --all build --clean-per-run
|
||||
|
||||
- name: Check for clean repo
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "there are changes";
|
||||
git status --porcelain
|
||||
exit 1
|
||||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
62
.github/workflows/release-msi.nu
vendored
62
.github/workflows/release-msi.nu
vendored
@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env nu
|
||||
|
||||
# Created: 2025/05/21 19:05:20
|
||||
# Description:
|
||||
# A script to build Windows MSI packages for NuShell. Need wix 6.0 to be installed.
|
||||
# The script will download the specified NuShell release, extract it, and create an MSI package.
|
||||
# Can be run locally or in GitHub Actions.
|
||||
# To run this script locally:
|
||||
# load-env { TARGET: 'x86_64-pc-windows-msvc' REF: '0.103.0' GITHUB_REPOSITORY: 'nushell/nushell' }
|
||||
# nu .github/workflows/release-msi.nu
|
||||
|
||||
def build-msi [] {
|
||||
let target = $env.TARGET
|
||||
# We should read the version from the environment variable first
|
||||
# As we may build the MSI package for a specific version not the latest one
|
||||
let version = $env.MSI_VERSION? | default (open Cargo.toml | get package.version)
|
||||
let arch = if $nu.os-info.arch =~ 'x86_64' { 'x64' } else { 'arm64' }
|
||||
|
||||
print $'Building msi package for (ansi g)($target)(ansi reset) with version (ansi g)($version)(ansi reset) from tag (ansi g)($env.REF)(ansi reset)...'
|
||||
fetch-nu-pkg
|
||||
# Create extra Windows msi release package if dotnet and wix are available
|
||||
let installed = [dotnet wix] | all { (which $in | length) > 0 }
|
||||
if $installed and (wix --version | split row . | first | into int) >= 6 {
|
||||
|
||||
print $'(char nl)Start creating Windows msi package with the following contents...'
|
||||
cd wix; hr-line
|
||||
cp nu/README.txt .
|
||||
ls -f nu/* | print
|
||||
./nu/nu.exe -c $'NU_RELEASE_VERSION=($version) dotnet build -c Release -p:Platform=($arch)'
|
||||
glob **/*.msi | print
|
||||
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||
let wixRelease = (glob **/*.msi | where $it =~ bin | get 0 | str replace --all '\' '/')
|
||||
let msi = $'($wixRelease | path dirname)/nu-($version)-($target).msi'
|
||||
mv $wixRelease $msi
|
||||
print $'MSI archive: ---> ($msi)';
|
||||
# Run only in GitHub Actions
|
||||
if ($env.GITHUB_ACTIONS? | default false | into bool) {
|
||||
echo $"msi=($msi)(char nl)" o>> $env.GITHUB_OUTPUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def fetch-nu-pkg [] {
|
||||
mkdir wix/nu
|
||||
# See: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
|
||||
gh release download $env.REF --repo $env.GITHUB_REPOSITORY --pattern $'*-($env.TARGET).zip' --dir wix/nu
|
||||
cd wix/nu
|
||||
let pkg = ls *.zip | get name.0
|
||||
unzip $pkg
|
||||
rm $pkg
|
||||
ls | print
|
||||
}
|
||||
|
||||
# Print a horizontal line marker
|
||||
def 'hr-line' [
|
||||
--blank-line(-b)
|
||||
] {
|
||||
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
|
||||
if $blank_line { char nl }
|
||||
}
|
||||
|
||||
alias main = build-msi
|
103
.github/workflows/release-msi.yml
vendored
103
.github/workflows/release-msi.yml
vendored
@ -1,103 +0,0 @@
|
||||
#
|
||||
# REF:
|
||||
# 1. https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
|
||||
#
|
||||
name: Build Windows MSI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
required: true
|
||||
description: 'Tag to Rebuild MSI'
|
||||
version:
|
||||
description: 'Version of Rebuild MSI'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Nu
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-pc-windows-msvc
|
||||
- aarch64-pc-windows-msvc
|
||||
extra: ['bin']
|
||||
|
||||
include:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-11-arm
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Wix Toolset 6 for Windows
|
||||
shell: pwsh
|
||||
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||
run: |
|
||||
dotnet tool install --global wix --version 6.0.0
|
||||
dotnet workload install wix
|
||||
$wixPath = "$env:USERPROFILE\.dotnet\tools"
|
||||
echo "$wixPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
$env:PATH = "$wixPath;$env:PATH"
|
||||
wix --version
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
with:
|
||||
version: 0.105.1
|
||||
|
||||
- name: Release MSI Packages
|
||||
id: nu
|
||||
run: nu .github/workflows/release-msi.nu
|
||||
env:
|
||||
OS: ${{ matrix.os }}
|
||||
REF: ${{ inputs.tag }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
MSI_VERSION: ${{ inputs.version }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# REF: https://github.com/marketplace/actions/gh-release
|
||||
- name: Publish Archive
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
with:
|
||||
tag_name: ${{ inputs.tag }}
|
||||
files: ${{ steps.nu.outputs.msi }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
sha256sum:
|
||||
needs: release
|
||||
name: Create Sha256sum
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Release Archives
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: >-
|
||||
gh release download ${{ inputs.tag }}
|
||||
--repo ${{ github.repository }}
|
||||
--pattern '*'
|
||||
--dir release
|
||||
- name: Create Checksums
|
||||
run: cd release && rm -f SHA256SUMS && shasum -a 256 * > ../SHA256SUMS
|
||||
- name: Publish Checksums
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
with:
|
||||
files: SHA256SUMS
|
||||
tag_name: ${{ inputs.tag }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
30
.github/workflows/release-pkg.nu
vendored
30
.github/workflows/release-pkg.nu
vendored
@ -8,10 +8,10 @@
|
||||
|
||||
# Instructions for manually creating an MSI for Winget Releases when they fail
|
||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
||||
# Updated again on 2023-02-23 because MSIs are still failing validation
|
||||
# Updated again on 2023-02-23 because msis are still failing validation
|
||||
# To run this manual for windows here are the steps I take
|
||||
# checkout the release you want to publish
|
||||
# 1. git checkout 0.103.0
|
||||
# 1. git checkout 0.86.0
|
||||
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
|
||||
# 2. $env:CARGO_TARGET_DIR = ""
|
||||
# 2. hide-env CARGO_TARGET_DIR
|
||||
@ -23,13 +23,19 @@
|
||||
# 7. $env.Path = ($env.Path | append 'c:\apps\7-zip')
|
||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
||||
# 8. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||
# make sure you have the wix 6.0 installed: dotnet tool install --global wix --version 6.0.0
|
||||
# then build nu*.exe and the MSI installer by running:
|
||||
# 9. source .github\workflows\release-pkg.nu
|
||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
||||
# 9. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
||||
# You need to run the release-pkg twice. The first pass, with _EXTRA_ as 'bin', makes the output
|
||||
# folder and builds everything. The second pass, that generates the msi file, with _EXTRA_ as 'msi'
|
||||
# 10. $env._EXTRA_ = 'bin'
|
||||
# 11. source .github\workflows\release-pkg.nu
|
||||
# 12. cd ..
|
||||
# 13. $env._EXTRA_ = 'msi'
|
||||
# 14. source .github\workflows\release-pkg.nu
|
||||
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
|
||||
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
||||
# on the winget-pkgs PR. To generate the hash, run this command
|
||||
# 10. open wix\bin\x64\Release\nu-0.103.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||
# 15. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
||||
|
||||
|
||||
@ -79,14 +85,14 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||
cargo-build-nu
|
||||
}
|
||||
'aarch64-unknown-linux-musl' => {
|
||||
aria2c https://github.com/nushell/integrations/releases/download/build-tools/aarch64-linux-musl-cross.tgz
|
||||
aria2c https://musl.cc/aarch64-linux-musl-cross.tgz
|
||||
tar -xf aarch64-linux-musl-cross.tgz -C $env.HOME
|
||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/aarch64-linux-musl-cross/bin')
|
||||
$env.CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER = 'aarch64-linux-musl-gcc'
|
||||
cargo-build-nu
|
||||
}
|
||||
'armv7-unknown-linux-musleabihf' => {
|
||||
aria2c https://github.com/nushell/integrations/releases/download/build-tools/armv7r-linux-musleabihf-cross.tgz
|
||||
aria2c https://musl.cc/armv7r-linux-musleabihf-cross.tgz
|
||||
tar -xf armv7r-linux-musleabihf-cross.tgz -C $env.HOME
|
||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/armv7r-linux-musleabihf-cross/bin')
|
||||
$env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER = 'armv7r-linux-musleabihf-gcc'
|
||||
@ -99,14 +105,6 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
|
||||
cargo-build-nu
|
||||
}
|
||||
'loongarch64-unknown-linux-musl' => {
|
||||
print $"(ansi g)Downloading LoongArch64 musl cross-compilation toolchain...(ansi reset)"
|
||||
aria2c -q https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-linux-musl-cross-gcc-13.2.0/loongarch64-linux-musl-cross.tgz
|
||||
tar -xf loongarch64-linux-musl-cross.tgz
|
||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/loongarch64-linux-musl-cross/bin')
|
||||
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER = "loongarch64-linux-musl-gcc"
|
||||
cargo-build-nu
|
||||
}
|
||||
_ => {
|
||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||
# Actually just for x86_64-unknown-linux-musl target
|
||||
|
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@ -35,7 +35,6 @@ jobs:
|
||||
- armv7-unknown-linux-musleabihf
|
||||
- riscv64gc-unknown-linux-gnu
|
||||
- loongarch64-unknown-linux-gnu
|
||||
- loongarch64-unknown-linux-musl
|
||||
include:
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
@ -61,8 +60,6 @@ jobs:
|
||||
os: ubuntu-22.04
|
||||
- target: loongarch64-unknown-linux-gnu
|
||||
os: ubuntu-22.04
|
||||
- target: loongarch64-unknown-linux-musl
|
||||
os: ubuntu-22.04
|
||||
|
||||
runs-on: ${{matrix.os}}
|
||||
|
||||
@ -93,17 +90,32 @@ jobs:
|
||||
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||
with:
|
||||
version: 0.105.1
|
||||
version: 0.103.0
|
||||
|
||||
- name: Release Nu Binary
|
||||
id: nu
|
||||
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||
run: nu .github/workflows/release-pkg.nu
|
||||
env:
|
||||
OS: ${{ matrix.os }}
|
||||
REF: ${{ github.ref }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
|
||||
- name: Build Nu for Windows ARM64
|
||||
id: nu0
|
||||
shell: pwsh
|
||||
if: ${{ matrix.os == 'windows-11-arm' }}
|
||||
run: |
|
||||
$env:OS = 'windows'
|
||||
$env:REF = '${{ github.ref }}'
|
||||
$env:TARGET = '${{ matrix.target }}'
|
||||
cargo build --release --all --target aarch64-pc-windows-msvc
|
||||
cp ./target/${{ matrix.target }}/release/nu.exe .
|
||||
./nu.exe -c 'version'
|
||||
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
|
||||
|
||||
# WARN: Don't upgrade this action due to the release per asset issue.
|
||||
# See: https://github.com/softprops/action-gh-release/issues/445
|
||||
- name: Publish Archive
|
||||
@ -113,7 +125,9 @@ jobs:
|
||||
draft: true
|
||||
files: |
|
||||
${{ steps.nu.outputs.msi }}
|
||||
${{ steps.nu0.outputs.msi }}
|
||||
${{ steps.nu.outputs.archive }}
|
||||
${{ steps.nu0.outputs.archive }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,4 +10,4 @@ jobs:
|
||||
uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@v1.35.4
|
||||
uses: crate-ci/typos@v1.31.1
|
||||
|
5
.github/workflows/winget-submission.yml
vendored
5
.github/workflows/winget-submission.yml
vendored
@ -10,11 +10,6 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
|
||||
winget:
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -32,17 +32,11 @@ unstable_cargo_features.txt
|
||||
# Helix configuration folder
|
||||
.helix/*
|
||||
.helix
|
||||
wix/bin/
|
||||
wix/obj/
|
||||
wix/nu/
|
||||
|
||||
# Coverage tools
|
||||
lcov.info
|
||||
tarpaulin-report.html
|
||||
|
||||
# benchmarking
|
||||
/tango
|
||||
|
||||
# Visual Studio
|
||||
.vs/*
|
||||
*.rsproj
|
||||
|
@ -3,7 +3,6 @@
|
||||
Welcome to Nushell and thank you for considering contributing!
|
||||
|
||||
## Table of contents
|
||||
- [Tips for submitting PRs](#tips-for-submitting-prs)
|
||||
- [Proposing design changes](#proposing-design-changes)
|
||||
- [Developing](#developing)
|
||||
- [Setup](#setup)
|
||||
@ -21,51 +20,6 @@ More resources can be found in the nascent [developer documentation](devdocs/REA
|
||||
- [Platform support policy](devdocs/PLATFORM_SUPPORT.md)
|
||||
- [Our Rust style](devdocs/rust_style.md)
|
||||
|
||||
## Tips for submitting PRs
|
||||
|
||||
Thank you for improving Nushell! We are always glad to see contributions, and we are absolutely willing to talk through the design or implementation of your PR. Come talk with us in [Discord](https://discordapp.com/invite/NtAbbGn), or create a GitHub discussion or draft PR and we can help you work out the details from there.
|
||||
|
||||
**Please talk to the core team before making major changes!** See the [proposing design changes](#proposing-design-changes) for more details.
|
||||
|
||||
### Release notes section
|
||||
|
||||
In our PR template, we have a "Release notes summary" section which will be included in our release notes for our blog.
|
||||
|
||||
This section should include all information about your change which is relevant to a user of Nushell. You should try to keep it **brief and simple to understand**, and focus on the ways your change directly impacts the user experience. We highly encourage adding examples and, when relevant, screenshots in this section.
|
||||
|
||||
Please make sure to consider both the *intended changes*, such as additions or deliberate breaking changes **and** possible *side effects* that might change how users interact with a command or feature. It's important to think carefully about the ways that your PR might affect any aspect of the user experience, and to document these changes even if they seem minor or aren't directly related to the main purpose of the PR.
|
||||
|
||||
This section might not be relevant for all PRs. If your PR is a work in progress, feel free to write "WIP"/"TODO"/etc in this section. You can also write "N/A" if this is a technical change which doesn't impact the user experience.
|
||||
|
||||
If you're not sure what to put here, or need some help, **a core team member would be glad to help you out**. We may also makes some tweaks to your release notes section. Please don't take it personally, we just want to make sure our release notes are polished and easy to understand. Once the release notes section is ready, we'll add the (TODO label name) label to indicate that the release notes section is ready to be included in the actual release notes.
|
||||
|
||||
### Tests and formatting checks
|
||||
|
||||
Our CI system automatically checks formatting and runs our tests. If you're running into an issue, or just want to make sure everything is ready to go before creating your PR, you can run the checks yourself:
|
||||
|
||||
```nushell
|
||||
use toolkit.nu # or use an `env_change` hook to activate it automatically
|
||||
toolkit check pr
|
||||
```
|
||||
|
||||
Furthermore, you can also runs these checks individually with the subcommands of `toolkit`, or run the underlying commands yourself:
|
||||
|
||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style
|
||||
- `cargo test --workspace` to check that all tests pass (on Windows make sure to enable [developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
|
||||
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library
|
||||
|
||||
If the checks are passing on your local system, but CI just won't pass, feel free to ask for help from the core team.
|
||||
|
||||
### Linking and mentioning issues
|
||||
|
||||
If your PR closes one or more issues, you can automatically link the PR with them by using one of the [linking keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword):
|
||||
|
||||
- This PR should close #xxxx
|
||||
- Fixes #xxxx
|
||||
|
||||
You can also mention related issues, PRs or discussions!
|
||||
|
||||
## Proposing design changes
|
||||
|
||||
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
|
||||
@ -77,7 +31,7 @@ The review process can be summarized as follows:
|
||||
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
||||
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
||||
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
||||
4. If your PR includes any user-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
||||
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
||||
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
||||
6. When we all agree, the PR will be merged.
|
||||
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
||||
|
1131
Cargo.lock
generated
1131
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
150
Cargo.toml
150
Cargo.toml
@ -4,14 +4,14 @@ build = "scripts/build.rs"
|
||||
default-run = "nu"
|
||||
description = "A new type of shell"
|
||||
documentation = "https://www.nushell.sh/book/"
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
exclude = ["images"]
|
||||
homepage = "https://www.nushell.sh"
|
||||
license = "MIT"
|
||||
name = "nu"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
rust-version = "1.87.0"
|
||||
version = "0.106.2"
|
||||
rust-version = "1.84.1"
|
||||
version = "0.104.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -24,37 +24,36 @@ pkg-fmt = "zip"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/nu_plugin_custom_values",
|
||||
"crates/nu_plugin_example",
|
||||
"crates/nu_plugin_formats",
|
||||
"crates/nu_plugin_gstat",
|
||||
"crates/nu_plugin_inc",
|
||||
"crates/nu_plugin_polars",
|
||||
"crates/nu_plugin_query",
|
||||
"crates/nu_plugin_stress_internals",
|
||||
"crates/nu-cli",
|
||||
"crates/nu-engine",
|
||||
"crates/nu-parser",
|
||||
"crates/nu-system",
|
||||
"crates/nu-cmd-base",
|
||||
"crates/nu-cmd-extra",
|
||||
"crates/nu-cmd-lang",
|
||||
"crates/nu-cmd-plugin",
|
||||
"crates/nu-color-config",
|
||||
"crates/nu-command",
|
||||
"crates/nu-derive-value",
|
||||
"crates/nu-engine",
|
||||
"crates/nu-experimental",
|
||||
"crates/nu-color-config",
|
||||
"crates/nu-explore",
|
||||
"crates/nu-json",
|
||||
"crates/nu-lsp",
|
||||
"crates/nu-parser",
|
||||
"crates/nu-pretty-hex",
|
||||
"crates/nu-protocol",
|
||||
"crates/nu-derive-value",
|
||||
"crates/nu-plugin",
|
||||
"crates/nu-plugin-core",
|
||||
"crates/nu-plugin-engine",
|
||||
"crates/nu-plugin-protocol",
|
||||
"crates/nu-plugin-test-support",
|
||||
"crates/nu-plugin",
|
||||
"crates/nu-pretty-hex",
|
||||
"crates/nu-protocol",
|
||||
"crates/nu_plugin_inc",
|
||||
"crates/nu_plugin_gstat",
|
||||
"crates/nu_plugin_example",
|
||||
"crates/nu_plugin_query",
|
||||
"crates/nu_plugin_custom_values",
|
||||
"crates/nu_plugin_formats",
|
||||
"crates/nu_plugin_polars",
|
||||
"crates/nu_plugin_stress_internals",
|
||||
"crates/nu-std",
|
||||
"crates/nu-system",
|
||||
"crates/nu-table",
|
||||
"crates/nu-term-grid",
|
||||
"crates/nu-test-support",
|
||||
@ -64,15 +63,15 @@ members = [
|
||||
|
||||
[workspace.dependencies]
|
||||
alphanumeric-sort = "1.5"
|
||||
ansi-str = "0.9"
|
||||
ansi-str = "0.8"
|
||||
anyhow = "1.0.82"
|
||||
base64 = "0.22.1"
|
||||
bracoxide = "0.1.6"
|
||||
bracoxide = "0.1.5"
|
||||
brotli = "7.0"
|
||||
byteorder = "1.5"
|
||||
bytes = "1"
|
||||
bytesize = "1.3.3"
|
||||
calamine = "0.28"
|
||||
calamine = "0.27"
|
||||
chardetng = "0.1.17"
|
||||
chrono = { default-features = false, version = "0.4.34" }
|
||||
chrono-humanize = "0.2.3"
|
||||
@ -83,34 +82,32 @@ csv = "1.3"
|
||||
ctrlc = "3.4"
|
||||
devicons = "0.6.12"
|
||||
dialoguer = { default-features = false, version = "0.11" }
|
||||
fuzzy-matcher = { version = "^0.3.7" }
|
||||
digest = { default-features = false, version = "0.10" }
|
||||
dirs = "5.0"
|
||||
dirs-sys = "0.4"
|
||||
dtparse = "2.0"
|
||||
encoding_rs = "0.8"
|
||||
fancy-regex = "0.16"
|
||||
fancy-regex = "0.14"
|
||||
filesize = "0.2"
|
||||
filetime = "0.2"
|
||||
heck = "0.5.0"
|
||||
http = "1.3.1"
|
||||
human-date-parser = "0.3.0"
|
||||
indexmap = "2.10"
|
||||
indexmap = "2.9"
|
||||
indicatif = "0.17"
|
||||
interprocess = "2.2.0"
|
||||
is_executable = "1.0"
|
||||
itertools = "0.14"
|
||||
itertools = "0.13"
|
||||
libc = "0.2"
|
||||
libproc = "0.14"
|
||||
log = "0.4"
|
||||
lru = "0.12"
|
||||
lscolors = { version = "0.20", default-features = false }
|
||||
lscolors = { version = "0.17", default-features = false }
|
||||
lsp-server = "0.7.8"
|
||||
lsp-types = { version = "0.97.0", features = ["proposed"] }
|
||||
lsp-textdocument = "0.4.2"
|
||||
mach2 = "0.4"
|
||||
md5 = { version = "0.10", package = "md-5" }
|
||||
miette = "7.6"
|
||||
miette = "7.5"
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0"
|
||||
mockito = { version = "1.7", default-features = false }
|
||||
@ -134,16 +131,16 @@ proc-macro-error2 = "2.0"
|
||||
proc-macro2 = "1.0"
|
||||
procfs = "0.17.0"
|
||||
pwd = "1.3"
|
||||
quick-xml = "0.37.5"
|
||||
quick-xml = "0.37.0"
|
||||
quickcheck = "1.0"
|
||||
quickcheck_macros = "1.1"
|
||||
quickcheck_macros = "1.0"
|
||||
quote = "1.0"
|
||||
rand = "0.9"
|
||||
getrandom = "0.2" # pick same version that rand requires
|
||||
rand_chacha = "0.9"
|
||||
ratatui = "0.29"
|
||||
rayon = "1.11"
|
||||
reedline = "0.41.0"
|
||||
rayon = "1.10"
|
||||
reedline = "0.40.0"
|
||||
rmp = "0.8"
|
||||
rmp-serde = "1.3"
|
||||
roxmltree = "0.20"
|
||||
@ -151,33 +148,27 @@ rstest = { version = "0.23", default-features = false }
|
||||
rstest_reuse = "0.7"
|
||||
rusqlite = "0.31"
|
||||
rust-embed = "8.7.0"
|
||||
# We have to fix rustls and ureq versions
|
||||
# because we use unversioned api to allow users set up their own
|
||||
# crypto providers (grep for "unversioned")
|
||||
rustls = { version = "=0.23.28", default-features = false, features = ["std", "tls12"] }
|
||||
rustls-native-certs = "0.8"
|
||||
scopeguard = { version = "1.2.0" }
|
||||
serde = { version = "1.0" }
|
||||
serde_json = "1.0.97"
|
||||
serde_urlencoded = "0.7.1"
|
||||
serde_yaml = "0.9.33"
|
||||
sha2 = "0.10"
|
||||
strip-ansi-escapes = "0.2.1"
|
||||
strip-ansi-escapes = "0.2.0"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
syn = "2.0"
|
||||
sysinfo = "0.36"
|
||||
tabled = { version = "0.20", default-features = false }
|
||||
tempfile = "3.20"
|
||||
thiserror = "2.0.12"
|
||||
titlecase = "3.6"
|
||||
sysinfo = "0.33"
|
||||
tabled = { version = "0.17.0", default-features = false }
|
||||
tempfile = "3.15"
|
||||
titlecase = "3.5"
|
||||
toml = "0.8"
|
||||
trash = "5.2"
|
||||
update-informer = { version = "1.3.0", default-features = false, features = ["github", "ureq"] }
|
||||
update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] }
|
||||
umask = "2.1"
|
||||
unicode-segmentation = "1.12"
|
||||
unicode-width = "0.2"
|
||||
ureq = { version = "=3.0.12", default-features = false, features = ["socks-proxy"] }
|
||||
ureq = { version = "2.12", default-features = false, features = ["socks-proxy"] }
|
||||
url = "2.2"
|
||||
uu_cp = "0.0.30"
|
||||
uu_mkdir = "0.0.30"
|
||||
@ -191,41 +182,37 @@ uuid = "1.16.0"
|
||||
v_htmlescape = "0.15.0"
|
||||
wax = "0.6"
|
||||
web-time = "1.1.0"
|
||||
which = "8.0.0"
|
||||
which = "7.0.0"
|
||||
windows = "0.56"
|
||||
windows-sys = "0.48"
|
||||
winreg = "0.52"
|
||||
memchr = "2.7.4"
|
||||
webpki-roots = "1.0"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
|
||||
# todo = "warn"
|
||||
unchecked_duration_subtraction = "warn"
|
||||
used_underscore_binding = "warn"
|
||||
result_large_err = "allow"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.106.2" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.106.2" }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.106.2" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.106.2" }
|
||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.106.2", optional = true }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.106.2", default-features = false, features = ["os"] }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.106.2" }
|
||||
nu-experimental = { path = "./crates/nu-experimental", version = "0.106.2" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.106.2" }
|
||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.106.2" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.106.2" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.106.2" }
|
||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.106.2" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.106.2" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.106.2" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.106.2" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.106.2" }
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.104.1" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.104.1" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.104.1" }
|
||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.104.1", optional = true }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.104.1" }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.104.1" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.104.1" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.104.1" }
|
||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.104.1" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.104.1" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.104.1" }
|
||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.104.1" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.104.1" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.104.1" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.104.1" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.104.1" }
|
||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||
|
||||
crossterm = { workspace = true }
|
||||
@ -254,9 +241,9 @@ nix = { workspace = true, default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.106.2" }
|
||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.106.2" }
|
||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.106.2" }
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.104.1" }
|
||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.104.1" }
|
||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.104.1" }
|
||||
assert_cmd = "2.0"
|
||||
dirs = { workspace = true }
|
||||
tango-bench = "0.6"
|
||||
@ -267,14 +254,10 @@ serial_test = "3.2"
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
# Enable all features while still avoiding mutually exclusive features.
|
||||
# Use this if `--all-features` fails.
|
||||
full = ["plugin", "rustls-tls", "system-clipboard", "trash-support", "sqlite"]
|
||||
|
||||
plugin = [
|
||||
# crates
|
||||
"dep:nu-cmd-plugin",
|
||||
"dep:nu-plugin-engine",
|
||||
"nu-cmd-plugin",
|
||||
"nu-plugin-engine",
|
||||
|
||||
# features
|
||||
"nu-cli/plugin",
|
||||
@ -286,34 +269,31 @@ plugin = [
|
||||
"nu-protocol/plugin",
|
||||
]
|
||||
|
||||
native-tls = ["nu-command/native-tls"]
|
||||
rustls-tls = ["nu-command/rustls-tls"]
|
||||
|
||||
default = [
|
||||
"plugin",
|
||||
"trash-support",
|
||||
"sqlite",
|
||||
"rustls-tls"
|
||||
]
|
||||
stable = ["default"]
|
||||
# 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/);
|
||||
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||
static-link-openssl = ["dep:openssl"]
|
||||
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
||||
|
||||
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
||||
# Missing X server/ Wayland can cause issues
|
||||
system-clipboard = [
|
||||
"reedline/system_clipboard",
|
||||
"nu-cli/system-clipboard",
|
||||
"nu-cmd-lang/system-clipboard",
|
||||
]
|
||||
|
||||
# Stable (Default)
|
||||
trash-support = ["nu-command/trash-support"]
|
||||
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
||||
|
||||
# SQLite commands for nushell
|
||||
sqlite = ["nu-command/sqlite", "nu-std/sqlite"]
|
||||
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s" # Optimize for size
|
||||
@ -343,7 +323,7 @@ bench = false
|
||||
# To use a development version of a dependency please use a global override here
|
||||
# changing versions in each sub-crate of the workspace is tedious
|
||||
[patch.crates-io]
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||
|
||||
# Run all benchmarks with `cargo bench`
|
||||
|
@ -222,7 +222,6 @@ Please submit an issue or PR to be added to this list.
|
||||
- [Dorothy](http://github.com/bevry/dorothy)
|
||||
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
|
||||
- [x-cmd](https://x-cmd.com/mod/nu)
|
||||
- [vfox](https://github.com/version-fox/vfox)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Security Policy
|
||||
|
||||
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
|
||||
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
|
||||
We want to fix those issues without exposing our users to unnecessary risk. Thus we want to explain our security policy.
|
||||
Additional issues may be part of *safety* where the behavior of Nushell as designed and implemented can cause unintended harm or a bug causes damage without the involvement of a third party.
|
||||
|
||||
@ -11,7 +11,7 @@ Only if you provide a strong reasoning and the necessary resources, will we cons
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
|
||||
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
|
||||
Either reach out to the core team on [our Discord server](https://discord.gg/NtAbbGn) to arrange a private channel or use the [GitHub vulnerability reporting form](https://github.com/nushell/nushell/security/advisories/new).
|
||||
Please try to answer the following questions:
|
||||
- How can we reach you for further questions?
|
||||
|
@ -2,8 +2,8 @@ use nu_cli::{eval_source, evaluate_commands};
|
||||
use nu_plugin_core::{Encoder, EncodingType};
|
||||
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
||||
use nu_protocol::{
|
||||
PipelineData, Signals, Span, Spanned, Value,
|
||||
engine::{EngineState, Stack},
|
||||
PipelineData, Signals, Span, Spanned, Value,
|
||||
};
|
||||
use nu_std::load_standard_library;
|
||||
use nu_utils::{get_default_config, get_default_env};
|
||||
@ -11,9 +11,9 @@ use std::{
|
||||
fmt::Write,
|
||||
hint::black_box,
|
||||
rc::Rc,
|
||||
sync::{Arc, atomic::AtomicBool},
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use tango_bench::{IntoBenchmarks, benchmark_fn, tango_benchmarks, tango_main};
|
||||
use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, IntoBenchmarks};
|
||||
|
||||
fn load_bench_commands() -> EngineState {
|
||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
||||
@ -68,14 +68,14 @@ fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
||||
}
|
||||
|
||||
fn bench_command(
|
||||
name: impl Into<String>,
|
||||
command: impl Into<String> + Clone,
|
||||
name: &str,
|
||||
command: &str,
|
||||
stack: Stack,
|
||||
engine: EngineState,
|
||||
) -> impl IntoBenchmarks {
|
||||
let commands = Spanned {
|
||||
span: Span::unknown(),
|
||||
item: command.into(),
|
||||
item: command.to_string(),
|
||||
};
|
||||
[benchmark_fn(name, move |b| {
|
||||
let commands = commands.clone();
|
||||
@ -175,8 +175,8 @@ fn create_example_table_nrows(n: usize) -> String {
|
||||
|
||||
fn bench_record_create(n: usize) -> impl IntoBenchmarks {
|
||||
bench_command(
|
||||
format!("record_create_{n}"),
|
||||
create_flat_record_string(n),
|
||||
&format!("record_create_{n}"),
|
||||
&create_flat_record_string(n),
|
||||
Stack::new(),
|
||||
setup_engine(),
|
||||
)
|
||||
@ -186,7 +186,7 @@ fn bench_record_flat_access(n: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_flat_record_string(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
format!("record_flat_access_{n}"),
|
||||
&format!("record_flat_access_{n}"),
|
||||
"$record.col_0 | ignore",
|
||||
stack,
|
||||
engine,
|
||||
@ -198,8 +198,8 @@ fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
let nested_access = ".col".repeat(n);
|
||||
bench_command(
|
||||
format!("record_nested_access_{n}"),
|
||||
format!("$record{nested_access} | ignore"),
|
||||
&format!("record_nested_access_{n}"),
|
||||
&format!("$record{} | ignore", nested_access),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -213,13 +213,13 @@ fn bench_record_insert(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(format!("record_insert_{n}_{m}"), insert, stack, engine)
|
||||
bench_command(&format!("record_insert_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_table_create(n: usize) -> impl IntoBenchmarks {
|
||||
bench_command(
|
||||
format!("table_create_{n}"),
|
||||
create_example_table_nrows(n),
|
||||
&format!("table_create_{n}"),
|
||||
&create_example_table_nrows(n),
|
||||
Stack::new(),
|
||||
setup_engine(),
|
||||
)
|
||||
@ -229,7 +229,7 @@ fn bench_table_get(n: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
format!("table_get_{n}"),
|
||||
&format!("table_get_{n}"),
|
||||
"$table | get bar | math sum | ignore",
|
||||
stack,
|
||||
engine,
|
||||
@ -240,7 +240,7 @@ fn bench_table_select(n: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
format!("table_select_{n}"),
|
||||
&format!("table_select_{n}"),
|
||||
"$table | select foo baz | ignore",
|
||||
stack,
|
||||
engine,
|
||||
@ -255,7 +255,7 @@ fn bench_table_insert_row(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
write!(insert, " | insert {i} {{ foo: 0, bar: 1, baz: {i} }}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(format!("table_insert_row_{n}_{m}"), insert, stack, engine)
|
||||
bench_command(&format!("table_insert_row_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
@ -266,15 +266,15 @@ fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(format!("table_insert_col_{n}_{m}"), insert, stack, engine)
|
||||
bench_command(&format!("table_insert_col_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
format!("eval_interleave_{n}"),
|
||||
format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
||||
&format!("eval_interleave_{n}"),
|
||||
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -285,8 +285,8 @@ fn bench_eval_interleave_with_interrupt(n: usize) -> impl IntoBenchmarks {
|
||||
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
format!("eval_interleave_with_interrupt_{n}"),
|
||||
format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
||||
&format!("eval_interleave_with_interrupt_{n}"),
|
||||
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -296,8 +296,8 @@ fn bench_eval_for(n: usize) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
format!("eval_for_{n}"),
|
||||
format!("(for $x in (1..{n}) {{ 1 }}) | ignore"),
|
||||
&format!("eval_for_{n}"),
|
||||
&format!("(for $x in (1..{n}) {{ 1 }}) | ignore"),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -307,8 +307,8 @@ fn bench_eval_each(n: usize) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
format!("eval_each_{n}"),
|
||||
format!("(1..{n}) | each {{|_| 1 }} | ignore"),
|
||||
&format!("eval_each_{n}"),
|
||||
&format!("(1..{n}) | each {{|_| 1 }} | ignore"),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -318,8 +318,8 @@ fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
format!("eval_par_each_{n}"),
|
||||
format!("(1..{n}) | par-each -t 2 {{|_| 1 }} | ignore"),
|
||||
&format!("eval_par_each_{n}"),
|
||||
&format!("(1..{}) | par-each -t 2 {{|_| 1 }} | ignore", n),
|
||||
stack,
|
||||
engine,
|
||||
)
|
||||
@ -357,7 +357,7 @@ fn encode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
|
||||
|
||||
[benchmark_fn(
|
||||
format!("encode_json_{row_cnt}_{col_cnt}"),
|
||||
format!("encode_json_{}_{}", row_cnt, col_cnt),
|
||||
move |b| {
|
||||
let encoder = encoder.clone();
|
||||
let test_data = test_data.clone();
|
||||
@ -377,7 +377,7 @@ fn encode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
|
||||
|
||||
[benchmark_fn(
|
||||
format!("encode_msgpack_{row_cnt}_{col_cnt}"),
|
||||
format!("encode_msgpack_{}_{}", row_cnt, col_cnt),
|
||||
move |b| {
|
||||
let encoder = encoder.clone();
|
||||
let test_data = test_data.clone();
|
||||
@ -399,7 +399,7 @@ fn decode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
||||
encoder.encode(&test_data, &mut res).unwrap();
|
||||
|
||||
[benchmark_fn(
|
||||
format!("decode_json_{row_cnt}_{col_cnt}"),
|
||||
format!("decode_json_{}_{}", row_cnt, col_cnt),
|
||||
move |b| {
|
||||
let res = res.clone();
|
||||
b.iter(move || {
|
||||
@ -422,7 +422,7 @@ fn decode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
||||
encoder.encode(&test_data, &mut res).unwrap();
|
||||
|
||||
[benchmark_fn(
|
||||
format!("decode_msgpack_{row_cnt}_{col_cnt}"),
|
||||
format!("decode_msgpack_{}_{}", row_cnt, col_cnt),
|
||||
move |b| {
|
||||
let res = res.clone();
|
||||
b.iter(move || {
|
||||
|
@ -2,32 +2,32 @@
|
||||
authors = ["The Nushell Project Developers"]
|
||||
description = "CLI-related functionality for Nushell"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.106.2"
|
||||
version = "0.104.1"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.2" }
|
||||
nu-command = { path = "../nu-command", version = "0.106.2" }
|
||||
nu-std = { path = "../nu-std", version = "0.106.2" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.104.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.104.1" }
|
||||
nu-std = { path = "../nu-std", version = "0.104.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.104.1" }
|
||||
rstest = { workspace = true, default-features = false }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.106.2", features = ["os"] }
|
||||
nu-glob = { path = "../nu-glob", version = "0.106.2" }
|
||||
nu-path = { path = "../nu-path", version = "0.106.2" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.106.2" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.2", optional = true }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.106.2", features = ["os"] }
|
||||
nu-utils = { path = "../nu-utils", version = "0.106.2" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.106.2" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.104.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", features = ["os"] }
|
||||
nu-glob = { path = "../nu-glob", version = "0.104.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.104.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.104.1", optional = true }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", features = ["os"] }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.104.1" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{
|
||||
HistoryFileFormat,
|
||||
shell_error::{self, io::IoError},
|
||||
};
|
||||
use nu_protocol::{shell_error::io::IoError, HistoryFileFormat};
|
||||
use reedline::{
|
||||
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
|
||||
SqliteBackedHistory,
|
||||
@ -97,7 +94,7 @@ impl Command for History {
|
||||
})
|
||||
})
|
||||
.ok_or(IoError::new(
|
||||
shell_error::io::ErrorKind::FileNotFound,
|
||||
std::io::ErrorKind::NotFound,
|
||||
head,
|
||||
history_path,
|
||||
))?
|
||||
@ -113,7 +110,7 @@ impl Command for History {
|
||||
})
|
||||
})
|
||||
.ok_or(IoError::new(
|
||||
shell_error::io::ErrorKind::FileNotFound,
|
||||
std::io::ErrorKind::NotFound,
|
||||
head,
|
||||
history_path,
|
||||
))?
|
||||
|
@ -2,8 +2,8 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{
|
||||
HistoryFileFormat,
|
||||
shell_error::{self, io::IoError},
|
||||
HistoryFileFormat,
|
||||
};
|
||||
|
||||
use reedline::{
|
||||
@ -26,7 +26,7 @@ impl Command for HistoryImport {
|
||||
|
||||
fn extra_description(&self) -> &str {
|
||||
r#"Can import history from input, either successive command lines or more detailed records. If providing records, available fields are:
|
||||
command, start_timestamp, hostname, cwd, duration, exit_status.
|
||||
command_line, id, start_timestamp, hostname, cwd, duration, exit_status.
|
||||
|
||||
If no input is provided, will import all history items from existing history in the other format: if current history is stored in sqlite, it will store it in plain text and vice versa.
|
||||
|
||||
@ -48,7 +48,8 @@ Note that history item IDs are ignored when importing from file."#
|
||||
vec![
|
||||
Example {
|
||||
example: "history import",
|
||||
description: "Append all items from history in the other format to the current history",
|
||||
description:
|
||||
"Append all items from history in the other format to the current history",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
@ -57,7 +58,7 @@ Note that history item IDs are ignored when importing from file."#
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
example: "[[ command cwd ]; [ foo /home ]] | history import",
|
||||
example: "[[ command_line cwd ]; [ foo /home ]] | history import",
|
||||
description: "Append `foo` ran from `/home` to the current history",
|
||||
result: None,
|
||||
},
|
||||
@ -197,7 +198,7 @@ fn item_from_record(mut rec: Record, span: Span) -> Result<HistoryItem, ShellErr
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: format!("missing column: {}", fields::COMMAND_LINE),
|
||||
span,
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@ -282,22 +283,22 @@ fn backup(path: &Path, span: Span) -> Result<Option<PathBuf>, ShellError> {
|
||||
PathBuf::from(path),
|
||||
"history path exists but is not a file",
|
||||
)
|
||||
.into());
|
||||
.into())
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
||||
Err(e) => {
|
||||
return Err(IoError::new_internal(
|
||||
e,
|
||||
e.kind(),
|
||||
"Could not get metadata",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
.into());
|
||||
.into())
|
||||
}
|
||||
}
|
||||
let bak_path = find_backup_path(path, span)?;
|
||||
std::fs::copy(path, &bak_path).map_err(|err| {
|
||||
IoError::new_internal(
|
||||
err.not_found_as(NotFound::File),
|
||||
err.kind(),
|
||||
"Could not copy backup",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crossterm::{
|
||||
QueueableCommand, event::Event, event::KeyCode, event::KeyEvent, execute, terminal,
|
||||
event::Event, event::KeyCode, event::KeyEvent, execute, terminal, QueueableCommand,
|
||||
};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::shell_error::io::IoError;
|
||||
use std::io::{Write, stdout};
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeybindingsListen;
|
||||
@ -42,7 +42,7 @@ impl Command for KeybindingsListen {
|
||||
Err(e) => {
|
||||
terminal::disable_raw_mode().map_err(|err| {
|
||||
IoError::new_internal(
|
||||
err,
|
||||
err.kind(),
|
||||
"Could not disable raw mode",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
@ -71,10 +71,18 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
let config = engine_state.get_config();
|
||||
|
||||
stdout().flush().map_err(|err| {
|
||||
IoError::new_internal(err, "Could not flush stdout", nu_protocol::location!())
|
||||
IoError::new_internal(
|
||||
err.kind(),
|
||||
"Could not flush stdout",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
})?;
|
||||
terminal::enable_raw_mode().map_err(|err| {
|
||||
IoError::new_internal(err, "Could not enable raw mode", nu_protocol::location!())
|
||||
IoError::new_internal(
|
||||
err.kind(),
|
||||
"Could not enable raw mode",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if config.use_kitty_protocol {
|
||||
@ -106,7 +114,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
|
||||
loop {
|
||||
let event = crossterm::event::read().map_err(|err| {
|
||||
IoError::new_internal(err, "Could not read event", nu_protocol::location!())
|
||||
IoError::new_internal(err.kind(), "Could not read event", nu_protocol::location!())
|
||||
})?;
|
||||
if event == Event::Key(KeyCode::Esc.into()) {
|
||||
break;
|
||||
@ -128,7 +136,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
};
|
||||
stdout.queue(crossterm::style::Print(o)).map_err(|err| {
|
||||
IoError::new_internal(
|
||||
err,
|
||||
err.kind(),
|
||||
"Could not print output record",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
@ -136,10 +144,14 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
stdout
|
||||
.queue(crossterm::style::Print("\r\n"))
|
||||
.map_err(|err| {
|
||||
IoError::new_internal(err, "Could not print linebreak", nu_protocol::location!())
|
||||
IoError::new_internal(
|
||||
err.kind(),
|
||||
"Could not print linebreak",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
})?;
|
||||
stdout.flush().map_err(|err| {
|
||||
IoError::new_internal(err, "Could not flush", nu_protocol::location!())
|
||||
IoError::new_internal(err.kind(), "Could not flush", nu_protocol::location!())
|
||||
})?;
|
||||
}
|
||||
|
||||
@ -151,7 +163,11 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
||||
}
|
||||
|
||||
terminal::disable_raw_mode().map_err(|err| {
|
||||
IoError::new_internal(err, "Could not disable raw mode", nu_protocol::location!())
|
||||
IoError::new_internal(
|
||||
err.kind(),
|
||||
"Could not disable raw mode",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Value::nothing(Span::unknown()))
|
||||
|
@ -1,11 +1,11 @@
|
||||
use super::{SemanticSuggestion, completion_options::NuMatcher};
|
||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
||||
use crate::{
|
||||
SuggestionKind,
|
||||
completions::{Completer, CompletionOptions},
|
||||
SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
@ -33,12 +33,13 @@ impl Completer for AttributeCompletion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(name).into_owned(),
|
||||
description: desc,
|
||||
style: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: false,
|
||||
..Default::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(ty, Some(decl_id))),
|
||||
});
|
||||
@ -69,12 +70,13 @@ impl Completer for AttributableCompletion {
|
||||
suggestion: Suggestion {
|
||||
value: cmd.name().into(),
|
||||
description: Some(cmd.description().into()),
|
||||
style: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: false,
|
||||
..Default::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(cmd.command_type(), None)),
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::completions::CompletionOptions;
|
||||
use nu_protocol::{
|
||||
DeclId, Span,
|
||||
engine::{Stack, StateWorkingSet},
|
||||
DeclId, Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||
use nu_engine::{column::get_columns, eval_variable};
|
||||
use nu_protocol::{
|
||||
ShellError, Span, Value,
|
||||
ast::{Expr, Expression, FullCellPath, PathMember},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
eval_const::eval_constant,
|
||||
ShellError, Span, Value,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
@ -103,9 +101,7 @@ pub(crate) fn eval_cell_path(
|
||||
} else {
|
||||
eval_constant(working_set, head)
|
||||
}?;
|
||||
head_value
|
||||
.follow_cell_path(path_members)
|
||||
.map(Cow::into_owned)
|
||||
head_value.follow_cell_path(path_members, false)
|
||||
}
|
||||
|
||||
fn get_suggestions_by_value(
|
||||
@ -118,7 +114,7 @@ fn get_suggestions_by_value(
|
||||
|| s.chars()
|
||||
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
|
||||
{
|
||||
format!("{s:?}")
|
||||
format!("{:?}", s)
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
@ -1,16 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
SuggestionKind,
|
||||
completions::{Completer, CompletionOptions},
|
||||
SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{CommandType, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
use super::{SemanticSuggestion, completion_options::NuMatcher};
|
||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
||||
|
||||
pub struct CommandCompletion {
|
||||
/// Whether to include internal commands
|
||||
@ -52,7 +52,7 @@ impl CommandCompletion {
|
||||
continue;
|
||||
};
|
||||
let value = if matched_internal(&name) {
|
||||
format!("^{name}")
|
||||
format!("^{}", name)
|
||||
} else {
|
||||
name.clone()
|
||||
};
|
||||
|
@ -1,17 +1,17 @@
|
||||
use crate::completions::{
|
||||
base::{SemanticSuggestion, SuggestionKind},
|
||||
AttributableCompletion, AttributeCompletion, CellPathCompletion, CommandCompletion, Completer,
|
||||
CompletionOptions, CustomCompletion, DirectoryCompletion, DotNuCompletion,
|
||||
ExportableCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||
base::{SemanticSuggestion, SuggestionKind},
|
||||
};
|
||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::{flatten_expression, parse, parse_module_file_or_dir};
|
||||
use nu_protocol::{
|
||||
PipelineData, Span, Type, Value,
|
||||
ast::{Argument, Block, Expr, Expression, FindMapResult, ListItem, Traverse},
|
||||
debugger::WithoutDebug,
|
||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||
PipelineData, Span, Type, Value,
|
||||
};
|
||||
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
||||
use std::sync::Arc;
|
||||
@ -176,7 +176,7 @@ impl NuCompleter {
|
||||
&mut working_set,
|
||||
Some("completer"),
|
||||
// Add a placeholder `a` to the end
|
||||
format!("{line}a").as_bytes(),
|
||||
format!("{}a", line).as_bytes(),
|
||||
false,
|
||||
);
|
||||
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
|
||||
@ -348,43 +348,8 @@ impl NuCompleter {
|
||||
for (arg_idx, arg) in call.arguments.iter().enumerate() {
|
||||
let span = arg.span();
|
||||
if span.contains(pos) {
|
||||
// Get custom completion from PositionalArg or Flag
|
||||
let custom_completion_decl_id = {
|
||||
// Check PositionalArg or Flag from Signature
|
||||
let signature = working_set.get_decl(call.decl_id).signature();
|
||||
|
||||
match arg {
|
||||
// For named arguments, check Flag
|
||||
Argument::Named((name, short, value)) => {
|
||||
if value.as_ref().is_none_or(|e| !e.span.contains(pos)) {
|
||||
None
|
||||
} else {
|
||||
// If we're completing the value of the flag,
|
||||
// search for the matching custom completion decl_id (long or short)
|
||||
let flag =
|
||||
signature.get_long_flag(&name.item).or_else(|| {
|
||||
short.as_ref().and_then(|s| {
|
||||
signature.get_short_flag(
|
||||
s.item.chars().next().unwrap_or('_'),
|
||||
)
|
||||
})
|
||||
});
|
||||
flag.and_then(|f| f.custom_completion)
|
||||
}
|
||||
}
|
||||
// For positional arguments, check PositionalArg
|
||||
Argument::Positional(_) => {
|
||||
// Find the right positional argument by index
|
||||
let arg_pos = positional_arg_indices.len();
|
||||
signature
|
||||
.get_positional(arg_pos)
|
||||
.and_then(|pos_arg| pos_arg.custom_completion)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(decl_id) = custom_completion_decl_id {
|
||||
// if customized completion specified, it has highest priority
|
||||
if let Some(decl_id) = arg.expr().and_then(|e| e.custom_completion) {
|
||||
// for `--foo <tab>` and `--foo=<tab>`, the arg span should be trimmed
|
||||
let (new_span, prefix) = if matches!(arg, Argument::Named(_)) {
|
||||
strip_placeholder_with_rsplit(
|
||||
@ -405,8 +370,7 @@ impl NuCompleter {
|
||||
FileCompletion,
|
||||
);
|
||||
|
||||
// Prioritize argument completions over (sub)commands
|
||||
suggestions.splice(0..0, self.process_completion(&mut completer, &ctx));
|
||||
suggestions.extend(self.process_completion(&mut completer, &ctx));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -420,39 +384,33 @@ impl NuCompleter {
|
||||
};
|
||||
self.process_completion(&mut flag_completions, &ctx)
|
||||
};
|
||||
// Prioritize argument completions over (sub)commands
|
||||
suggestions.splice(
|
||||
0..0,
|
||||
match arg {
|
||||
// flags
|
||||
Argument::Named(_) | Argument::Unknown(_)
|
||||
if prefix.starts_with(b"-") =>
|
||||
{
|
||||
flag_completion_helper()
|
||||
}
|
||||
// only when `strip` == false
|
||||
Argument::Positional(_) if prefix == b"-" => {
|
||||
flag_completion_helper()
|
||||
}
|
||||
// complete according to expression type and command head
|
||||
Argument::Positional(expr) => {
|
||||
let command_head = working_set.get_decl(call.decl_id).name();
|
||||
positional_arg_indices.push(arg_idx);
|
||||
self.argument_completion_helper(
|
||||
PositionalArguments {
|
||||
command_head,
|
||||
positional_arg_indices,
|
||||
arguments: &call.arguments,
|
||||
expr,
|
||||
},
|
||||
pos,
|
||||
&ctx,
|
||||
suggestions.is_empty(),
|
||||
)
|
||||
}
|
||||
_ => vec![],
|
||||
},
|
||||
);
|
||||
suggestions.extend(match arg {
|
||||
// flags
|
||||
Argument::Named(_) | Argument::Unknown(_)
|
||||
if prefix.starts_with(b"-") =>
|
||||
{
|
||||
flag_completion_helper()
|
||||
}
|
||||
// only when `strip` == false
|
||||
Argument::Positional(_) if prefix == b"-" => flag_completion_helper(),
|
||||
// complete according to expression type and command head
|
||||
Argument::Positional(expr) => {
|
||||
let command_head = working_set.get_decl(call.decl_id).name();
|
||||
positional_arg_indices.push(arg_idx);
|
||||
self.argument_completion_helper(
|
||||
PositionalArguments {
|
||||
command_head,
|
||||
positional_arg_indices,
|
||||
arguments: &call.arguments,
|
||||
expr,
|
||||
},
|
||||
pos,
|
||||
&ctx,
|
||||
suggestions.is_empty(),
|
||||
)
|
||||
}
|
||||
_ => vec![],
|
||||
});
|
||||
break;
|
||||
} else if !matches!(arg, Argument::Named(_)) {
|
||||
positional_arg_indices.push(arg_idx);
|
||||
@ -504,18 +462,10 @@ impl NuCompleter {
|
||||
if let Some(external_result) =
|
||||
self.external_completion(closure, &text_spans, offset, new_span)
|
||||
{
|
||||
// Prioritize external results over (sub)commands
|
||||
suggestions.splice(0..0, external_result);
|
||||
suggestions.extend(external_result);
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
// for external path arguments with spaces, please check issue #15790
|
||||
if suggestions.is_empty() {
|
||||
let (new_span, prefix) =
|
||||
strip_placeholder_if_any(working_set, &span, strip);
|
||||
let ctx = Context::new(working_set, new_span, prefix, offset);
|
||||
return self.process_completion(&mut FileCompletion, &ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -753,7 +703,7 @@ impl NuCompleter {
|
||||
Ok(value) => {
|
||||
log::error!(
|
||||
"External completer returned invalid value of type {}",
|
||||
value.get_type()
|
||||
value.get_type().to_string()
|
||||
);
|
||||
Some(vec![])
|
||||
}
|
||||
@ -892,7 +842,7 @@ mod completer_tests {
|
||||
for (line, has_result, begins_with, expected_values) in dataset {
|
||||
let result = completer.fetch_completions_at(line, line.len());
|
||||
// Test whether the result is empty or not
|
||||
assert_eq!(!result.is_empty(), has_result, "line: {line}");
|
||||
assert_eq!(!result.is_empty(), has_result, "line: {}", line);
|
||||
|
||||
// Test whether the result begins with the expected value
|
||||
result
|
||||
@ -907,7 +857,8 @@ mod completer_tests {
|
||||
.filter(|x| *x)
|
||||
.count(),
|
||||
expected_values.len(),
|
||||
"line: {line}"
|
||||
"line: {}",
|
||||
line
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use super::{MatchAlgorithm, completion_options::NuMatcher};
|
||||
use super::{completion_options::NuMatcher, MatchAlgorithm};
|
||||
use crate::completions::CompletionOptions;
|
||||
use nu_ansi_term::Style;
|
||||
use nu_engine::env_to_string;
|
||||
use nu_path::dots::expand_ndots;
|
||||
use nu_path::{expand_to_real_path, home_dir};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use nu_utils::get_ls_colors;
|
||||
use std::path::{Component, MAIN_SEPARATOR as SEP, Path, PathBuf, is_separator};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PathBuiltFromString {
|
||||
@ -39,10 +39,8 @@ fn complete_rec(
|
||||
isdir: bool,
|
||||
enable_exact_match: bool,
|
||||
) -> Vec<PathBuiltFromString> {
|
||||
let has_more = !partial.is_empty() && (partial.len() > 1 || isdir);
|
||||
|
||||
if let Some((&base, rest)) = partial.split_first() {
|
||||
if base.chars().all(|c| c == '.') && has_more {
|
||||
if base.chars().all(|c| c == '.') && (isdir || !rest.is_empty()) {
|
||||
let built_paths: Vec<_> = built_paths
|
||||
.iter()
|
||||
.map(|built| {
|
||||
@ -66,9 +64,6 @@ fn complete_rec(
|
||||
let prefix = partial.first().unwrap_or(&"");
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
|
||||
let mut exact_match = None;
|
||||
// Only relevant for case insensitive matching
|
||||
let mut multiple_exact_matches = false;
|
||||
for built in built_paths {
|
||||
let mut path = built.cwd.clone();
|
||||
for part in &built.parts {
|
||||
@ -88,56 +83,48 @@ fn complete_rec(
|
||||
built.isdir = entry_isdir && !entry.path().is_symlink();
|
||||
|
||||
if !want_directory || entry_isdir {
|
||||
if enable_exact_match && !multiple_exact_matches && has_more {
|
||||
let matches = if options.case_sensitive {
|
||||
entry_name.eq(prefix)
|
||||
} else {
|
||||
entry_name.eq_ignore_case(prefix)
|
||||
};
|
||||
if matches {
|
||||
if exact_match.is_none() {
|
||||
exact_match = Some(built.clone());
|
||||
} else {
|
||||
multiple_exact_matches = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matcher.add(entry_name, built);
|
||||
matcher.add(entry_name.clone(), (entry_name, built));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show longer completions if we have a single exact match (#13204, #14794)
|
||||
if !multiple_exact_matches {
|
||||
if let Some(built) = exact_match {
|
||||
return complete_rec(
|
||||
&partial[1..],
|
||||
&[built],
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
true,
|
||||
);
|
||||
let mut completions = vec![];
|
||||
for (entry_name, built) in matcher.results() {
|
||||
match partial.split_first() {
|
||||
Some((base, rest)) => {
|
||||
// We use `isdir` to confirm that the current component has
|
||||
// at least one next component or a slash.
|
||||
// Serves as confirmation to ignore longer completions for
|
||||
// components in between.
|
||||
if !rest.is_empty() || isdir {
|
||||
// Don't show longer completions if we have an exact match (#13204, #14794)
|
||||
let exact_match = enable_exact_match
|
||||
&& (if options.case_sensitive {
|
||||
entry_name.eq(base)
|
||||
} else {
|
||||
entry_name.eq_ignore_case(base)
|
||||
});
|
||||
completions.extend(complete_rec(
|
||||
rest,
|
||||
&[built],
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
exact_match,
|
||||
));
|
||||
if exact_match {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
completions.push(built);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
completions.push(built);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if has_more {
|
||||
let mut completions = vec![];
|
||||
for built in matcher.results() {
|
||||
completions.extend(complete_rec(
|
||||
&partial[1..],
|
||||
&[built],
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
false,
|
||||
));
|
||||
}
|
||||
completions
|
||||
} else {
|
||||
matcher.results()
|
||||
}
|
||||
completions
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -314,7 +301,7 @@ pub fn escape_path(path: String) -> String {
|
||||
if path.contains('\'') {
|
||||
// decide to use double quotes
|
||||
// Path as Debug will do the escaping for `"`, `\`
|
||||
format!("{path:?}")
|
||||
format!("{:?}", path)
|
||||
} else {
|
||||
format!("'{path}'")
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_parser::trim_quotes_str;
|
||||
use nu_protocol::{CompletionAlgorithm, CompletionSort};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use nucleo_matcher::{
|
||||
Config, Matcher, Utf32Str,
|
||||
pattern::{Atom, AtomKind, CaseMatching, Normalization},
|
||||
Config, Matcher, Utf32Str,
|
||||
};
|
||||
use std::{borrow::Cow, fmt::Display};
|
||||
|
||||
@ -102,11 +102,7 @@ impl<T> NuMatcher<'_, T> {
|
||||
options,
|
||||
needle: needle.to_owned(),
|
||||
state: State::Fuzzy {
|
||||
matcher: Matcher::new({
|
||||
let mut cfg = Config::DEFAULT;
|
||||
cfg.prefer_prefix = true;
|
||||
cfg
|
||||
}),
|
||||
matcher: Matcher::new(Config::DEFAULT),
|
||||
atom,
|
||||
items: Vec::new(),
|
||||
},
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::completions::{
|
||||
Completer, CompletionOptions, MatchAlgorithm, SemanticSuggestion,
|
||||
completer::map_value_completions,
|
||||
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
||||
SemanticSuggestion,
|
||||
};
|
||||
use nu_engine::eval_call;
|
||||
use nu_protocol::{
|
||||
DeclId, PipelineData, Span, Type, Value,
|
||||
ast::{Argument, Call, Expr, Expression},
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
DeclId, PipelineData, Span, Type, Value,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -106,9 +106,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
let positional =
|
||||
options.get("positional").and_then(|val| val.as_bool().ok());
|
||||
if positional.is_some() {
|
||||
log::warn!(
|
||||
"Use of the positional option is deprecated. Use the substring match algorithm instead."
|
||||
);
|
||||
log::warn!("Use of the positional option is deprecated. Use the substring match algorithm instead.");
|
||||
}
|
||||
if let Some(algorithm) = options
|
||||
.get("completion_algorithm")
|
||||
@ -140,7 +138,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
_ => {
|
||||
log::error!(
|
||||
"Custom completer returned invalid value of type {}",
|
||||
value.get_type()
|
||||
value.get_type().to_string()
|
||||
);
|
||||
return vec![];
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions,
|
||||
completion_common::{AdjustView, adjust_if_intermediate, complete_item},
|
||||
};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{SemanticSuggestion, SuggestionKind, completion_common::FileSuggestion};
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
||||
|
||||
pub struct DirectoryCompletion;
|
||||
|
||||
|
@ -1,18 +1,17 @@
|
||||
use crate::completions::{
|
||||
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
completion_common::{FileSuggestion, surround_remove},
|
||||
completion_common::{surround_remove, FileSuggestion},
|
||||
completion_options::NuMatcher,
|
||||
file_path_completion,
|
||||
file_path_completion, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_path::expand_tilde;
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{Stack, StateWorkingSet, VirtualPath},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
path::{MAIN_SEPARATOR_STR, PathBuf, is_separator},
|
||||
path::{is_separator, PathBuf, MAIN_SEPARATOR_STR},
|
||||
};
|
||||
|
||||
pub struct DotNuCompletion {
|
||||
@ -129,7 +128,7 @@ impl Completer for DotNuCompletion {
|
||||
.take_while(|c| "`'\"".contains(*c))
|
||||
.collect::<String>();
|
||||
for path in ["std", "std-rfc"] {
|
||||
let path = format!("{surround_prefix}{path}");
|
||||
let path = format!("{}{}", surround_prefix, path);
|
||||
matcher.add(
|
||||
path.clone(),
|
||||
FileSuggestion {
|
||||
@ -146,7 +145,7 @@ impl Completer for DotNuCompletion {
|
||||
for sub_vp_id in sub_paths {
|
||||
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
||||
let path = path
|
||||
.strip_prefix(&format!("{base_dir}/"))
|
||||
.strip_prefix(&format!("{}/", base_dir))
|
||||
.unwrap_or(path)
|
||||
.to_string();
|
||||
matcher.add(
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::completions::{
|
||||
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
completion_common::surround_remove, completion_options::NuMatcher,
|
||||
completion_common::surround_remove, completion_options::NuMatcher, Completer,
|
||||
CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
ModuleId, Span,
|
||||
engine::{Stack, StateWorkingSet},
|
||||
ModuleId, Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions,
|
||||
completion_common::{AdjustView, adjust_if_intermediate, complete_item},
|
||||
};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{SemanticSuggestion, SuggestionKind, completion_common::FileSuggestion};
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
||||
|
||||
pub struct FileCompletion;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::completions::{
|
||||
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind, completion_options::NuMatcher,
|
||||
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
DeclId, Span,
|
||||
engine::{Stack, StateWorkingSet},
|
||||
DeclId, Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub use custom_completions::CustomCompletion;
|
||||
pub use directory_completions::DirectoryCompletion;
|
||||
pub use dotnu_completions::DotNuCompletion;
|
||||
pub use exportable_completions::ExportableCompletion;
|
||||
pub use file_completions::{FileCompletion, file_path_completion};
|
||||
pub use file_completions::{file_path_completion, FileCompletion};
|
||||
pub use flag_completions::FlagCompletion;
|
||||
pub use operator_completions::OperatorCompletion;
|
||||
pub use variable_completions::VariableCompletion;
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::completions::{
|
||||
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind, completion_options::NuMatcher,
|
||||
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
ENV_VARIABLE_ID, Span, Type, Value,
|
||||
ast::{self, Comparison, Expr, Expression},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span, Type, Value, ENV_VARIABLE_ID,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use strum::{EnumMessage, IntoEnumIterator};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||
use nu_protocol::{
|
||||
Span, VarId,
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span, VarId,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
|
@ -2,11 +2,10 @@ use crate::util::eval_source;
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_path::canonicalize_with;
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_protocol::{ParseError, PluginRegistryFile, Spanned, engine::StateWorkingSet};
|
||||
use nu_protocol::{engine::StateWorkingSet, ParseError, PluginRegistryFile, Spanned};
|
||||
use nu_protocol::{
|
||||
PipelineData,
|
||||
engine::{EngineState, Stack},
|
||||
report_shell_error,
|
||||
report_shell_error, PipelineData,
|
||||
};
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_utils::perf;
|
||||
@ -19,7 +18,7 @@ const OLD_PLUGIN_FILE: &str = "plugin.nu";
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Spanned<String>>) {
|
||||
use nu_protocol::{ShellError, shell_error::io::IoError};
|
||||
use nu_protocol::{shell_error::io::IoError, ShellError};
|
||||
use std::path::Path;
|
||||
|
||||
let span = plugin_file.as_ref().map(|s| s.span);
|
||||
@ -80,7 +79,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
|
||||
report_shell_error(
|
||||
engine_state,
|
||||
&ShellError::Io(IoError::new_internal_with_path(
|
||||
err,
|
||||
err.kind(),
|
||||
"Could not open plugin registry file",
|
||||
nu_protocol::location!(),
|
||||
plugin_path,
|
||||
@ -231,8 +230,8 @@ pub fn eval_config_contents(
|
||||
#[cfg(feature = "plugin")]
|
||||
pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
||||
use nu_protocol::{
|
||||
PluginExample, PluginIdentity, PluginRegistryItem, PluginRegistryItemData, PluginSignature,
|
||||
ShellError, shell_error::io::IoError,
|
||||
shell_error::io::IoError, PluginExample, PluginIdentity, PluginRegistryItem,
|
||||
PluginRegistryItemData, PluginSignature, ShellError,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@ -278,7 +277,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
||||
&mut stack,
|
||||
&old_contents,
|
||||
&old_plugin_file_path.to_string_lossy(),
|
||||
PipelineData::empty(),
|
||||
PipelineData::Empty,
|
||||
false,
|
||||
) != 0
|
||||
{
|
||||
@ -323,7 +322,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
||||
if let Err(err) = std::fs::File::create(&new_plugin_file_path)
|
||||
.map_err(|err| {
|
||||
IoError::new_internal_with_path(
|
||||
err,
|
||||
err.kind(),
|
||||
"Could not create new plugin file",
|
||||
nu_protocol::location!(),
|
||||
new_plugin_file_path.clone(),
|
||||
|
@ -2,11 +2,10 @@ use log::info;
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
PipelineData, ShellError, Spanned, Value,
|
||||
cli_error::report_compile_error,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
report_error::report_compile_error,
|
||||
report_parse_error, report_parse_warning,
|
||||
report_parse_error, report_parse_warning, PipelineData, ShellError, Spanned, Value,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -4,12 +4,12 @@ use nu_engine::eval_block;
|
||||
use nu_parser::parse;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{
|
||||
PipelineData, ShellError, Span, Value,
|
||||
cli_error::report_compile_error,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
report_error::report_compile_error,
|
||||
report_parse_error, report_parse_warning,
|
||||
shell_error::io::*,
|
||||
PipelineData, ShellError, Span, Value,
|
||||
};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
@ -28,7 +28,7 @@ pub fn evaluate_file(
|
||||
|
||||
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
|
||||
IoError::new_internal_with_path(
|
||||
err.not_found_as(NotFound::File),
|
||||
err.kind().not_found_as(NotFound::File),
|
||||
"Could not access file",
|
||||
nu_protocol::location!(),
|
||||
PathBuf::from(&path),
|
||||
@ -47,7 +47,7 @@ pub fn evaluate_file(
|
||||
|
||||
let file = std::fs::read(&file_path).map_err(|err| {
|
||||
IoError::new_internal_with_path(
|
||||
err.not_found_as(NotFound::File),
|
||||
err.kind().not_found_as(NotFound::File),
|
||||
"Could not read file",
|
||||
nu_protocol::location!(),
|
||||
file_path.clone(),
|
||||
|
@ -18,7 +18,7 @@ mod validation;
|
||||
pub use commands::add_cli_context;
|
||||
pub use completions::{FileCompletion, NuCompleter, SemanticSuggestion, SuggestionKind};
|
||||
pub use config_files::eval_config_contents;
|
||||
pub use eval_cmds::{EvaluateCommandsOpts, evaluate_commands};
|
||||
pub use eval_cmds::{evaluate_commands, EvaluateCommandsOpts};
|
||||
pub use eval_file::evaluate_file;
|
||||
pub use menus::NuHelpCompleter;
|
||||
pub use nu_highlight::NuHighlight;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_engine::documentation::{FormatterValue, HelpStyle, get_flags_section};
|
||||
use nu_protocol::{Config, engine::EngineState, levenshtein_distance};
|
||||
use nu_engine::documentation::{get_flags_section, HelpStyle};
|
||||
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use reedline::{Completer, Suggestion};
|
||||
use std::{fmt::Write, sync::Arc};
|
||||
@ -66,11 +66,8 @@ impl NuHelpCompleter {
|
||||
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
||||
|
||||
if !sig.named.is_empty() {
|
||||
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| match v {
|
||||
FormatterValue::DefaultValue(value) => {
|
||||
value.to_parsable_string(", ", &self.config)
|
||||
}
|
||||
FormatterValue::CodeString(text) => text.to_string(),
|
||||
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| {
|
||||
v.to_parsable_string(", ", &self.config)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
use nu_engine::eval_block;
|
||||
use nu_protocol::{
|
||||
BlockId, IntoPipelineData, Span, Value,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack},
|
||||
BlockId, IntoPipelineData, Span, Value,
|
||||
};
|
||||
use reedline::{Completer, Suggestion, menu_functions::parse_selection_char};
|
||||
use reedline::{menu_functions::parse_selection_char, Completer, Suggestion};
|
||||
use std::sync::Arc;
|
||||
|
||||
const SELECTION_CHAR: char = '!';
|
||||
|
@ -3,8 +3,6 @@ use std::sync::Arc;
|
||||
use nu_engine::command_prelude::*;
|
||||
use reedline::{Highlighter, StyledText};
|
||||
|
||||
use crate::syntax_highlight::highlight_syntax;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NuHighlight;
|
||||
|
||||
@ -16,11 +14,6 @@ impl Command for NuHighlight {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("nu-highlight")
|
||||
.category(Category::Strings)
|
||||
.switch(
|
||||
"reject-garbage",
|
||||
"Return an error if invalid syntax (garbage) was encountered",
|
||||
Some('r'),
|
||||
)
|
||||
.input_output_types(vec![(Type::String, Type::String)])
|
||||
}
|
||||
|
||||
@ -39,33 +32,19 @@ impl Command for NuHighlight {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let reject_garbage = call.has_flag(engine_state, stack, "reject-garbage")?;
|
||||
let head = call.head;
|
||||
|
||||
let signals = engine_state.signals();
|
||||
|
||||
let engine_state = Arc::new(engine_state.clone());
|
||||
let stack = Arc::new(stack.clone());
|
||||
let highlighter = crate::NuHighlighter {
|
||||
engine_state: Arc::new(engine_state.clone()),
|
||||
stack: Arc::new(stack.clone()),
|
||||
};
|
||||
|
||||
input.map(
|
||||
move |x| match x.coerce_into_string() {
|
||||
Ok(line) => {
|
||||
let result = highlight_syntax(&engine_state, &stack, &line, line.len());
|
||||
|
||||
let highlights = match (reject_garbage, result.found_garbage) {
|
||||
(false, _) => result.text,
|
||||
(true, None) => result.text,
|
||||
(true, Some(span)) => {
|
||||
let error = ShellError::OutsideSpannedLabeledError {
|
||||
src: line,
|
||||
error: "encountered invalid syntax while highlighting".into(),
|
||||
msg: "invalid syntax".into(),
|
||||
span,
|
||||
};
|
||||
return Value::error(error, head);
|
||||
}
|
||||
};
|
||||
|
||||
let highlights = highlighter.highlight(&line, line.len());
|
||||
Value::string(highlights.render_simple(), head)
|
||||
}
|
||||
Err(err) => Value::error(err, head),
|
||||
|
@ -2,9 +2,8 @@ use crate::NushellPrompt;
|
||||
use log::{trace, warn};
|
||||
use nu_engine::ClosureEvalOnce;
|
||||
use nu_protocol::{
|
||||
Config, PipelineData, Value,
|
||||
engine::{EngineState, Stack},
|
||||
report_shell_error,
|
||||
report_shell_error, Config, PipelineData, Value,
|
||||
};
|
||||
use reedline::Prompt;
|
||||
|
||||
@ -61,7 +60,7 @@ fn get_prompt_string(
|
||||
.and_then(|v| match v {
|
||||
Value::Closure { val, .. } => {
|
||||
let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
|
||||
.run_with_input(PipelineData::empty());
|
||||
.run_with_input(PipelineData::Empty);
|
||||
|
||||
trace!(
|
||||
"get_prompt_string (block) {}:{}:{}",
|
||||
@ -76,7 +75,7 @@ fn get_prompt_string(
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
Value::String { .. } => Some(PipelineData::value(v.clone(), None)),
|
||||
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
||||
_ => None,
|
||||
})
|
||||
.and_then(|pipeline_data| {
|
||||
|
@ -1,20 +1,19 @@
|
||||
use crate::{NuHelpCompleter, menus::NuMenuCompleter};
|
||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use nu_ansi_term::Style;
|
||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
Config, EditBindings, FromValue, ParsedKeybinding, ParsedMenu, PipelineData, Record,
|
||||
ShellError, Span, Type, Value,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
extract_value,
|
||||
extract_value, Config, EditBindings, FromValue, ParsedKeybinding, ParsedMenu, PipelineData,
|
||||
Record, ShellError, Span, Type, Value,
|
||||
};
|
||||
use reedline::{
|
||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
ColumnarMenu, DescriptionMenu, DescriptionMode, EditCommand, IdeMenu, Keybindings, ListMenu,
|
||||
MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu, default_emacs_keybindings,
|
||||
default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||
MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -159,7 +158,7 @@ pub(crate) fn add_menus(
|
||||
engine_state.merge_delta(delta)?;
|
||||
|
||||
let mut temp_stack = Stack::new().collect_value();
|
||||
let input = PipelineData::empty();
|
||||
let input = PipelineData::Empty;
|
||||
menu_eval_results.push(eval_block::<WithoutDebug>(
|
||||
&engine_state,
|
||||
&mut temp_stack,
|
||||
@ -1047,10 +1046,6 @@ fn event_from_record(
|
||||
ReedlineEvent::ExecuteHostCommand(cmd.to_expanded_string("", config))
|
||||
}
|
||||
"openeditor" => ReedlineEvent::OpenEditor,
|
||||
"vichangemode" => {
|
||||
let mode = extract_value("mode", record, span)?;
|
||||
ReedlineEvent::ViChangeMode(mode.as_str()?.to_owned())
|
||||
}
|
||||
str => {
|
||||
return Err(ShellError::InvalidValue {
|
||||
valid: "a reedline event".into(),
|
||||
@ -1176,7 +1171,6 @@ fn edit_from_record(
|
||||
"cutfromlinestart" => EditCommand::CutFromLineStart,
|
||||
"cuttoend" => EditCommand::CutToEnd,
|
||||
"cuttolineend" => EditCommand::CutToLineEnd,
|
||||
"killline" => EditCommand::KillLine,
|
||||
"cutwordleft" => EditCommand::CutWordLeft,
|
||||
"cutbigwordleft" => EditCommand::CutBigWordLeft,
|
||||
"cutwordright" => EditCommand::CutWordRight,
|
||||
|
@ -6,12 +6,12 @@ use crate::prompt_update::{
|
||||
VSCODE_PRE_EXECUTION_MARKER,
|
||||
};
|
||||
use crate::{
|
||||
NuHighlighter, NuValidator, NushellPrompt,
|
||||
completions::NuCompleter,
|
||||
nu_highlight::NoOpHighlighter,
|
||||
prompt_update,
|
||||
reedline_config::{KeybindingsMode, add_menus, create_keybindings},
|
||||
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
|
||||
util::eval_source,
|
||||
NuHighlighter, NuValidator, NushellPrompt,
|
||||
};
|
||||
use crossterm::cursor::SetCursorStyle;
|
||||
use log::{error, trace, warn};
|
||||
@ -23,15 +23,14 @@ use nu_engine::env_to_strings;
|
||||
use nu_engine::exit::cleanup_exit;
|
||||
use nu_parser::{lex, parse, trim_quotes_str};
|
||||
use nu_protocol::shell_error::io::IoError;
|
||||
use nu_protocol::{BannerKind, shell_error};
|
||||
use nu_protocol::{
|
||||
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
||||
config::NuCursorShape,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
report_shell_error,
|
||||
report_shell_error, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
||||
Value,
|
||||
};
|
||||
use nu_utils::{
|
||||
filesystem::{PermissionResult, have_permission},
|
||||
filesystem::{have_permission, PermissionResult},
|
||||
perf,
|
||||
};
|
||||
use reedline::{
|
||||
@ -43,7 +42,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
env::temp_dir,
|
||||
io::{self, IsTerminal, Write},
|
||||
panic::{AssertUnwindSafe, catch_unwind},
|
||||
panic::{catch_unwind, AssertUnwindSafe},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
@ -145,8 +144,8 @@ pub fn evaluate_repl(
|
||||
|
||||
if load_std_lib.is_none() {
|
||||
match engine_state.get_config().show_banner {
|
||||
BannerKind::None => {}
|
||||
BannerKind::Short => {
|
||||
Value::Bool { val: false, .. } => {}
|
||||
Value::String { ref val, .. } if val == "short" => {
|
||||
eval_source(
|
||||
engine_state,
|
||||
&mut unique_stack,
|
||||
@ -156,7 +155,7 @@ pub fn evaluate_repl(
|
||||
false,
|
||||
);
|
||||
}
|
||||
BannerKind::Full => {
|
||||
_ => {
|
||||
eval_source(
|
||||
engine_state,
|
||||
&mut unique_stack,
|
||||
@ -239,7 +238,7 @@ fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
|
||||
|
||||
match byte {
|
||||
// Escape bytes below 0x20
|
||||
b if b < 0x20 => format!("\\x{byte:02X}").into_bytes(),
|
||||
b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
|
||||
// Escape semicolon as \x3B
|
||||
b';' => "\\x3B".to_string().into_bytes(),
|
||||
// Escape backslash as \\
|
||||
@ -325,19 +324,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||
perf!("reset signals", start_time, use_color);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Check all the environment variables they ask for
|
||||
// fire the "env_change" hook
|
||||
if let Err(error) = hook::eval_env_change_hook(
|
||||
&engine_state.get_config().hooks.env_change.clone(),
|
||||
engine_state,
|
||||
&mut stack,
|
||||
) {
|
||||
report_shell_error(engine_state, &error)
|
||||
}
|
||||
perf!("env-change hook", start_time, use_color);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Next, right before we start our prompt and take input from the user, fire the "pre_prompt" hook
|
||||
// Right before we start our prompt and take input from the user, fire the "pre_prompt" hook
|
||||
if let Err(err) = hook::eval_hooks(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
@ -349,6 +336,18 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||
}
|
||||
perf!("pre-prompt hook", start_time, use_color);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Next, check all the environment variables they ask for
|
||||
// fire the "env_change" hook
|
||||
if let Err(error) = hook::eval_env_change_hook(
|
||||
&engine_state.get_config().hooks.env_change.clone(),
|
||||
engine_state,
|
||||
&mut stack,
|
||||
) {
|
||||
report_shell_error(engine_state, &error)
|
||||
}
|
||||
perf!("env-change hook", start_time, use_color);
|
||||
|
||||
let engine_reference = Arc::new(engine_state.clone());
|
||||
let config = stack.get_config(engine_state);
|
||||
|
||||
@ -855,7 +854,7 @@ fn do_auto_cd(
|
||||
report_shell_error(
|
||||
engine_state,
|
||||
&ShellError::Io(IoError::new_with_additional_context(
|
||||
shell_error::io::ErrorKind::DirectoryNotFound,
|
||||
std::io::ErrorKind::NotFound,
|
||||
span,
|
||||
PathBuf::from(&path),
|
||||
"Cannot change directory",
|
||||
@ -869,7 +868,7 @@ fn do_auto_cd(
|
||||
report_shell_error(
|
||||
engine_state,
|
||||
&ShellError::Io(IoError::new_with_additional_context(
|
||||
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
|
||||
std::io::ErrorKind::PermissionDenied,
|
||||
span,
|
||||
PathBuf::from(path),
|
||||
"Cannot change directory",
|
||||
@ -1097,7 +1096,8 @@ fn run_shell_integration_osc633(
|
||||
// If we're in vscode, run their specific ansi escape sequence.
|
||||
// This is helpful for ctrl+g to change directories in the terminal.
|
||||
run_ansi_sequence(&format!(
|
||||
"{VSCODE_CWD_PROPERTY_MARKER_PREFIX}{path}{VSCODE_CWD_PROPERTY_MARKER_SUFFIX}"
|
||||
"{}{}{}",
|
||||
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
||||
));
|
||||
|
||||
perf!(
|
||||
@ -1113,7 +1113,10 @@ fn run_shell_integration_osc633(
|
||||
|
||||
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
||||
run_ansi_sequence(&format!(
|
||||
"{VSCODE_COMMANDLINE_MARKER_PREFIX}{replaced_cmd_text}{VSCODE_COMMANDLINE_MARKER_SUFFIX}"
|
||||
"{}{}{}",
|
||||
VSCODE_COMMANDLINE_MARKER_PREFIX,
|
||||
replaced_cmd_text,
|
||||
VSCODE_COMMANDLINE_MARKER_SUFFIX
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -1448,7 +1451,7 @@ fn are_session_ids_in_sync() {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_auto_cd {
|
||||
use super::{ReplOperation, do_auto_cd, escape_special_vscode_bytes, parse_operation};
|
||||
use super::{do_auto_cd, escape_special_vscode_bytes, parse_operation, ReplOperation};
|
||||
use nu_path::AbsolutePath;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use tempfile::tempdir;
|
||||
@ -1489,7 +1492,7 @@ mod test_auto_cd {
|
||||
// Parse the input. It must be an auto-cd operation.
|
||||
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
|
||||
let ReplOperation::AutoCd { cwd, target, span } = op else {
|
||||
panic!("'{input}' was not parsed into an auto-cd operation")
|
||||
panic!("'{}' was not parsed into an auto-cd operation", input)
|
||||
};
|
||||
|
||||
// Perform the auto-cd operation.
|
||||
|
@ -2,11 +2,11 @@ use log::trace;
|
||||
use nu_ansi_term::Style;
|
||||
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
||||
use nu_engine::env;
|
||||
use nu_parser::{FlatShape, flatten_block, parse};
|
||||
use nu_parser::{flatten_block, parse, FlatShape};
|
||||
use nu_protocol::{
|
||||
Span,
|
||||
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::{Highlighter, StyledText};
|
||||
use std::sync::Arc;
|
||||
@ -17,173 +17,147 @@ pub struct NuHighlighter {
|
||||
}
|
||||
|
||||
impl Highlighter for NuHighlighter {
|
||||
fn highlight(&self, line: &str, cursor: usize) -> StyledText {
|
||||
let result = highlight_syntax(&self.engine_state, &self.stack, line, cursor);
|
||||
result.text
|
||||
}
|
||||
}
|
||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||
trace!("highlighting: {}", line);
|
||||
|
||||
/// Result of a syntax highlight operation
|
||||
#[derive(Default)]
|
||||
pub(crate) struct HighlightResult {
|
||||
/// The highlighted text
|
||||
pub(crate) text: StyledText,
|
||||
/// The span of any garbage that was highlighted
|
||||
pub(crate) found_garbage: Option<Span>,
|
||||
}
|
||||
let config = self.stack.get_config(&self.engine_state);
|
||||
let highlight_resolved_externals = config.highlight_resolved_externals;
|
||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
||||
let (shapes, global_span_offset) = {
|
||||
let mut shapes = flatten_block(&working_set, &block);
|
||||
// Highlighting externals has a config point because of concerns that using which to resolve
|
||||
// externals may slow down things too much.
|
||||
if highlight_resolved_externals {
|
||||
for (span, shape) in shapes.iter_mut() {
|
||||
if *shape == FlatShape::External {
|
||||
let str_contents =
|
||||
working_set.get_span_contents(Span::new(span.start, span.end));
|
||||
|
||||
pub(crate) fn highlight_syntax(
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
line: &str,
|
||||
cursor: usize,
|
||||
) -> HighlightResult {
|
||||
trace!("highlighting: {}", line);
|
||||
|
||||
let config = stack.get_config(engine_state);
|
||||
let highlight_resolved_externals = config.highlight_resolved_externals;
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
||||
let (shapes, global_span_offset) = {
|
||||
let mut shapes = flatten_block(&working_set, &block);
|
||||
// Highlighting externals has a config point because of concerns that using which to resolve
|
||||
// externals may slow down things too much.
|
||||
if highlight_resolved_externals {
|
||||
for (span, shape) in shapes.iter_mut() {
|
||||
if *shape == FlatShape::External {
|
||||
let str_contents =
|
||||
working_set.get_span_contents(Span::new(span.start, span.end));
|
||||
|
||||
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
||||
let paths = env::path_str(engine_state, stack, *span).ok();
|
||||
let res = if let Ok(cwd) = engine_state.cwd(Some(stack)) {
|
||||
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
||||
} else {
|
||||
which::which_in_global(str_word, paths.as_ref())
|
||||
.ok()
|
||||
.and_then(|mut i| i.next())
|
||||
};
|
||||
if res.is_some() {
|
||||
*shape = FlatShape::ExternalResolved;
|
||||
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
||||
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
|
||||
#[allow(deprecated)]
|
||||
let res = if let Ok(cwd) =
|
||||
env::current_dir_str(&self.engine_state, &self.stack)
|
||||
{
|
||||
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
||||
} else {
|
||||
which::which_in_global(str_word, paths.as_ref())
|
||||
.ok()
|
||||
.and_then(|mut i| i.next())
|
||||
};
|
||||
if res.is_some() {
|
||||
*shape = FlatShape::ExternalResolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(shapes, engine_state.next_span_start())
|
||||
};
|
||||
|
||||
let mut result = HighlightResult::default();
|
||||
let mut last_seen_span = global_span_offset;
|
||||
|
||||
let global_cursor_offset = cursor + global_span_offset;
|
||||
let matching_brackets_pos = find_matching_brackets(
|
||||
line,
|
||||
&working_set,
|
||||
&block,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
);
|
||||
|
||||
for shape in &shapes {
|
||||
if shape.0.end <= last_seen_span
|
||||
|| last_seen_span < global_span_offset
|
||||
|| shape.0.start < global_span_offset
|
||||
{
|
||||
// We've already output something for this span
|
||||
// so just skip this one
|
||||
continue;
|
||||
}
|
||||
if shape.0.start > last_seen_span {
|
||||
let gap = line
|
||||
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
||||
.to_string();
|
||||
result.text.push((Style::new(), gap));
|
||||
}
|
||||
let next_token = line
|
||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||
.to_string();
|
||||
|
||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||
result
|
||||
.text
|
||||
.push((get_shape_color(shape.as_str(), &config), text));
|
||||
(shapes, self.engine_state.next_span_start())
|
||||
};
|
||||
|
||||
match shape.1 {
|
||||
FlatShape::Garbage => {
|
||||
result.found_garbage.get_or_insert_with(|| {
|
||||
Span::new(
|
||||
shape.0.start - global_span_offset,
|
||||
shape.0.end - global_span_offset,
|
||||
)
|
||||
});
|
||||
add_colored_token(&shape.1, next_token)
|
||||
let mut output = StyledText::default();
|
||||
let mut last_seen_span = global_span_offset;
|
||||
|
||||
let global_cursor_offset = _cursor + global_span_offset;
|
||||
let matching_brackets_pos = find_matching_brackets(
|
||||
line,
|
||||
&working_set,
|
||||
&block,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
);
|
||||
|
||||
for shape in &shapes {
|
||||
if shape.0.end <= last_seen_span
|
||||
|| last_seen_span < global_span_offset
|
||||
|| shape.0.start < global_span_offset
|
||||
{
|
||||
// We've already output something for this span
|
||||
// so just skip this one
|
||||
continue;
|
||||
}
|
||||
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::List
|
||||
| FlatShape::Table
|
||||
| FlatShape::Record
|
||||
| FlatShape::Block
|
||||
| FlatShape::Closure => {
|
||||
let span = shape.0;
|
||||
let shape = &shape.1;
|
||||
let spans = split_span_by_highlight_positions(
|
||||
line,
|
||||
span,
|
||||
&matching_brackets_pos,
|
||||
global_span_offset,
|
||||
);
|
||||
for (part, highlight) in spans {
|
||||
let start = part.start - span.start;
|
||||
let end = part.end - span.start;
|
||||
let text = next_token[start..end].to_string();
|
||||
let mut style = get_shape_color(shape.as_str(), &config);
|
||||
if highlight {
|
||||
style = get_matching_brackets_style(style, &config);
|
||||
if shape.0.start > last_seen_span {
|
||||
let gap = line
|
||||
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
||||
.to_string();
|
||||
output.push((Style::new(), gap));
|
||||
}
|
||||
let next_token = line
|
||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||
.to_string();
|
||||
|
||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||
output.push((get_shape_color(shape.as_str(), &config), text));
|
||||
};
|
||||
|
||||
match shape.1 {
|
||||
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::List
|
||||
| FlatShape::Table
|
||||
| FlatShape::Record
|
||||
| FlatShape::Block
|
||||
| FlatShape::Closure => {
|
||||
let span = shape.0;
|
||||
let shape = &shape.1;
|
||||
let spans = split_span_by_highlight_positions(
|
||||
line,
|
||||
span,
|
||||
&matching_brackets_pos,
|
||||
global_span_offset,
|
||||
);
|
||||
for (part, highlight) in spans {
|
||||
let start = part.start - span.start;
|
||||
let end = part.end - span.start;
|
||||
let text = next_token[start..end].to_string();
|
||||
let mut style = get_shape_color(shape.as_str(), &config);
|
||||
if highlight {
|
||||
style = get_matching_brackets_style(style, &config);
|
||||
}
|
||||
output.push((style, text));
|
||||
}
|
||||
result.text.push((style, text));
|
||||
}
|
||||
}
|
||||
|
||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||
add_colored_token(&shape.1, next_token)
|
||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||
add_colored_token(&shape.1, next_token)
|
||||
}
|
||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
||||
}
|
||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
||||
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
||||
last_seen_span = shape.0.end;
|
||||
}
|
||||
last_seen_span = shape.0.end;
|
||||
}
|
||||
|
||||
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
||||
if !remainder.is_empty() {
|
||||
result.text.push((Style::new(), remainder));
|
||||
}
|
||||
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
||||
if !remainder.is_empty() {
|
||||
output.push((Style::new(), remainder));
|
||||
}
|
||||
|
||||
result
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
fn split_span_by_highlight_positions(
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
use nu_cmd_base::hook::eval_hook;
|
||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||
use nu_parser::{Token, TokenContents, lex, parse, unescape_unquote_string};
|
||||
use nu_parser::{lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||
use nu_protocol::{
|
||||
PipelineData, ShellError, Span, Value,
|
||||
cli_error::report_compile_error,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
report_error::report_compile_error,
|
||||
report_parse_error, report_parse_warning, report_shell_error,
|
||||
report_parse_error, report_parse_warning, report_shell_error, PipelineData, ShellError, Span,
|
||||
Value,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use nu_utils::enable_vt_processing;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
ParseError,
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
ParseError,
|
||||
};
|
||||
use reedline::{ValidationResult, Validator};
|
||||
use std::sync::Arc;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_protocol::HistoryFileFormat;
|
||||
use nu_test_support::{Outcome, nu};
|
||||
use nu_test_support::{nu, Outcome};
|
||||
use reedline::{
|
||||
FileBackedHistory, History, HistoryItem, HistoryItemId, ReedlineError, SearchQuery,
|
||||
SqliteBackedHistory,
|
||||
|
@ -5,9 +5,3 @@ fn nu_highlight_not_expr() {
|
||||
let actual = nu!("'not false' | nu-highlight | ansi strip");
|
||||
assert_eq!(actual.out, "not false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nu_highlight_where_row_condition() {
|
||||
let actual = nu!("'ls | where a b 12345(' | nu-highlight | ansi strip");
|
||||
assert_eq!(actual.out, "ls | where a b 12345(");
|
||||
}
|
||||
|
@ -1,24 +1,22 @@
|
||||
pub mod support;
|
||||
|
||||
use std::{
|
||||
fs::{FileType, ReadDir, read_dir},
|
||||
path::{MAIN_SEPARATOR, PathBuf},
|
||||
fs::{read_dir, FileType, ReadDir},
|
||||
path::{PathBuf, MAIN_SEPARATOR},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use nu_cli::NuCompleter;
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::parse;
|
||||
use nu_path::{AbsolutePathBuf, expand_tilde};
|
||||
use nu_protocol::{Config, PipelineData, debugger::WithoutDebug, engine::StateWorkingSet};
|
||||
use nu_path::expand_tilde;
|
||||
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, Config, PipelineData};
|
||||
use nu_std::load_standard_library;
|
||||
use nu_test_support::fs;
|
||||
use reedline::{Completer, Suggestion};
|
||||
use rstest::{fixture, rstest};
|
||||
use support::{
|
||||
completions_helpers::{
|
||||
new_dotnu_engine, new_engine_helper, new_external_engine, new_partial_engine,
|
||||
new_quote_engine,
|
||||
new_dotnu_engine, new_external_engine, new_partial_engine, new_quote_engine,
|
||||
},
|
||||
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
||||
};
|
||||
@ -97,11 +95,8 @@ fn extern_completer() -> NuCompleter {
|
||||
// Add record value as example
|
||||
let record = r#"
|
||||
def animals [] { [ "cat", "dog", "eel" ] }
|
||||
def fruits [] { [ "apple", "banana" ] }
|
||||
extern spam [
|
||||
animal: string@animals
|
||||
fruit?: string@fruits
|
||||
...rest: string@animals
|
||||
--foo (-f): string@animals
|
||||
-b: string@animals
|
||||
]
|
||||
@ -128,7 +123,7 @@ fn custom_completer_with_options(
|
||||
global_opts,
|
||||
completions
|
||||
.iter()
|
||||
.map(|comp| format!("'{comp}'"))
|
||||
.map(|comp| format!("'{}'", comp))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
completer_opts,
|
||||
@ -312,10 +307,10 @@ fn custom_arguments_and_subcommands() {
|
||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||
// including both subcommand and directory completions
|
||||
let expected = [
|
||||
"foo test bar".into(),
|
||||
folder("test_a"),
|
||||
file("test_a_symlink"),
|
||||
folder("test_b"),
|
||||
"foo test bar".into(),
|
||||
];
|
||||
match_suggestions_by_string(&expected, &suggestions);
|
||||
}
|
||||
@ -333,7 +328,7 @@ fn custom_flags_and_subcommands() {
|
||||
let completion_str = "foo --test";
|
||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||
// including both flag and directory completions
|
||||
let expected: Vec<_> = vec!["--test", "foo --test bar"];
|
||||
let expected: Vec<_> = vec!["foo --test bar", "--test"];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
@ -721,16 +716,6 @@ fn external_completer_fallback() {
|
||||
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
||||
let suggestions = run_external_completion(block, input);
|
||||
match_suggestions_by_string(&expected, &suggestions);
|
||||
|
||||
// issue #15790
|
||||
let input = "foo `dir with space/`";
|
||||
let expected = vec!["`dir with space/bar baz`", "`dir with space/foo`"];
|
||||
let suggestions = run_external_completion_within_pwd(
|
||||
block,
|
||||
input,
|
||||
fs::fixtures().join("external_completions"),
|
||||
);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
/// Fallback to external completions for flags of `sudo`
|
||||
@ -1483,12 +1468,11 @@ fn command_watch_with_filecompletion() {
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subcommand_vs_external_completer() {
|
||||
#[rstest]
|
||||
fn subcommand_completions() {
|
||||
let (_, _, mut engine, mut stack) = new_engine();
|
||||
let commands = r#"
|
||||
$env.config.completions.algorithm = "fuzzy"
|
||||
$env.config.completions.external.completer = {|spans| ["external"]}
|
||||
def foo-test-command [] {}
|
||||
def "foo-test-command bar" [] {}
|
||||
def "foo-test-command aagap bcr" [] {}
|
||||
@ -1501,7 +1485,6 @@ fn subcommand_vs_external_completer() {
|
||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||
match_suggestions(
|
||||
&vec![
|
||||
"external",
|
||||
"food bar",
|
||||
"foo-test-command bar",
|
||||
"foo-test-command aagap bcr",
|
||||
@ -1511,7 +1494,7 @@ fn subcommand_vs_external_completer() {
|
||||
|
||||
let prefix = "foot bar";
|
||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||
match_suggestions(&vec!["external", "foo-test-command bar"], &suggestions);
|
||||
match_suggestions(&vec!["foo-test-command bar"], &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1563,7 +1546,9 @@ fn flag_completions() {
|
||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||
// Test completions for the 'ls' flags
|
||||
let suggestions = completer.complete("ls -", 4);
|
||||
|
||||
assert_eq!(18, suggestions.len());
|
||||
|
||||
let expected: Vec<_> = vec![
|
||||
"--all",
|
||||
"--directory",
|
||||
@ -1584,12 +1569,9 @@ fn flag_completions() {
|
||||
"-s",
|
||||
"-t",
|
||||
];
|
||||
|
||||
// Match results
|
||||
match_suggestions(&expected, &suggestions);
|
||||
|
||||
// https://github.com/nushell/nushell/issues/16375
|
||||
let suggestions = completer.complete("table -", 7);
|
||||
assert_eq!(20, suggestions.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1597,25 +1579,16 @@ fn attribute_completions() {
|
||||
// Create a new engine
|
||||
let (_, _, engine, stack) = new_engine();
|
||||
|
||||
// Compile a list of built-in attribute names (without the "attr " prefix)
|
||||
let attribute_names: Vec<String> = engine
|
||||
.get_signatures_and_declids(false)
|
||||
.into_iter()
|
||||
.map(|(sig, _)| sig.name)
|
||||
.filter(|name| name.starts_with("attr "))
|
||||
.map(|name| name[5..].to_string())
|
||||
.collect();
|
||||
|
||||
// Make sure we actually found some attributes so the test is valid
|
||||
assert!(attribute_names.contains(&String::from("example")));
|
||||
|
||||
// Instantiate a new completer
|
||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||
// Test completions for the 'ls' flags
|
||||
let suggestions = completer.complete("@", 1);
|
||||
|
||||
// Only checking for the builtins and not the std attributes
|
||||
let expected: Vec<_> = vec!["category", "example", "search-terms"];
|
||||
|
||||
// Match results
|
||||
match_suggestions_by_string(&attribute_names, &suggestions);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2121,15 +2094,11 @@ fn alias_of_another_alias() {
|
||||
match_suggestions(&expected_paths, &suggestions)
|
||||
}
|
||||
|
||||
fn run_external_completion_within_pwd(
|
||||
completer: &str,
|
||||
input: &str,
|
||||
pwd: AbsolutePathBuf,
|
||||
) -> 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
|
||||
let (_, _, mut engine_state, mut stack) = new_engine_helper(pwd);
|
||||
let (_, _, mut engine_state, mut stack) = new_engine();
|
||||
let (block, delta) = {
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
||||
@ -2141,8 +2110,7 @@ fn run_external_completion_within_pwd(
|
||||
assert!(engine_state.merge_delta(delta).is_ok());
|
||||
|
||||
assert!(
|
||||
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::empty())
|
||||
.is_ok()
|
||||
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::Empty).is_ok()
|
||||
);
|
||||
|
||||
// Merge environment into the permanent state
|
||||
@ -2154,10 +2122,6 @@ fn run_external_completion_within_pwd(
|
||||
completer.complete(input, input.len())
|
||||
}
|
||||
|
||||
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||
run_external_completion_within_pwd(completer, input, fs::fixtures().join("completions"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_command_completion() {
|
||||
let (_, _, engine, stack) = new_engine();
|
||||
@ -2266,22 +2230,6 @@ fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn extern_custom_completion_optional(mut extern_completer: NuCompleter) {
|
||||
let suggestions = extern_completer.complete("spam foo -f bar ", 16);
|
||||
let expected: Vec<_> = vec!["apple", "banana"];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn extern_custom_completion_rest(mut extern_completer: NuCompleter) {
|
||||
let suggestions = extern_completer.complete("spam foo -f bar baz ", 20);
|
||||
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
let suggestions = extern_completer.complete("spam foo -f bar baz qux ", 24);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
||||
let suggestions = extern_completer.complete("spam --foo=", 11);
|
||||
@ -2310,17 +2258,6 @@ fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
/// When we're completing the flag name itself, not its value,
|
||||
/// custom completions should not be used
|
||||
#[rstest]
|
||||
fn custom_completion_flag_name_not_value(mut extern_completer: NuCompleter) {
|
||||
let suggestions = extern_completer.complete("spam --f", 8);
|
||||
match_suggestions(&vec!["--foo"], &suggestions);
|
||||
// Also test with partial short flag
|
||||
let suggestions = extern_completer.complete("spam -f", 7);
|
||||
match_suggestions(&vec!["-f"], &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
||||
let suggestions = extern_completer.complete("spam -", 6);
|
||||
@ -2409,32 +2346,6 @@ fn exact_match() {
|
||||
assert!(suggestions.len() > 1);
|
||||
}
|
||||
|
||||
#[cfg(all(not(windows), not(target_os = "macos")))]
|
||||
#[test]
|
||||
fn exact_match_case_insensitive() {
|
||||
use nu_test_support::playground::Playground;
|
||||
use support::completions_helpers::new_engine_helper;
|
||||
|
||||
Playground::setup("exact_match_case_insensitive", |dirs, playground| {
|
||||
playground.mkdir("AA/foo");
|
||||
playground.mkdir("aa/foo");
|
||||
playground.mkdir("aaa/foo");
|
||||
|
||||
let (dir, _, engine, stack) = new_engine_helper(dirs.test().into());
|
||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||
|
||||
let target = format!("open {}", folder(dir.join("aa")));
|
||||
match_suggestions(
|
||||
&vec![
|
||||
folder(dir.join("AA").join("foo")).as_str(),
|
||||
folder(dir.join("aa").join("foo")).as_str(),
|
||||
folder(dir.join("aaa").join("foo")).as_str(),
|
||||
],
|
||||
&completer.complete(&target, target.len()),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[ignore = "was reverted, still needs fixing"]
|
||||
#[rstest]
|
||||
fn alias_offset_bug_7648() {
|
||||
|
@ -2,9 +2,9 @@ use nu_engine::eval_block;
|
||||
use nu_parser::parse;
|
||||
use nu_path::{AbsolutePathBuf, PathBuf};
|
||||
use nu_protocol::{
|
||||
PipelineData, ShellError, Span, Value,
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
PipelineData, ShellError, Span, Value,
|
||||
};
|
||||
use nu_test_support::fs;
|
||||
use reedline::Suggestion;
|
||||
@ -14,8 +14,11 @@ fn create_default_context() -> EngineState {
|
||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
||||
}
|
||||
|
||||
pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
let pwd_str = pwd
|
||||
/// creates a new engine with the current path into the completions fixtures folder
|
||||
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("completions");
|
||||
let dir_str = dir
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
@ -33,13 +36,13 @@ pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, Engi
|
||||
// Add pwd as env var
|
||||
stack.add_env_var(
|
||||
"PWD".to_string(),
|
||||
Value::string(pwd_str.clone(), nu_protocol::Span::new(0, pwd_str.len())),
|
||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
||||
);
|
||||
stack.add_env_var(
|
||||
"TEST".to_string(),
|
||||
Value::string(
|
||||
"NUSHELL".to_string(),
|
||||
nu_protocol::Span::new(0, pwd_str.len()),
|
||||
nu_protocol::Span::new(0, dir_str.len()),
|
||||
),
|
||||
);
|
||||
#[cfg(windows)]
|
||||
@ -47,7 +50,7 @@ pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, Engi
|
||||
"Path".to_string(),
|
||||
Value::string(
|
||||
"c:\\some\\path;c:\\some\\other\\path".to_string(),
|
||||
nu_protocol::Span::new(0, pwd_str.len()),
|
||||
nu_protocol::Span::new(0, dir_str.len()),
|
||||
),
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
@ -55,7 +58,7 @@ pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, Engi
|
||||
"PATH".to_string(),
|
||||
Value::string(
|
||||
"/some/path:/some/other/path".to_string(),
|
||||
nu_protocol::Span::new(0, pwd_str.len()),
|
||||
nu_protocol::Span::new(0, dir_str.len()),
|
||||
),
|
||||
);
|
||||
|
||||
@ -63,12 +66,7 @@ pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, Engi
|
||||
let merge_result = engine_state.merge_env(&mut stack);
|
||||
assert!(merge_result.is_ok());
|
||||
|
||||
(pwd, pwd_str, engine_state, stack)
|
||||
}
|
||||
|
||||
/// creates a new engine with the current path in the completions fixtures folder
|
||||
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
new_engine_helper(fs::fixtures().join("completions"))
|
||||
(dir, dir_str, engine_state, stack)
|
||||
}
|
||||
|
||||
/// Adds pseudo PATH env for external completion tests
|
||||
@ -90,13 +88,23 @@ pub fn new_external_engine() -> EngineState {
|
||||
engine
|
||||
}
|
||||
|
||||
/// creates a new engine with the current path in the dotnu_completions fixtures folder
|
||||
/// creates a new engine with the current path into the completions fixtures folder
|
||||
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("dotnu_completions");
|
||||
let (dir, dir_str, mut engine_state, mut stack) = new_engine_helper(dir);
|
||||
let dir_str = dir
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap_or_default();
|
||||
let dir_span = nu_protocol::Span::new(0, dir_str.len());
|
||||
|
||||
// Create a new engine with default context
|
||||
let mut engine_state = create_default_context();
|
||||
|
||||
// Add $nu
|
||||
engine_state.generate_nu_constant();
|
||||
|
||||
// const $NU_LIB_DIRS
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let var_id = working_set.add_variable(
|
||||
@ -114,6 +122,15 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
);
|
||||
let _ = engine_state.merge_delta(working_set.render());
|
||||
|
||||
// New stack
|
||||
let mut stack = Stack::new();
|
||||
|
||||
// Add pwd as env var
|
||||
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
|
||||
stack.add_env_var(
|
||||
"TEST".to_string(),
|
||||
Value::string("NUSHELL".to_string(), dir_span),
|
||||
);
|
||||
stack.add_env_var(
|
||||
"NU_LIB_DIRS".into(),
|
||||
Value::test_list(vec![
|
||||
@ -130,11 +147,73 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
}
|
||||
|
||||
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
new_engine_helper(fs::fixtures().join("quoted_completions"))
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("quoted_completions");
|
||||
let dir_str = dir
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Create a new engine with default context
|
||||
let mut engine_state = create_default_context();
|
||||
|
||||
// New stack
|
||||
let mut stack = Stack::new();
|
||||
|
||||
// Add pwd as env var
|
||||
stack.add_env_var(
|
||||
"PWD".to_string(),
|
||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
||||
);
|
||||
stack.add_env_var(
|
||||
"TEST".to_string(),
|
||||
Value::string(
|
||||
"NUSHELL".to_string(),
|
||||
nu_protocol::Span::new(0, dir_str.len()),
|
||||
),
|
||||
);
|
||||
|
||||
// Merge environment into the permanent state
|
||||
let merge_result = engine_state.merge_env(&mut stack);
|
||||
assert!(merge_result.is_ok());
|
||||
|
||||
(dir, dir_str, engine_state, stack)
|
||||
}
|
||||
|
||||
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
new_engine_helper(fs::fixtures().join("partial_completions"))
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("partial_completions");
|
||||
let dir_str = dir
|
||||
.clone()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap_or_default();
|
||||
|
||||
// Create a new engine with default context
|
||||
let mut engine_state = create_default_context();
|
||||
|
||||
// New stack
|
||||
let mut stack = Stack::new();
|
||||
|
||||
// Add pwd as env var
|
||||
stack.add_env_var(
|
||||
"PWD".to_string(),
|
||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
||||
);
|
||||
stack.add_env_var(
|
||||
"TEST".to_string(),
|
||||
Value::string(
|
||||
"NUSHELL".to_string(),
|
||||
nu_protocol::Span::new(0, dir_str.len()),
|
||||
),
|
||||
);
|
||||
|
||||
// Merge environment into the permanent state
|
||||
let merge_result = engine_state.merge_env(&mut stack);
|
||||
assert!(merge_result.is_ok());
|
||||
|
||||
(dir, dir_str, engine_state, stack)
|
||||
}
|
||||
|
||||
/// match a list of suggestions with the expected values
|
||||
@ -194,15 +273,13 @@ pub fn merge_input(
|
||||
|
||||
engine_state.merge_delta(delta)?;
|
||||
|
||||
assert!(
|
||||
eval_block::<WithoutDebug>(
|
||||
engine_state,
|
||||
stack,
|
||||
&block,
|
||||
PipelineData::value(Value::nothing(Span::unknown()), None),
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(eval_block::<WithoutDebug>(
|
||||
engine_state,
|
||||
stack,
|
||||
&block,
|
||||
PipelineData::Value(Value::nothing(Span::unknown()), None),
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
// Merge environment into the permanent state
|
||||
engine_state.merge_env(stack)
|
||||
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
authors = ["The Nushell Project Developers"]
|
||||
description = "The foundation tools to build Nushell commands."
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-base"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||
version = "0.106.2"
|
||||
version = "0.104.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -13,12 +13,12 @@ version = "0.106.2"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.106.2" }
|
||||
nu-path = { path = "../nu-path", version = "0.106.2" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.104.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
|
||||
indexmap = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
[dev-dependencies]
|
@ -1,4 +1,4 @@
|
||||
use indexmap::{IndexSet, indexset};
|
||||
use indexmap::{indexset, IndexSet};
|
||||
use nu_protocol::Value;
|
||||
|
||||
pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use miette::Result;
|
||||
use nu_engine::{eval_block, eval_block_with_early_return, redirect_env};
|
||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::{
|
||||
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
||||
cli_error::{report_parse_error, report_shell_error},
|
||||
debugger::WithoutDebug,
|
||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||
report_error::{report_parse_error, report_shell_error},
|
||||
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
@ -325,7 +325,19 @@ fn run_hook(
|
||||
}
|
||||
|
||||
// If all went fine, preserve the environment of the called block
|
||||
redirect_env(engine_state, stack, &callee_stack);
|
||||
let caller_env_vars = stack.get_env_var_names(engine_state);
|
||||
|
||||
// remove env vars that are present in the caller but not in the callee
|
||||
// (the callee hid them)
|
||||
for var in caller_env_vars.iter() {
|
||||
if !callee_stack.has_env_var(engine_state, var) {
|
||||
stack.remove_env_var(engine_state, var);
|
||||
}
|
||||
}
|
||||
|
||||
// add new env vars from callee to caller
|
||||
for (var, value) in callee_stack.get_stack_env_vars() {
|
||||
stack.add_env_var(var, value);
|
||||
}
|
||||
Ok(pipeline_data)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nu_protocol::{PipelineData, ShellError, Signals, Span, Value, ast::CellPath};
|
||||
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait CmdArgument {
|
||||
|
@ -3,6 +3,3 @@ pub mod formats;
|
||||
pub mod hook;
|
||||
pub mod input_handler;
|
||||
pub mod util;
|
||||
mod wrap_call;
|
||||
|
||||
pub use wrap_call::*;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_protocol::{
|
||||
Range, ShellError, Span, Value,
|
||||
engine::{EngineState, Stack},
|
||||
Range, ShellError, Span, Value,
|
||||
};
|
||||
use std::ops::Bound;
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
DeclId, FromValue, ShellError, Span,
|
||||
engine::{Call, EngineState, Stack, StateWorkingSet},
|
||||
};
|
||||
|
||||
/// A helper utility to aid in implementing commands which have the same behavior for `run` and `run_const`.
|
||||
///
|
||||
/// Only supports functions in [`Call`] and [`CallExt`] which have a `const` suffix.
|
||||
///
|
||||
/// To use, the actual command logic should be moved to a function. Then, `eval` and `eval_const` can be implemented like this:
|
||||
/// ```rust
|
||||
/// # use nu_engine::command_prelude::*;
|
||||
/// # use nu_cmd_base::WrapCall;
|
||||
/// # fn do_command_logic(call: WrapCall) -> Result<PipelineData, ShellError> { Ok(PipelineData::empty()) }
|
||||
///
|
||||
/// # struct Command {}
|
||||
/// # impl Command {
|
||||
/// fn run(&self, engine_state: &EngineState, stack: &mut Stack, call: &Call) -> Result<PipelineData, ShellError> {
|
||||
/// let call = WrapCall::Eval(engine_state, stack, call);
|
||||
/// do_command_logic(call)
|
||||
/// }
|
||||
///
|
||||
/// fn run_const(&self, working_set: &StateWorkingSet, call: &Call) -> Result<PipelineData, ShellError> {
|
||||
/// let call = WrapCall::ConstEval(working_set, call);
|
||||
/// do_command_logic(call)
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Then, the typical [`Call`] and [`CallExt`] operations can be called using destructuring:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_engine::command_prelude::*;
|
||||
/// # use nu_cmd_base::WrapCall;
|
||||
/// # let call = WrapCall::Eval(&EngineState::new(), &mut Stack::new(), &Call::new(Span::unknown()));
|
||||
/// # fn do_command_logic(call: WrapCall) -> Result<(), ShellError> {
|
||||
/// let (call, required): (_, String) = call.req(0)?;
|
||||
/// let (call, flag): (_, Option<i64>) = call.get_flag("number")?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// A new `WrapCall` instance has to be returned after each function to ensure
|
||||
/// that there is only ever one copy of mutable [`Stack`] reference.
|
||||
pub enum WrapCall<'a> {
|
||||
Eval(&'a EngineState, &'a mut Stack, &'a Call<'a>),
|
||||
ConstEval(&'a StateWorkingSet<'a>, &'a Call<'a>),
|
||||
}
|
||||
|
||||
/// Macro to choose between the non-const and const versions of each [`Call`]/[`CallExt`] function
|
||||
macro_rules! proxy {
|
||||
($self:ident , $eval:ident , $const:ident , $( $args:expr ),*) => {
|
||||
match $self {
|
||||
WrapCall::Eval(engine_state, stack, call) => {
|
||||
Call::$eval(call, engine_state, stack, $( $args ),*)
|
||||
.map(|val| (WrapCall::Eval(engine_state, stack, call), val))
|
||||
},
|
||||
WrapCall::ConstEval(working_set, call) => {
|
||||
Call::$const(call, working_set, $( $args ),*)
|
||||
.map(|val| (WrapCall::ConstEval(working_set, call), val))
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl WrapCall<'_> {
|
||||
pub fn head(&self) -> Span {
|
||||
match self {
|
||||
WrapCall::Eval(_, _, call) => call.head,
|
||||
WrapCall::ConstEval(_, call) => call.head,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl_id(&self) -> DeclId {
|
||||
match self {
|
||||
WrapCall::Eval(_, _, call) => call.decl_id,
|
||||
WrapCall::ConstEval(_, call) => call.decl_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_flag<T: FromValue>(self, flag_name: &str) -> Result<(Self, bool), ShellError> {
|
||||
proxy!(self, has_flag, has_flag_const, flag_name)
|
||||
}
|
||||
|
||||
pub fn get_flag<T: FromValue>(self, name: &str) -> Result<(Self, Option<T>), ShellError> {
|
||||
proxy!(self, get_flag, get_flag_const, name)
|
||||
}
|
||||
|
||||
pub fn req<T: FromValue>(self, pos: usize) -> Result<(Self, T), ShellError> {
|
||||
proxy!(self, req, req_const, pos)
|
||||
}
|
||||
|
||||
pub fn rest<T: FromValue>(self, pos: usize) -> Result<(Self, Vec<T>), ShellError> {
|
||||
proxy!(self, rest, rest_const, pos)
|
||||
}
|
||||
|
||||
pub fn opt<T: FromValue>(self, pos: usize) -> Result<(Self, Option<T>), ShellError> {
|
||||
proxy!(self, opt, opt_const, pos)
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
[package]
|
||||
authors = ["The Nushell Project Developers"]
|
||||
description = "Nushell's extra commands that are not part of the 1.0 api standard."
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-extra"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||
version = "0.106.2"
|
||||
version = "0.104.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -16,13 +16,13 @@ bench = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
|
||||
nu-json = { version = "0.106.2", path = "../nu-json" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.106.2" }
|
||||
nu-pretty-hex = { version = "0.106.2", path = "../nu-pretty-hex" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.106.2", default-features = false }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.104.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-json = { version = "0.104.1", path = "../nu-json" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-pretty-hex = { version = "0.104.1", path = "../nu-pretty-hex" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1", default-features = false }
|
||||
|
||||
# Potential dependencies for extras
|
||||
heck = { workspace = true }
|
||||
@ -37,6 +37,6 @@ itertools = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.2" }
|
||||
nu-command = { path = "../nu-command", version = "0.106.2" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.106.2" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.104.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.104.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.104.1" }
|
||||
|
@ -3,12 +3,7 @@ use nu_protocol::engine::Command;
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_examples(cmd: impl Command + 'static) {
|
||||
test_examples::test_examples(cmd, &[]);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_examples_with_commands(cmd: impl Command + 'static, commands: &[&dyn Command]) {
|
||||
test_examples::test_examples(cmd, commands);
|
||||
test_examples::test_examples(cmd);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -21,15 +16,15 @@ mod test_examples {
|
||||
};
|
||||
|
||||
use nu_protocol::{
|
||||
Type,
|
||||
engine::{Command, EngineState, StateWorkingSet},
|
||||
Type,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn test_examples(cmd: impl Command + 'static, commands: &[&dyn Command]) {
|
||||
pub fn test_examples(cmd: impl Command + 'static) {
|
||||
let examples = cmd.examples();
|
||||
let signature = cmd.signature();
|
||||
let mut engine_state = make_engine_state(cmd.clone_box(), commands);
|
||||
let mut engine_state = make_engine_state(cmd.clone_box());
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
|
||||
@ -43,7 +38,7 @@ mod test_examples {
|
||||
check_example_input_and_output_types_match_command_signature(
|
||||
&example,
|
||||
&cwd,
|
||||
&mut make_engine_state(cmd.clone_box(), commands),
|
||||
&mut make_engine_state(cmd.clone_box()),
|
||||
&signature.input_output_types,
|
||||
signature.operates_on_cell_paths(),
|
||||
),
|
||||
@ -62,7 +57,7 @@ mod test_examples {
|
||||
);
|
||||
}
|
||||
|
||||
fn make_engine_state(cmd: Box<dyn Command>, commands: &[&dyn Command]) -> Box<EngineState> {
|
||||
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
|
||||
let mut engine_state = Box::new(EngineState::new());
|
||||
|
||||
let delta = {
|
||||
@ -74,10 +69,6 @@ mod test_examples {
|
||||
working_set.add_decl(Box::new(nu_cmd_lang::If));
|
||||
working_set.add_decl(Box::new(nu_command::MathRound));
|
||||
|
||||
for command in commands {
|
||||
working_set.add_decl(command.clone_box());
|
||||
}
|
||||
|
||||
// Adding the command that is being tested to the working set
|
||||
working_set.add_decl(cmd);
|
||||
working_set.render()
|
||||
|
@ -65,7 +65,7 @@ impl Command for BitsAnd {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Endian must be one of native, little, big".to_string(),
|
||||
span: endian.span,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -113,7 +113,8 @@ impl Command for BitsAnd {
|
||||
])),
|
||||
},
|
||||
Example {
|
||||
description: "Apply bitwise and to binary data of varying lengths with specified endianness",
|
||||
description:
|
||||
"Apply bitwise and to binary data of varying lengths with specified endianness",
|
||||
example: "0x[c0 ff ee] | bits and 0x[ff] --endian big",
|
||||
result: Some(Value::test_binary(vec![0x00, 0x00, 0xee])),
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{NumberBytes, get_number_bytes};
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use super::{get_number_bytes, NumberBytes};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -100,7 +100,8 @@ impl Command for BitsNot {
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
||||
description:
|
||||
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
||||
example: "[4 3 2] | bits not --number-bytes 2",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
@ -112,7 +113,8 @@ impl Command for BitsNot {
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Apply logical negation to a list of numbers, treat input as signed number",
|
||||
description:
|
||||
"Apply logical negation to a list of numbers, treat input as signed number",
|
||||
example: "[4 3 2] | bits not --signed",
|
||||
result: Some(Value::list(
|
||||
vec![
|
||||
|
@ -66,7 +66,7 @@ impl Command for BitsOr {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Endian must be one of native, little, big".to_string(),
|
||||
span: endian.span,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -106,7 +106,8 @@ impl Command for BitsOr {
|
||||
result: Some(Value::test_binary(vec![0xca, 0xfe])),
|
||||
},
|
||||
Example {
|
||||
description: "Apply bitwise or to binary data of varying lengths with specified endianness",
|
||||
description:
|
||||
"Apply bitwise or to binary data of varying lengths with specified endianness",
|
||||
example: "0x[c0 ff ee] | bits or 0x[ff] --endian big",
|
||||
result: Some(Value::test_binary(vec![0xc0, 0xff, 0xff])),
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
struct Arguments {
|
||||
@ -222,8 +222,7 @@ fn rotate_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize)
|
||||
debug_assert!(byte_shift < data.len());
|
||||
debug_assert!(
|
||||
(1..8).contains(&bit_shift),
|
||||
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
|
||||
);
|
||||
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift");
|
||||
let mut bytes = Vec::with_capacity(data.len());
|
||||
let mut next_index = byte_shift;
|
||||
for _ in 0..data.len() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
struct Arguments {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
||||
use itertools::Itertools;
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use std::iter;
|
||||
@ -237,8 +237,7 @@ fn shift_bytes_left(data: &[u8], byte_shift: usize) -> Vec<u8> {
|
||||
|
||||
fn shift_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize) -> Vec<u8> {
|
||||
use itertools::Position::*;
|
||||
debug_assert!(
|
||||
(1..8).contains(&bit_shift),
|
||||
debug_assert!((1..8).contains(&bit_shift),
|
||||
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
|
||||
);
|
||||
data.iter()
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
struct Arguments {
|
||||
|
@ -66,7 +66,7 @@ impl Command for BitsXor {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: "Endian must be one of native, little, big".to_string(),
|
||||
span: endian.span,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -106,7 +106,8 @@ impl Command for BitsXor {
|
||||
result: Some(Value::test_binary(vec![0x70, 0x40])),
|
||||
},
|
||||
Example {
|
||||
description: "Apply bitwise xor to binary data of varying lengths with specified endianness",
|
||||
description:
|
||||
"Apply bitwise xor to binary data of varying lengths with specified endianness",
|
||||
example: "0x[ca fe] | bits xor 0x[aa] --endian big",
|
||||
result: Some(Value::test_binary(vec![0xca, 0x54])),
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nu_engine::{ClosureEval, ClosureEvalOnce, command_prelude::*};
|
||||
use nu_engine::{command_prelude::*, ClosureEval, ClosureEvalOnce};
|
||||
use nu_protocol::engine::Closure;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -72,7 +72,7 @@ impl Command for EachWhile {
|
||||
|
||||
let metadata = input.metadata();
|
||||
match input {
|
||||
PipelineData::Empty => Ok(PipelineData::empty()),
|
||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||
PipelineData::Value(Value::Range { .. }, ..)
|
||||
| PipelineData::Value(Value::List { .. }, ..)
|
||||
| PipelineData::ListStream(..) => {
|
||||
@ -109,7 +109,7 @@ impl Command for EachWhile {
|
||||
.fuse()
|
||||
.into_pipeline_data(head, engine_state.signals().clone()))
|
||||
} else {
|
||||
Ok(PipelineData::empty())
|
||||
Ok(PipelineData::Empty)
|
||||
}
|
||||
}
|
||||
// This match allows non-iterables to be accepted,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{VerticalDirection, vertical_rotate_value};
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{HorizontalDirection, horizontal_rotate_value};
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{HorizontalDirection, horizontal_rotate_value};
|
||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{VerticalDirection, vertical_rotate_value};
|
||||
use super::{vertical_rotate_value, VerticalDirection};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -36,93 +36,104 @@ impl Command for Rotate {
|
||||
description: "Rotate a record clockwise, producing a table (like `transpose` but with column order reversed)",
|
||||
example: "{a:1, b:2} | rotate",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(1),
|
||||
"column1" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(2),
|
||||
"column1" => Value::test_string("b"),
|
||||
}),
|
||||
])),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(1),
|
||||
"column1" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(2),
|
||||
"column1" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate 2x3 table clockwise",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(5),
|
||||
"column1" => Value::test_int(3),
|
||||
"column2" => Value::test_int(1),
|
||||
"column3" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(6),
|
||||
"column1" => Value::test_int(4),
|
||||
"column2" => Value::test_int(2),
|
||||
"column3" => Value::test_string("b"),
|
||||
}),
|
||||
])),
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(5),
|
||||
"column1" => Value::test_int(3),
|
||||
"column2" => Value::test_int(1),
|
||||
"column3" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_int(6),
|
||||
"column1" => Value::test_int(4),
|
||||
"column2" => Value::test_int(2),
|
||||
"column3" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table clockwise and change columns names",
|
||||
example: "[[a b]; [1 2]] | rotate col_a col_b",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(1),
|
||||
"col_b" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(2),
|
||||
"col_b" => Value::test_string("b"),
|
||||
}),
|
||||
])),
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(1),
|
||||
"col_b" => Value::test_string("a"),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_int(2),
|
||||
"col_b" => Value::test_string("b"),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter clockwise",
|
||||
example: "[[a b]; [1 2]] | rotate --ccw",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
}),
|
||||
])),
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter-clockwise",
|
||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
"column2" => Value::test_int(4),
|
||||
"column3" => Value::test_int(6),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
"column2" => Value::test_int(3),
|
||||
"column3" => Value::test_int(5),
|
||||
}),
|
||||
])),
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("b"),
|
||||
"column1" => Value::test_int(2),
|
||||
"column2" => Value::test_int(4),
|
||||
"column3" => Value::test_int(6),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"column0" => Value::test_string("a"),
|
||||
"column1" => Value::test_int(1),
|
||||
"column2" => Value::test_int(3),
|
||||
"column3" => Value::test_int(5),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Rotate table counter-clockwise and change columns names",
|
||||
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
|
||||
result: Some(Value::test_list(vec![
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("b"),
|
||||
"col_b" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("a"),
|
||||
"col_b" => Value::test_int(1),
|
||||
}),
|
||||
])),
|
||||
result: Some(Value::test_list(
|
||||
vec![
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("b"),
|
||||
"col_b" => Value::test_int(2),
|
||||
}),
|
||||
Value::test_record(record! {
|
||||
"col_a" => Value::test_string("a"),
|
||||
"col_b" => Value::test_int(1),
|
||||
}),
|
||||
],
|
||||
)),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_engine::{ClosureEval, command_prelude::*};
|
||||
use nu_protocol::{PipelineIterator, engine::Closure};
|
||||
use nu_engine::{command_prelude::*, ClosureEval};
|
||||
use nu_protocol::{engine::Closure, PipelineIterator};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -12,10 +12,7 @@ impl Command for UpdateCells {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("update cells")
|
||||
.input_output_types(vec![
|
||||
(Type::table(), Type::table()),
|
||||
(Type::record(), Type::record()),
|
||||
])
|
||||
.input_output_types(vec![(Type::table(), Type::table())])
|
||||
.required(
|
||||
"closure",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||
@ -80,15 +77,6 @@ impl Command for UpdateCells {
|
||||
"2021-11-18" => Value::test_string(""),
|
||||
})])),
|
||||
},
|
||||
Example {
|
||||
example: r#"{a: 1, b: 2, c: 3} | update cells { $in + 10 }"#,
|
||||
description: "Update each value in a record.",
|
||||
result: Some(Value::test_record(record! {
|
||||
"a" => Value::test_int(11),
|
||||
"b" => Value::test_int(12),
|
||||
"c" => Value::test_int(13),
|
||||
})),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@ -97,7 +85,7 @@ impl Command for UpdateCells {
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
mut input: PipelineData,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||
@ -114,51 +102,14 @@ impl Command for UpdateCells {
|
||||
|
||||
let metadata = input.metadata();
|
||||
|
||||
match input {
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
ref mut val,
|
||||
internal_span,
|
||||
},
|
||||
..,
|
||||
) => {
|
||||
let val = val.to_mut();
|
||||
update_record(
|
||||
val,
|
||||
&mut ClosureEval::new(engine_state, stack, closure),
|
||||
internal_span,
|
||||
columns.as_ref(),
|
||||
);
|
||||
Ok(input)
|
||||
}
|
||||
_ => Ok(UpdateCellIterator {
|
||||
iter: input.into_iter(),
|
||||
closure: ClosureEval::new(engine_state, stack, closure),
|
||||
columns,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_data(head, engine_state.signals().clone())
|
||||
.set_metadata(metadata)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_record(
|
||||
record: &mut Record,
|
||||
closure: &mut ClosureEval,
|
||||
span: Span,
|
||||
cols: Option<&HashSet<String>>,
|
||||
) {
|
||||
if let Some(columns) = cols {
|
||||
for (col, val) in record.iter_mut() {
|
||||
if columns.contains(col) {
|
||||
*val = eval_value(closure, span, std::mem::take(val));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (_, val) in record.iter_mut() {
|
||||
*val = eval_value(closure, span, std::mem::take(val))
|
||||
Ok(UpdateCellIterator {
|
||||
iter: input.into_iter(),
|
||||
closure: ClosureEval::new(engine_state, stack, closure),
|
||||
columns,
|
||||
span: head,
|
||||
}
|
||||
.into_pipeline_data(head, engine_state.signals().clone())
|
||||
.set_metadata(metadata))
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +128,18 @@ impl Iterator for UpdateCellIterator {
|
||||
|
||||
let value = if let Value::Record { val, .. } = &mut value {
|
||||
let val = val.to_mut();
|
||||
update_record(val, &mut self.closure, self.span, self.columns.as_ref());
|
||||
if let Some(columns) = &self.columns {
|
||||
for (col, val) in val.iter_mut() {
|
||||
if columns.contains(col) {
|
||||
*val = eval_value(&mut self.closure, self.span, std::mem::take(val));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (_, val) in val.iter_mut() {
|
||||
*val = eval_value(&mut self.closure, self.span, std::mem::take(val))
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
} else {
|
||||
eval_value(&mut self.closure, self.span, value)
|
||||
|
@ -55,7 +55,7 @@ fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
||||
.map(|(k, v)| (k, Value::string(v, head)))
|
||||
.collect();
|
||||
|
||||
Ok(PipelineData::value(Value::record(record, head), metadata))
|
||||
Ok(PipelineData::Value(Value::record(record, head), metadata))
|
||||
}
|
||||
_ => Err(ShellError::UnsupportedInput {
|
||||
msg: "String not compatible with URL encoding".to_string(),
|
||||
|
@ -101,7 +101,7 @@ impl Command for ToHtml {
|
||||
.named(
|
||||
"theme",
|
||||
SyntaxShape::String,
|
||||
"the name of the theme to use (github, blulocolight, ...); case-insensitive",
|
||||
"the name of the theme to use (github, blulocolight, ...)",
|
||||
Some('t'),
|
||||
)
|
||||
.switch(
|
||||
@ -109,26 +109,18 @@ impl Command for ToHtml {
|
||||
"produce a color table of all available themes",
|
||||
Some('l'),
|
||||
)
|
||||
.switch("raw", "do not escape html tags", Some('r'))
|
||||
.category(Category::Formats)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Outputs an HTML string representing the contents of this table",
|
||||
description: "Outputs an HTML string representing the contents of this table",
|
||||
example: "[[foo bar]; [1 2]] | to html",
|
||||
result: Some(Value::test_string(
|
||||
r#"<html><style>body { background-color:white;color:black; }</style><body><table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr></tbody></table></body></html>"#,
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Outputs an HTML string using a record of xml data",
|
||||
example: r#"{tag: a attributes: { style: "color: red" } content: ["hello!"] } | to xml | to html --raw"#,
|
||||
result: Some(Value::test_string(
|
||||
r#"<html><style>body { background-color:white;color:black; }</style><body><a style="color: red">hello!</a></body></html>"#,
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Optionally, only output the html for the content itself",
|
||||
example: "[[foo bar]; [1 2]] | to html --partial",
|
||||
@ -171,16 +163,9 @@ fn get_theme_from_asset_file(
|
||||
) -> Result<HashMap<&'static str, String>, ShellError> {
|
||||
let theme_name = match theme {
|
||||
Some(s) => &s.item,
|
||||
None => {
|
||||
return Ok(convert_html_theme_to_hash_map(
|
||||
is_dark,
|
||||
&HtmlTheme::default(),
|
||||
));
|
||||
}
|
||||
None => "default", // There is no theme named "default" so this will be HtmlTheme::default(), which is "nu_default".
|
||||
};
|
||||
|
||||
let theme_span = theme.map(|s| s.span).unwrap_or(Span::unknown());
|
||||
|
||||
// 228 themes come from
|
||||
// https://github.com/mbadolato/iTerm2-Color-Schemes/tree/master/windowsterminal
|
||||
// we should find a hit on any name in there
|
||||
@ -190,17 +175,8 @@ fn get_theme_from_asset_file(
|
||||
let th = asset
|
||||
.themes
|
||||
.into_iter()
|
||||
.find(|n| n.name.eq_ignore_case(theme_name)); // case insensitive search
|
||||
|
||||
let th = match th {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: format!("Unknown HTML theme '{theme_name}'"),
|
||||
span: theme_span,
|
||||
});
|
||||
}
|
||||
};
|
||||
.find(|n| n.name.eq_ignore_case(theme_name)) // case insensitive search
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(convert_html_theme_to_hash_map(is_dark, &th))
|
||||
}
|
||||
@ -262,7 +238,6 @@ fn to_html(
|
||||
let dark = call.has_flag(engine_state, stack, "dark")?;
|
||||
let partial = call.has_flag(engine_state, stack, "partial")?;
|
||||
let list = call.has_flag(engine_state, stack, "list")?;
|
||||
let raw = call.has_flag(engine_state, stack, "raw")?;
|
||||
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
|
||||
let config = &stack.get_config(engine_state);
|
||||
|
||||
@ -282,20 +257,18 @@ fn to_html(
|
||||
None => head,
|
||||
};
|
||||
|
||||
let color_hm = match get_theme_from_asset_file(dark, theme.as_ref()) {
|
||||
let color_hm = get_theme_from_asset_file(dark, theme.as_ref());
|
||||
let color_hm = match color_hm {
|
||||
Ok(c) => c,
|
||||
Err(e) => match e {
|
||||
ShellError::TypeMismatch {
|
||||
err_message,
|
||||
span: _,
|
||||
} => {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message,
|
||||
span: theme_span,
|
||||
});
|
||||
}
|
||||
_ => return Err(e),
|
||||
},
|
||||
_ => {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Error finding theme name".into(),
|
||||
msg: "Error finding theme name".into(),
|
||||
span: Some(theme_span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// change the color of the page
|
||||
@ -328,15 +301,15 @@ fn to_html(
|
||||
let inner_value = match vec_of_values.len() {
|
||||
0 => String::default(),
|
||||
1 => match headers {
|
||||
Some(headers) => html_table(vec_of_values, headers, raw, config),
|
||||
Some(headers) => html_table(vec_of_values, headers, config),
|
||||
None => {
|
||||
let value = &vec_of_values[0];
|
||||
html_value(value.clone(), raw, config)
|
||||
html_value(value.clone(), config)
|
||||
}
|
||||
},
|
||||
_ => match headers {
|
||||
Some(headers) => html_table(vec_of_values, headers, raw, config),
|
||||
None => html_list(vec_of_values, raw, config),
|
||||
Some(headers) => html_table(vec_of_values, headers, config),
|
||||
None => html_list(vec_of_values, config),
|
||||
},
|
||||
};
|
||||
|
||||
@ -404,19 +377,19 @@ fn theme_demo(span: Span) -> PipelineData {
|
||||
})
|
||||
}
|
||||
|
||||
fn html_list(list: Vec<Value>, raw: bool, config: &Config) -> String {
|
||||
fn html_list(list: Vec<Value>, config: &Config) -> String {
|
||||
let mut output_string = String::new();
|
||||
output_string.push_str("<ol>");
|
||||
for value in list {
|
||||
output_string.push_str("<li>");
|
||||
output_string.push_str(&html_value(value, raw, config));
|
||||
output_string.push_str(&html_value(value, config));
|
||||
output_string.push_str("</li>");
|
||||
}
|
||||
output_string.push_str("</ol>");
|
||||
output_string
|
||||
}
|
||||
|
||||
fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Config) -> String {
|
||||
fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> String {
|
||||
let mut output_string = String::new();
|
||||
|
||||
output_string.push_str("<table>");
|
||||
@ -439,7 +412,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Confi
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Value::nothing(span));
|
||||
output_string.push_str("<td>");
|
||||
output_string.push_str(&html_value(data, raw, config));
|
||||
output_string.push_str(&html_value(data, config));
|
||||
output_string.push_str("</td>");
|
||||
}
|
||||
output_string.push_str("</tr>");
|
||||
@ -450,7 +423,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Confi
|
||||
output_string
|
||||
}
|
||||
|
||||
fn html_value(value: Value, raw: bool, config: &Config) -> String {
|
||||
fn html_value(value: Value, config: &Config) -> String {
|
||||
let mut output_string = String::new();
|
||||
match value {
|
||||
Value::Binary { val, .. } => {
|
||||
@ -459,22 +432,11 @@ fn html_value(value: Value, raw: bool, config: &Config) -> String {
|
||||
output_string.push_str(&output);
|
||||
output_string.push_str("</pre>");
|
||||
}
|
||||
other => {
|
||||
if raw {
|
||||
output_string.push_str(
|
||||
&other
|
||||
.to_abbreviated_string(config)
|
||||
.to_string()
|
||||
.replace('\n', "<br>"),
|
||||
)
|
||||
} else {
|
||||
output_string.push_str(
|
||||
&v_htmlescape::escape(&other.to_abbreviated_string(config))
|
||||
.to_string()
|
||||
.replace('\n', "<br>"),
|
||||
)
|
||||
}
|
||||
}
|
||||
other => output_string.push_str(
|
||||
&v_htmlescape::escape(&other.to_abbreviated_string(config))
|
||||
.to_string()
|
||||
.replace('\n', "<br>"),
|
||||
),
|
||||
}
|
||||
output_string
|
||||
}
|
||||
@ -737,92 +699,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples_with_commands;
|
||||
use nu_command::ToXml;
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples_with_commands(ToHtml {}, &[&ToXml])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_theme_from_asset_file_returns_default() {
|
||||
let result = super::get_theme_from_asset_file(false, None);
|
||||
|
||||
assert!(result.is_ok(), "Expected Ok result for None theme");
|
||||
|
||||
let theme_map = result.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
theme_map.get("background").map(String::as_str),
|
||||
Some("white"),
|
||||
"Expected default background color to be white"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
theme_map.get("foreground").map(String::as_str),
|
||||
Some("black"),
|
||||
"Expected default foreground color to be black"
|
||||
);
|
||||
|
||||
assert!(
|
||||
theme_map.contains_key("red"),
|
||||
"Expected default theme to have a 'red' color"
|
||||
);
|
||||
|
||||
assert!(
|
||||
theme_map.contains_key("bold_green"),
|
||||
"Expected default theme to have a 'bold_green' color"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_a_valid_theme() {
|
||||
let theme_name = "Dracula".to_string().into_spanned(Span::new(0, 7));
|
||||
let result = super::get_theme_from_asset_file(false, Some(&theme_name));
|
||||
|
||||
assert!(result.is_ok(), "Expected Ok result for valid theme");
|
||||
let theme_map = result.unwrap();
|
||||
let required_keys = [
|
||||
"background",
|
||||
"foreground",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"bold_red",
|
||||
"bold_green",
|
||||
"bold_blue",
|
||||
];
|
||||
|
||||
for key in required_keys {
|
||||
assert!(
|
||||
theme_map.contains_key(key),
|
||||
"Expected theme to contain key '{key}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_with_unknown_theme_name() {
|
||||
let result = super::get_theme_from_asset_file(
|
||||
false,
|
||||
Some(&"doesnt-exist".to_string().into_spanned(Span::new(0, 13))),
|
||||
);
|
||||
|
||||
assert!(result.is_err(), "Expected error for invalid theme name");
|
||||
|
||||
if let Err(err) = result {
|
||||
assert!(
|
||||
matches!(err, ShellError::TypeMismatch { .. }),
|
||||
"Expected TypeMismatch error, got: {err:?}"
|
||||
);
|
||||
|
||||
if let ShellError::TypeMismatch { err_message, span } = err {
|
||||
assert!(
|
||||
err_message.contains("doesnt-exist"),
|
||||
"Error message should mention theme name, got: {err_message}"
|
||||
);
|
||||
assert_eq!(span.start, 0);
|
||||
assert_eq!(span.end, 13);
|
||||
}
|
||||
}
|
||||
test_examples(ToHtml {})
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nu_ansi_term::{Gradient, Rgb, build_all_gradient_text, gradient::TargetGround};
|
||||
use nu_ansi_term::{build_all_gradient_text, gradient::TargetGround, Gradient, Rgb};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -70,25 +70,29 @@ impl Command for SubCommand {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "draw text in a gradient with foreground start and end colors",
|
||||
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
||||
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff' --bgstart '0xe81cff' --bgend '0x40c9ff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
||||
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
||||
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgend '0xe81cff'",
|
||||
result: None,
|
||||
},
|
||||
description: "draw text in a gradient with foreground start and end colors",
|
||||
example:
|
||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
||||
example:
|
||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff' --bgstart '0xe81cff' --bgend '0x40c9ff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
||||
example:
|
||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
||||
example:
|
||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgend '0xe81cff'",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -296,7 +300,7 @@ fn action(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SubCommand, action};
|
||||
use super::{action, SubCommand};
|
||||
use nu_ansi_term::Rgb;
|
||||
use nu_protocol::{Span, Value};
|
||||
|
||||
@ -310,9 +314,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_fg_gradient() {
|
||||
let input_string = Value::test_string("Hello, World!");
|
||||
let expected = Value::test_string(
|
||||
"\u{1b}[38;2;64;201;255mH\u{1b}[38;2;76;187;254me\u{1b}[38;2;89;174;254ml\u{1b}[38;2;102;160;254ml\u{1b}[38;2;115;147;254mo\u{1b}[38;2;128;133;254m,\u{1b}[38;2;141;120;254m \u{1b}[38;2;153;107;254mW\u{1b}[38;2;166;94;254mo\u{1b}[38;2;179;80;254mr\u{1b}[38;2;192;67;254ml\u{1b}[38;2;205;53;254md\u{1b}[38;2;218;40;254m!\u{1b}[0m",
|
||||
);
|
||||
let expected = Value::test_string("\u{1b}[38;2;64;201;255mH\u{1b}[38;2;76;187;254me\u{1b}[38;2;89;174;254ml\u{1b}[38;2;102;160;254ml\u{1b}[38;2;115;147;254mo\u{1b}[38;2;128;133;254m,\u{1b}[38;2;141;120;254m \u{1b}[38;2;153;107;254mW\u{1b}[38;2;166;94;254mo\u{1b}[38;2;179;80;254mr\u{1b}[38;2;192;67;254ml\u{1b}[38;2;205;53;254md\u{1b}[38;2;218;40;254m!\u{1b}[0m");
|
||||
let fg_start = Rgb::from_hex_string("0x40c9ff".to_string());
|
||||
let fg_end = Rgb::from_hex_string("0xe81cff".to_string());
|
||||
let actual = action(
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use nu_protocol::{Signals, shell_error::io::IoError};
|
||||
use nu_protocol::{shell_error::io::IoError, Signals};
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
struct Arguments {
|
||||
@ -68,33 +68,42 @@ impl Command for FormatBits {
|
||||
Example {
|
||||
description: "convert a binary value into a string, padded to 8 places with 0s",
|
||||
example: "0x[1] | format bits",
|
||||
result: Some(Value::string("00000001", Span::test_data())),
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert an int into a string, padded to 8 places with 0s",
|
||||
example: "1 | format bits",
|
||||
result: Some(Value::string("00000001", Span::test_data())),
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a filesize value into a string, padded to 8 places with 0s",
|
||||
example: "1b | format bits",
|
||||
result: Some(Value::string("00000001", Span::test_data())),
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a duration value into a string, padded to 8 places with 0s",
|
||||
example: "1ns | format bits",
|
||||
result: Some(Value::string("00000001", Span::test_data())),
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a boolean value into a string, padded to 8 places with 0s",
|
||||
example: "true | format bits",
|
||||
result: Some(Value::string("00000001", Span::test_data())),
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a string into a raw binary string, padded with 0s to 8 places",
|
||||
example: "'nushell.sh' | format bits",
|
||||
result: Some(Value::string(
|
||||
"01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
|
||||
result: Some(Value::string("01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
@ -113,7 +122,7 @@ fn format_bits(
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
|
||||
if let PipelineData::ByteStream(stream, metadata) = input {
|
||||
Ok(PipelineData::byte_stream(
|
||||
Ok(PipelineData::ByteStream(
|
||||
byte_stream_to_bits(stream, head),
|
||||
metadata,
|
||||
))
|
||||
@ -134,7 +143,7 @@ fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
|
||||
let mut byte = [0];
|
||||
if reader
|
||||
.read(&mut byte[..])
|
||||
.map_err(|err| IoError::new(err, head, None))?
|
||||
.map_err(|err| IoError::new(err.kind(), head, None))?
|
||||
> 0
|
||||
{
|
||||
// Format the byte as bits
|
||||
@ -161,28 +170,28 @@ fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
|
||||
let bytes = v.to_ne_bytes();
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in bytes {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
} else if let Some(v) = num.to_i16() {
|
||||
let bytes = v.to_ne_bytes();
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in bytes {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
} else if let Some(v) = num.to_i32() {
|
||||
let bytes = v.to_ne_bytes();
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in bytes {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
} else {
|
||||
let bytes = num.to_ne_bytes();
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in bytes {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
}
|
||||
@ -193,7 +202,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||
Value::Binary { val, .. } => {
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in val {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
}
|
||||
@ -204,7 +213,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||
let raw_bytes = val.as_bytes();
|
||||
let mut raw_string = "".to_string();
|
||||
for ch in raw_bytes {
|
||||
raw_string.push_str(&format!("{ch:08b} "));
|
||||
raw_string.push_str(&format!("{:08b} ", ch));
|
||||
}
|
||||
Value::string(raw_string.trim(), span)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{Config, ListStream, ast::PathMember, casing::Casing, engine::StateWorkingSet};
|
||||
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FormatPattern;
|
||||
@ -191,7 +191,7 @@ fn format(
|
||||
// We can only handle a Record or a List of Records
|
||||
match data_as_value {
|
||||
Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
|
||||
Ok(value) => Ok(PipelineData::value(Value::string(value, head_span), None)),
|
||||
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
|
||||
Err(value) => Err(value),
|
||||
},
|
||||
|
||||
@ -214,7 +214,7 @@ fn format(
|
||||
wrong_type: val.get_type().to_string(),
|
||||
dst_span: head_span,
|
||||
src_span: val.span(),
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,14 +251,14 @@ fn format_record(
|
||||
val: path.to_string(),
|
||||
span: *span,
|
||||
optional: false,
|
||||
casing: Casing::Sensitive,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expanded_string = data_as_value
|
||||
.follow_cell_path(&path_members)?
|
||||
.to_expanded_string(", ", config);
|
||||
output.push_str(expanded_string.as_str())
|
||||
match data_as_value.clone().follow_cell_path(&path_members, false) {
|
||||
Ok(value_at_column) => {
|
||||
output.push_str(value_at_column.to_expanded_string(", ", config).as_str())
|
||||
}
|
||||
Err(se) => return Err(se),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use nu_cmd_base::input_handler::{CellPathOnlyArgs, operate};
|
||||
use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -16,11 +16,6 @@ impl Command for FormatNumber {
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("format number")
|
||||
.input_output_types(vec![(Type::Number, Type::record())])
|
||||
.switch(
|
||||
"no-prefix",
|
||||
"don't include the binary, hex or octal prefixes",
|
||||
Some('n'),
|
||||
)
|
||||
.category(Category::Conversions)
|
||||
}
|
||||
|
||||
@ -29,36 +24,20 @@ impl Command for FormatNumber {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Get a record containing multiple formats for the number 42",
|
||||
example: "42 | format number",
|
||||
result: Some(Value::test_record(record! {
|
||||
"debug" => Value::test_string("42"),
|
||||
"display" => Value::test_string("42"),
|
||||
"binary" => Value::test_string("0b101010"),
|
||||
"lowerexp" => Value::test_string("4.2e1"),
|
||||
"upperexp" => Value::test_string("4.2E1"),
|
||||
"lowerhex" => Value::test_string("0x2a"),
|
||||
"upperhex" => Value::test_string("0x2A"),
|
||||
"octal" => Value::test_string("0o52"),
|
||||
})),
|
||||
},
|
||||
Example {
|
||||
description: "Format float without prefixes",
|
||||
example: "3.14 | format number --no-prefix",
|
||||
result: Some(Value::test_record(record! {
|
||||
"debug" => Value::test_string("3.14"),
|
||||
"display" => Value::test_string("3.14"),
|
||||
"binary" => Value::test_string("100000000001001000111101011100001010001111010111000010100011111"),
|
||||
"lowerexp" => Value::test_string("3.14e0"),
|
||||
"upperexp" => Value::test_string("3.14E0"),
|
||||
"lowerhex" => Value::test_string("40091eb851eb851f"),
|
||||
"upperhex" => Value::test_string("40091EB851EB851F"),
|
||||
"octal" => Value::test_string("400110753412172702437"),
|
||||
})),
|
||||
},
|
||||
]
|
||||
vec![Example {
|
||||
description: "Get a record containing multiple formats for the number 42",
|
||||
example: "42 | format number",
|
||||
result: Some(Value::test_record(record! {
|
||||
"binary" => Value::test_string("0b101010"),
|
||||
"debug" => Value::test_string("42"),
|
||||
"display" => Value::test_string("42"),
|
||||
"lowerexp" => Value::test_string("4.2e1"),
|
||||
"lowerhex" => Value::test_string("0x2a"),
|
||||
"octal" => Value::test_string("0o52"),
|
||||
"upperexp" => Value::test_string("4.2E1"),
|
||||
"upperhex" => Value::test_string("0x2A"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -80,24 +59,14 @@ pub(crate) fn format_number(
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
if call.has_flag(engine_state, stack, "no-prefix")? {
|
||||
operate(
|
||||
action_no_prefix,
|
||||
args,
|
||||
input,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
)
|
||||
} else {
|
||||
operate(action, args, input, call.head, engine_state.signals())
|
||||
}
|
||||
operate(action, args, input, call.head, engine_state.signals())
|
||||
}
|
||||
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Float { val, .. } => format_f64(*val, false, span),
|
||||
Value::Int { val, .. } => format_i64(*val, false, span),
|
||||
Value::Filesize { val, .. } => format_i64(val.get(), false, span),
|
||||
Value::Float { val, .. } => format_f64(*val, span),
|
||||
Value::Int { val, .. } => format_i64(*val, span),
|
||||
Value::Filesize { val, .. } => format_i64(val.get(), span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::error(
|
||||
@ -112,80 +81,33 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn action_no_prefix(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Float { val, .. } => format_f64(*val, true, span),
|
||||
Value::Int { val, .. } => format_i64(*val, true, span),
|
||||
Value::Filesize { val, .. } => format_i64(val.get(), true, span),
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "float, int, or filesize".into(),
|
||||
wrong_type: other.get_type().to_string(),
|
||||
dst_span: span,
|
||||
src_span: other.span(),
|
||||
},
|
||||
span,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_i64(num: i64, no_prefix: bool, span: Span) -> Value {
|
||||
fn format_i64(num: i64, span: Span) -> Value {
|
||||
Value::record(
|
||||
record! {
|
||||
"binary" => Value::string(format!("{num:#b}"), span),
|
||||
"debug" => Value::string(format!("{num:#?}"), span),
|
||||
"display" => Value::string(format!("{num}"), span),
|
||||
"binary" => Value::string(
|
||||
if no_prefix { format!("{num:b}") } else { format!("{num:#b}") },
|
||||
span,
|
||||
),
|
||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||
"lowerhex" => Value::string(format!("{num:#x}"), span),
|
||||
"octal" => Value::string(format!("{num:#o}"), span),
|
||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||
"lowerhex" => Value::string(
|
||||
if no_prefix { format!("{num:x}") } else { format!("{num:#x}") },
|
||||
span,
|
||||
),
|
||||
"upperhex" => Value::string(
|
||||
if no_prefix { format!("{num:X}") } else { format!("{num:#X}") },
|
||||
span,
|
||||
),
|
||||
"octal" => Value::string(
|
||||
if no_prefix { format!("{num:o}") } else { format!("{num:#o}") },
|
||||
span,
|
||||
)
|
||||
"upperhex" => Value::string(format!("{num:#X}"), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
fn format_f64(num: f64, no_prefix: bool, span: Span) -> Value {
|
||||
fn format_f64(num: f64, span: Span) -> Value {
|
||||
Value::record(
|
||||
record! {
|
||||
"binary" => Value::string(format!("{:b}", num.to_bits()), span),
|
||||
"debug" => Value::string(format!("{num:#?}"), span),
|
||||
"display" => Value::string(format!("{num}"), span),
|
||||
"binary" => Value::string(
|
||||
if no_prefix {
|
||||
format!("{:b}", num.to_bits())
|
||||
} else {
|
||||
format!("{:#b}", num.to_bits())
|
||||
},
|
||||
span,
|
||||
),
|
||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||
"lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span),
|
||||
"octal" => Value::string(format!("{:0o}", num.to_bits()), span),
|
||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||
"lowerhex" => Value::string(
|
||||
if no_prefix { format!("{:x}", num.to_bits()) } else { format!("{:#x}", num.to_bits()) },
|
||||
span,
|
||||
),
|
||||
"upperhex" => Value::string(
|
||||
if no_prefix { format!("{:X}", num.to_bits()) } else { format!("{:#X}", num.to_bits()) },
|
||||
span,
|
||||
),
|
||||
"octal" => Value::string(
|
||||
if no_prefix { format!("{:o}", num.to_bits()) } else { format!("{:#o}", num.to_bits()) },
|
||||
span,
|
||||
)
|
||||
"upperhex" => Value::string(format!("{:0X}", num.to_bits()), span),
|
||||
},
|
||||
span,
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ pub use snake_case::StrSnakeCase;
|
||||
pub use str_::Str;
|
||||
pub use title_case::StrTitleCase;
|
||||
|
||||
use nu_cmd_base::input_handler::{CmdArgument, operate as general_operate};
|
||||
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
struct Arguments<F: Fn(&str) -> String + Send + Sync + 'static> {
|
||||
|
@ -4,4 +4,4 @@ pub mod extra;
|
||||
pub use extra::*;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use example_test::{test_examples, test_examples_with_commands};
|
||||
pub use example_test::test_examples;
|
||||
|
@ -3,10 +3,10 @@ authors = ["The Nushell Project Developers"]
|
||||
build = "build.rs"
|
||||
description = "Nushell's core language commands"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.106.2"
|
||||
version = "0.104.1"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
@ -15,23 +15,20 @@ bench = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.106.2", default-features = false }
|
||||
nu-experimental = { path = "../nu-experimental", version = "0.106.2" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.106.2" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.106.2", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.106.2", default-features = false }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.2" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1", default-features = false }
|
||||
|
||||
itertools = { workspace = true }
|
||||
shadow-rs = { version = "1.2", default-features = false }
|
||||
shadow-rs = { version = "1.1", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { version = "1.2", default-features = false, features = ["build"] }
|
||||
shadow-rs = { version = "1.1", default-features = false, features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = { workspace = true }
|
||||
quickcheck_macros = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["os"]
|
||||
@ -44,3 +41,8 @@ plugin = [
|
||||
"nu-protocol/plugin",
|
||||
"os",
|
||||
]
|
||||
|
||||
trash-support = []
|
||||
sqlite = []
|
||||
static-link-openssl = []
|
||||
system-clipboard = []
|
||||
|
@ -1,148 +0,0 @@
|
||||
use nu_cmd_base::WrapCall;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AttrDeprecated;
|
||||
|
||||
impl Command for AttrDeprecated {
|
||||
fn name(&self) -> &str {
|
||||
"attr deprecated"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("attr deprecated")
|
||||
.input_output_types(vec![
|
||||
(Type::Nothing, Type::Nothing),
|
||||
(Type::Nothing, Type::String),
|
||||
])
|
||||
.optional(
|
||||
"message",
|
||||
SyntaxShape::String,
|
||||
"Help message to include with deprecation warning.",
|
||||
)
|
||||
.named(
|
||||
"flag",
|
||||
SyntaxShape::String,
|
||||
"Mark a flag as deprecated rather than the command",
|
||||
None,
|
||||
)
|
||||
.named(
|
||||
"since",
|
||||
SyntaxShape::String,
|
||||
"Denote a version when this item was deprecated",
|
||||
Some('s'),
|
||||
)
|
||||
.named(
|
||||
"remove",
|
||||
SyntaxShape::String,
|
||||
"Denote a version when this item will be removed",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"report",
|
||||
SyntaxShape::String,
|
||||
"How to warn about this item. One of: first (default), every",
|
||||
None,
|
||||
)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Attribute for marking a command or flag as deprecated."
|
||||
}
|
||||
|
||||
fn extra_description(&self) -> &str {
|
||||
"Mark a command (default) or flag/switch (--flag) as deprecated. By default, only the first usage will trigger a deprecation warning.
|
||||
|
||||
A help message can be included to provide more context for the deprecation, such as what to use as a replacement.
|
||||
|
||||
Also consider setting the category to deprecated with @category deprecated"
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = WrapCall::Eval(engine_state, stack, call);
|
||||
Ok(deprecated_record(call)?.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let call = WrapCall::ConstEval(working_set, call);
|
||||
Ok(deprecated_record(call)?.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Add a deprecation warning to a custom command",
|
||||
example: r###"@deprecated
|
||||
def outdated [] {}"###,
|
||||
result: Some(Value::nothing(Span::test_data())),
|
||||
},
|
||||
Example {
|
||||
description: "Add a deprecation warning with a custom message",
|
||||
example: r###"@deprecated "Use my-new-command instead."
|
||||
@category deprecated
|
||||
def my-old-command [] {}"###,
|
||||
result: Some(Value::string(
|
||||
"Use my-new-command instead.",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn deprecated_record(call: WrapCall) -> Result<Value, ShellError> {
|
||||
let (call, message): (_, Option<Spanned<String>>) = call.opt(0)?;
|
||||
let (call, flag): (_, Option<Spanned<String>>) = call.get_flag("flag")?;
|
||||
let (call, since): (_, Option<Spanned<String>>) = call.get_flag("since")?;
|
||||
let (call, remove): (_, Option<Spanned<String>>) = call.get_flag("remove")?;
|
||||
let (call, report): (_, Option<Spanned<String>>) = call.get_flag("report")?;
|
||||
|
||||
let mut record = Record::new();
|
||||
if let Some(message) = message {
|
||||
record.push("help", Value::string(message.item, message.span))
|
||||
}
|
||||
if let Some(flag) = flag {
|
||||
record.push("flag", Value::string(flag.item, flag.span))
|
||||
}
|
||||
if let Some(since) = since {
|
||||
record.push("since", Value::string(since.item, since.span))
|
||||
}
|
||||
if let Some(remove) = remove {
|
||||
record.push("expected_removal", Value::string(remove.item, remove.span))
|
||||
}
|
||||
|
||||
let report = if let Some(Spanned { item, span }) = report {
|
||||
match item.as_str() {
|
||||
"every" => Value::string(item, span),
|
||||
"first" => Value::string(item, span),
|
||||
_ => {
|
||||
return Err(ShellError::IncorrectValue {
|
||||
msg: "The report mode must be one of: every, first".into(),
|
||||
val_span: span,
|
||||
call_span: call.head(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Value::string("first", call.head())
|
||||
};
|
||||
record.push("report", report);
|
||||
|
||||
Ok(Value::record(record, call.head()))
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
mod category;
|
||||
mod deprecated;
|
||||
mod example;
|
||||
mod search_terms;
|
||||
|
||||
pub use category::AttrCategory;
|
||||
pub use deprecated::AttrDeprecated;
|
||||
pub use example::AttrExample;
|
||||
pub use search_terms::AttrSearchTerms;
|
||||
|
@ -34,15 +34,10 @@ impl Command for Break {
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
eprintln!(
|
||||
"Tried to execute 'run' for the 'break' command: this code path should never be reached in IR mode"
|
||||
);
|
||||
unreachable!()
|
||||
Err(ShellError::Break { span: call.head })
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_engine::{command_prelude::*, get_eval_block, redirect_env};
|
||||
use nu_protocol::{DataSource, PipelineMetadata, engine::Closure};
|
||||
use nu_protocol::{engine::Closure, DataSource, PipelineMetadata};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Collect;
|
||||
|
@ -41,17 +41,35 @@ impl Command for Const {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
eprintln!(
|
||||
"Tried to execute 'run' for the 'const' command: this code path should never be reached in IR mode"
|
||||
);
|
||||
unreachable!()
|
||||
let call = call.assert_ast_call()?;
|
||||
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
|
||||
id
|
||||
} else {
|
||||
return Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Could not get variable".to_string(),
|
||||
label: "variable not added by the parser".to_string(),
|
||||
span: call.head,
|
||||
});
|
||||
};
|
||||
|
||||
if let Some(constval) = &engine_state.get_var(var_id).const_val {
|
||||
stack.add_var(var_id, constval.clone());
|
||||
|
||||
Ok(PipelineData::empty())
|
||||
} else {
|
||||
Err(ShellError::NushellFailedSpanned {
|
||||
msg: "Missing Constant".to_string(),
|
||||
label: "constant not added by the parser".to_string(),
|
||||
span: call.head,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
|
@ -33,15 +33,10 @@ impl Command for Continue {
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
eprintln!(
|
||||
"Tried to execute 'run' for the 'continue' command: this code path should never be reached in IR mode"
|
||||
);
|
||||
unreachable!()
|
||||
Err(ShellError::Continue { span: call.head })
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{
|
||||
BlockId, ByteStreamSource, Category, PipelineMetadata, Signature,
|
||||
engine::{Closure, StateWorkingSet},
|
||||
BlockId, ByteStreamSource, Category, PipelineMetadata, Signature,
|
||||
};
|
||||
use std::any::type_name;
|
||||
#[derive(Clone)]
|
||||
@ -72,7 +72,8 @@ impl Command for Describe {
|
||||
},
|
||||
Example {
|
||||
description: "Describe the type of a record in a detailed way",
|
||||
example: "{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||
example:
|
||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||
result: Some(Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"detailed_type" => Value::test_string("record<shell: string, uwu: bool, features: record<bugs: bool, multiplatform: bool, speed: int>, fib: list<int>, on_save: closure, first_commit: datetime, my_duration: duration>"),
|
||||
@ -190,32 +191,32 @@ impl Command for Describe {
|
||||
Example {
|
||||
description: "Describe the type of a stream with detailed information",
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe -d",
|
||||
result: None, // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_record(record!(
|
||||
// "type" => Value::test_string("stream"),
|
||||
// "origin" => Value::test_string("nushell"),
|
||||
// "subtype" => Value::test_record(record!(
|
||||
// "type" => Value::test_string("list"),
|
||||
// "length" => Value::test_int(3),
|
||||
// "values" => Value::test_list(vec![
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// ])
|
||||
// ))
|
||||
// ))),
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_record(record!(
|
||||
// "type" => Value::test_string("stream"),
|
||||
// "origin" => Value::test_string("nushell"),
|
||||
// "subtype" => Value::test_record(record!(
|
||||
// "type" => Value::test_string("list"),
|
||||
// "length" => Value::test_int(3),
|
||||
// "values" => Value::test_list(vec![
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// Value::test_string("int"),
|
||||
// ])
|
||||
// ))
|
||||
// ))),
|
||||
},
|
||||
Example {
|
||||
description: "Describe a stream of data, collecting it first",
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe",
|
||||
result: None, // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("list<int> (stream)")),
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("list<int> (stream)")),
|
||||
},
|
||||
Example {
|
||||
description: "Describe the input but do not collect streams",
|
||||
example: "[1 2 3] | each {|i| echo $i} | describe --no-collect",
|
||||
result: None, // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("stream")),
|
||||
result: None // Give "Running external commands not supported" error
|
||||
// result: Some(Value::test_string("stream")),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -296,7 +297,7 @@ fn run(
|
||||
} else {
|
||||
let value = stream.into_value();
|
||||
let base_description = value.get_type().to_string();
|
||||
Value::string(format!("{base_description} (stream)"), head)
|
||||
Value::string(format!("{} (stream)", base_description), head)
|
||||
}
|
||||
}
|
||||
PipelineData::Value(value, ..) => {
|
||||
|
@ -2,7 +2,7 @@ use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_e
|
||||
#[cfg(feature = "os")]
|
||||
use nu_protocol::process::{ChildPipe, ChildProcess};
|
||||
use nu_protocol::{
|
||||
ByteStream, ByteStreamSource, OutDest, engine::Closure, shell_error::io::IoError,
|
||||
engine::Closure, shell_error::io::IoError, ByteStream, ByteStreamSource, OutDest,
|
||||
};
|
||||
|
||||
use std::{
|
||||
@ -107,14 +107,14 @@ impl Command for Do {
|
||||
let mut buf = Vec::new();
|
||||
stdout.read_to_end(&mut buf).map_err(|err| {
|
||||
IoError::new_internal(
|
||||
err,
|
||||
err.kind(),
|
||||
"Could not read stdout to end",
|
||||
nu_protocol::location!(),
|
||||
)
|
||||
})?;
|
||||
Ok::<_, ShellError>(buf)
|
||||
})
|
||||
.map_err(|err| IoError::new(err, head, None))
|
||||
.map_err(|err| IoError::new(err.kind(), head, None))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
@ -126,7 +126,7 @@ impl Command for Do {
|
||||
let mut buf = String::new();
|
||||
stderr
|
||||
.read_to_string(&mut buf)
|
||||
.map_err(|err| IoError::new(err, span, None))?;
|
||||
.map_err(|err| IoError::new(err.kind(), span, None))?;
|
||||
buf
|
||||
}
|
||||
};
|
||||
@ -157,12 +157,12 @@ impl Command for Do {
|
||||
if !stderr_msg.is_empty() {
|
||||
child.stderr = Some(ChildPipe::Tee(Box::new(Cursor::new(stderr_msg))));
|
||||
}
|
||||
Ok(PipelineData::byte_stream(
|
||||
Ok(PipelineData::ByteStream(
|
||||
ByteStream::child(child, span),
|
||||
metadata,
|
||||
))
|
||||
}
|
||||
Err(stream) => Ok(PipelineData::byte_stream(stream, metadata)),
|
||||
Err(stream) => Ok(PipelineData::ByteStream(stream, metadata)),
|
||||
}
|
||||
}
|
||||
Ok(PipelineData::ByteStream(mut stream, metadata))
|
||||
@ -176,7 +176,7 @@ impl Command for Do {
|
||||
if let ByteStreamSource::Child(child) = stream.source_mut() {
|
||||
child.ignore_error(true);
|
||||
}
|
||||
Ok(PipelineData::byte_stream(stream, metadata))
|
||||
Ok(PipelineData::ByteStream(stream, metadata))
|
||||
}
|
||||
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_all_errors => {
|
||||
Ok(PipelineData::empty())
|
||||
@ -189,7 +189,7 @@ impl Command for Do {
|
||||
value
|
||||
}
|
||||
});
|
||||
Ok(PipelineData::list_stream(stream, metadata))
|
||||
Ok(PipelineData::ListStream(stream, metadata))
|
||||
}
|
||||
r => r,
|
||||
}
|
||||
@ -264,7 +264,7 @@ fn bind_args_to(
|
||||
.expect("internal error: all custom parameters must have var_ids");
|
||||
if let Some(result) = val_iter.next() {
|
||||
let param_type = param.shape.to_type();
|
||||
if !result.is_subtype_of(¶m_type) {
|
||||
if required && !result.is_subtype_of(¶m_type) {
|
||||
return Err(ShellError::CantConvert {
|
||||
to_type: param.shape.to_type().to_string(),
|
||||
from_type: result.get_type().to_string(),
|
||||
|
@ -63,7 +63,8 @@ little reason to use this over just writing the values as-is."#
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "Returns the piped-in value, by using the special $in variable to obtain it.",
|
||||
description:
|
||||
"Returns the piped-in value, by using the special $in variable to obtain it.",
|
||||
example: "echo $in",
|
||||
result: None,
|
||||
},
|
||||
|
@ -76,7 +76,8 @@ impl Command for ErrorMake {
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a custom error for a custom command that shows the span of the argument",
|
||||
description:
|
||||
"Create a custom error for a custom command that shows the span of the argument",
|
||||
example: r#"def foo [x] {
|
||||
error make {
|
||||
msg: "this is fishy"
|
||||
@ -105,7 +106,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: throw_span,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -118,7 +119,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return ShellError::GenericError {
|
||||
@ -127,7 +128,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -145,7 +146,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
// correct return: no label
|
||||
None => {
|
||||
@ -155,7 +156,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: throw_span,
|
||||
help,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -179,7 +180,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(label_span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return ShellError::GenericError {
|
||||
@ -188,7 +189,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(label_span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -201,7 +202,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: Some(value.span()),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
// correct return: label, no span
|
||||
None => {
|
||||
@ -211,7 +212,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
span: throw_span,
|
||||
help,
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -229,7 +230,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
error: "invalid error format.".into(),
|
||||
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
|
||||
span: Some(label_span),
|
||||
help: Some(format!("{span_start} > {span_end}")),
|
||||
help: Some(format!("{} > {}", span_start, span_end)),
|
||||
inner: vec![],
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::engine::CommandType;
|
||||
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
||||
use nu_protocol::{engine::CommandType, Signals};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct For;
|
||||
@ -43,17 +43,83 @@ impl Command for For {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
eprintln!(
|
||||
"Tried to execute 'run' for the 'for' command: this code path should never be reached in IR mode"
|
||||
);
|
||||
unreachable!()
|
||||
let call = call.assert_ast_call()?;
|
||||
let head = call.head;
|
||||
let var_id = call
|
||||
.positional_nth(0)
|
||||
.expect("checked through parser")
|
||||
.as_var()
|
||||
.expect("internal error: missing variable");
|
||||
|
||||
let keyword_expr = call
|
||||
.positional_nth(1)
|
||||
.expect("checked through parser")
|
||||
.as_keyword()
|
||||
.expect("internal error: missing keyword");
|
||||
|
||||
let 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_block = get_eval_block(engine_state);
|
||||
|
||||
let value = eval_expression(engine_state, stack, keyword_expr)?;
|
||||
|
||||
let engine_state = engine_state.clone();
|
||||
let block = engine_state.get_block(block_id);
|
||||
|
||||
let stack = &mut stack.push_redirection(None, None);
|
||||
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::List { vals, .. } => {
|
||||
for x in vals.into_iter() {
|
||||
engine_state.signals().check(head)?;
|
||||
|
||||
// with_env() is used here to ensure that each iteration uses
|
||||
// a different set of environment variables.
|
||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
||||
|
||||
stack.add_var(var_id, x);
|
||||
|
||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||
Err(ShellError::Break { .. }) => break,
|
||||
Err(ShellError::Continue { .. }) => continue,
|
||||
Err(err) => return Err(err),
|
||||
Ok(data) => data.drain()?,
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Range { val, .. } => {
|
||||
for x in val.into_range_iter(span, Signals::empty()) {
|
||||
engine_state.signals().check(head)?;
|
||||
stack.add_var(var_id, x);
|
||||
|
||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||
Err(ShellError::Break { .. }) => break,
|
||||
Err(ShellError::Continue { .. }) => continue,
|
||||
Err(err) => return Err(err),
|
||||
Ok(data) => data.drain()?,
|
||||
}
|
||||
}
|
||||
}
|
||||
x => {
|
||||
stack.add_var(var_id, x);
|
||||
|
||||
eval_block(&engine_state, stack, block, PipelineData::empty())?.into_value(head)?;
|
||||
}
|
||||
}
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -1,4 +1,6 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_engine::{
|
||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{CommandType, StateWorkingSet},
|
||||
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
|
||||
@ -58,15 +60,15 @@ impl Command for If {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
let call = call.assert_ast_call()?;
|
||||
let cond = call.positional_nth(0).expect("checked through parser");
|
||||
let then_expr = call.positional_nth(1).expect("checked through parser");
|
||||
let then_block = then_expr
|
||||
let then_block = call
|
||||
.positional_nth(1)
|
||||
.expect("checked through parser")
|
||||
.as_block()
|
||||
.ok_or_else(|| ShellError::TypeMismatch {
|
||||
err_message: "expected block".into(),
|
||||
span: then_expr.span,
|
||||
})?;
|
||||
.expect("internal error: missing block");
|
||||
let else_case = call.positional_nth(2);
|
||||
|
||||
if eval_constant(working_set, cond)?.as_bool()? {
|
||||
@ -95,17 +97,43 @@ impl Command for If {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
_call: &Call,
|
||||
_input: PipelineData,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// This is compiled specially by the IR compiler. The code here is never used when
|
||||
// running in IR mode.
|
||||
eprintln!(
|
||||
"Tried to execute 'run' for the 'if' command: this code path should never be reached in IR mode"
|
||||
);
|
||||
unreachable!()
|
||||
let call = call.assert_ast_call()?;
|
||||
let cond = call.positional_nth(0).expect("checked through parser");
|
||||
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 eval_expression = get_eval_expression(engine_state);
|
||||
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
||||
let eval_block = get_eval_block(engine_state);
|
||||
|
||||
if eval_expression(engine_state, stack, cond)?.as_bool()? {
|
||||
let block = engine_state.get_block(then_block);
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
eval_expression_with_input(engine_state, stack, else_case, input)
|
||||
}
|
||||
} else {
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user