forked from extern/nushell
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370639d7d7 | ||
|
|
d326f6def6 |
@@ -12,17 +12,3 @@ rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-stati
|
|||||||
# set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB)
|
# set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB)
|
||||||
# [target.x86_64-apple-darwin]
|
# [target.x86_64-apple-darwin]
|
||||||
# rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"]
|
# rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"]
|
||||||
|
|
||||||
# How to use mold in linux and mac
|
|
||||||
|
|
||||||
# [target.x86_64-unknown-linux-gnu]
|
|
||||||
# linker = "clang"
|
|
||||||
# rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]
|
|
||||||
|
|
||||||
# [target.x86_64-apple-darwin]
|
|
||||||
# linker = "clang"
|
|
||||||
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
|
||||||
|
|
||||||
# [target.aarch64-apple-darwin]
|
|
||||||
# linker = "clang"
|
|
||||||
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -53,7 +53,7 @@ body:
|
|||||||
| features | clipboard-cli, ctrlc, dataframe, default, rustyline, term, trash, uuid, which, zip |
|
| features | clipboard-cli, ctrlc, dataframe, default, rustyline, term, trash, uuid, which, zip |
|
||||||
| installed_plugins | binaryview, chart bar, chart line, fetch, from bson, from sqlite, inc, match, post, ps, query json, s3, selector, start, sys, textview, to bson, to sqlite, tree, xpath |
|
| installed_plugins | binaryview, chart bar, chart line, fetch, from bson, from sqlite, inc, match, post, ps, query json, s3, selector, start, sys, textview, to bson, to sqlite, tree, xpath |
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: "When you want a new feature for something that doesn't already exist"
|
description: "When you want a new feature for something that doesn't already exist"
|
||||||
labels: "enhancement"
|
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
|
|||||||
21
.github/ISSUE_TEMPLATE/question.yml
vendored
21
.github/ISSUE_TEMPLATE/question.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: Question
|
|
||||||
description: "When you have a question to ask"
|
|
||||||
labels: "question"
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: problem
|
|
||||||
attributes:
|
|
||||||
label: Question
|
|
||||||
description: Leave your question here
|
|
||||||
placeholder: |
|
|
||||||
A clear and concise question
|
|
||||||
Example: Is there any equivalent of bash's $CDPATH in Nu?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional context and details
|
|
||||||
description: Add any other context, screenshots or other media that will help us understand your question here, if needed.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -1,20 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
# docs
|
|
||||||
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "cargo"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-patch"]
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
25
.github/pull_request_template.md
vendored
25
.github/pull_request_template.md
vendored
@@ -1,24 +1,17 @@
|
|||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
_(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 here)
|
||||||
|
|
||||||
_(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
|
# Tests
|
||||||
|
|
||||||
# User-Facing Changes
|
Make sure you've done the following:
|
||||||
|
|
||||||
_(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
|
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
|
||||||
|
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
|
||||||
# Tests + Formatting
|
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
|
||||||
|
|
||||||
Don't forget to add tests that cover your changes.
|
|
||||||
|
|
||||||
Make sure you've run and fixed any issues with these commands:
|
Make sure you've run and fixed any issues with these commands:
|
||||||
|
|
||||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||||
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||||
- `cargo test --workspace` to check that all tests pass
|
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|||||||
174
.github/workflows/ci.yml
vendored
174
.github/workflows/ci.yml
vendored
@@ -11,42 +11,40 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
|
|
||||||
# revisiting this when 20.04 is closer to EOL (April 2025)
|
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
|
||||||
style: [default, dataframe]
|
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
include:
|
|
||||||
- style: default
|
|
||||||
flags: ""
|
|
||||||
- style: dataframe
|
|
||||||
flags: "--features=dataframe "
|
|
||||||
exclude:
|
|
||||||
# only test dataframes on Ubuntu (the fastest platform)
|
|
||||||
- platform: windows-latest
|
|
||||||
style: dataframe
|
|
||||||
- platform: macos-latest
|
|
||||||
style: dataframe
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
env:
|
env:
|
||||||
NUSHELL_CARGO_TARGET: ci
|
NUSHELL_CARGO_TARGET: ci
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- name: cargo fmt
|
- uses: Swatinem/rust-cache@v1
|
||||||
run: cargo fmt --all -- --check
|
with:
|
||||||
|
key: "v2" # increment this to bust the cache if needed
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
nu-tests:
|
nu-tests:
|
||||||
env:
|
env:
|
||||||
@@ -55,32 +53,44 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
style: [default, dataframe]
|
style: [extra, default]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
include:
|
include:
|
||||||
|
- style: extra
|
||||||
|
flags: "--features=extra"
|
||||||
- style: default
|
- style: default
|
||||||
flags: ""
|
flags: ""
|
||||||
- style: dataframe
|
|
||||||
flags: "--features=dataframe"
|
|
||||||
exclude:
|
exclude:
|
||||||
# only test dataframes on Ubuntu (the fastest platform)
|
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
style: dataframe
|
style: default
|
||||||
- platform: macos-latest
|
- platform: macos-latest
|
||||||
style: dataframe
|
style: default
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
|
||||||
|
# Temporarily disabled; the cache was getting huge (2.6GB compressed) on Windows and causing issues.
|
||||||
|
# TODO: investigate why the cache was so big
|
||||||
|
# - uses: Swatinem/rust-cache@v1
|
||||||
|
# with:
|
||||||
|
# key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||||
|
|
||||||
python-virtualenv:
|
python-virtualenv:
|
||||||
env:
|
env:
|
||||||
@@ -89,7 +99,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ubuntu-20.04, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
py:
|
py:
|
||||||
@@ -98,33 +108,38 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
with:
|
||||||
|
key: "2" # increment this to bust the cache if needed
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
run: cargo install --locked --path=. --profile ci --no-default-features
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: --path=. --profile ci --no-default-features
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
- run: python -m pip install tox
|
- run: python -m pip install tox
|
||||||
|
|
||||||
# Get only the latest tagged version for stability reasons
|
|
||||||
- name: Install virtualenv
|
- name: Install virtualenv
|
||||||
run: git clone https://github.com/pypa/virtualenv.git && cd virtualenv && git checkout $(git describe --tags | cut -d - -f 1)
|
run: git clone https://github.com/pypa/virtualenv.git
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Test Nushell in virtualenv
|
- name: Test Nushell in virtualenv
|
||||||
run: |
|
run: cd virtualenv && tox -e ${{ matrix.py }} -- -k nushell
|
||||||
cd virtualenv
|
|
||||||
# We need to disable failing on coverage levels.
|
|
||||||
nu -c "open pyproject.toml | upsert tool.coverage.report.fail_under 1 | save patchproject.toml"
|
|
||||||
mv patchproject.toml pyproject.toml
|
|
||||||
tox -e ${{ matrix.py }} -- -k nushell
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
# Build+test plugins on their own, without the rest of Nu. This helps with CI parallelization and
|
# Build+test plugins on their own, without the rest of Nu. This helps with CI parallelization and
|
||||||
@@ -136,57 +151,30 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
matrix:
|
matrix:
|
||||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
platform: [windows-latest, macos-latest, ubuntu-latest]
|
||||||
rust:
|
rust:
|
||||||
- stable
|
- stable
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
uses: actions-rs/cargo@v1
|
||||||
|
|
||||||
- name: Tests
|
|
||||||
run: cargo test --profile ci --package nu_plugin_*
|
|
||||||
|
|
||||||
|
|
||||||
nu-coverage:
|
|
||||||
needs: nu-tests
|
|
||||||
env:
|
|
||||||
NUSHELL_CARGO_TARGET: ci
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
|
||||||
platform: [windows-latest, ubuntu-20.04]
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
|
||||||
- name: Install cargo-llvm-cov
|
|
||||||
uses: taiki-e/install-action@cargo-llvm-cov
|
|
||||||
|
|
||||||
- name: Tests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
source <(cargo llvm-cov show-env --export-prefix) # Set the environment variables needed to get coverage.
|
|
||||||
cargo llvm-cov clean --workspace # Remove artifacts that may affect the coverage results.
|
|
||||||
cargo build --lib --bins --examples --workspace --profile ci
|
|
||||||
cargo test --lib --bins --examples --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
|
||||||
cargo llvm-cov report --profile ci --lcov --output-path lcov.info
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov with GitHub Action
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
with:
|
with:
|
||||||
files: lcov.info
|
command: clippy
|
||||||
|
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --profile ci --package nu_plugin_*
|
||||||
|
|||||||
41
.github/workflows/manual.yml
vendored
41
.github/workflows/manual.yml
vendored
@@ -1,41 +0,0 @@
|
|||||||
# This is a basic workflow that is manually triggered
|
|
||||||
# Don't run it unless you know what you are doing
|
|
||||||
|
|
||||||
name: Manual Workflow for Winget Submission
|
|
||||||
|
|
||||||
# Controls when the action will run. Workflow runs when manually triggered using the UI
|
|
||||||
# or API.
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
# Inputs the workflow accepts.
|
|
||||||
inputs:
|
|
||||||
ver:
|
|
||||||
# Friendly description to be shown in the UI instead of 'ver'
|
|
||||||
description: 'The nushell version to release'
|
|
||||||
# Default value if no value is explicitly provided
|
|
||||||
default: '0.66.0'
|
|
||||||
# Input has to be provided for the workflow to run
|
|
||||||
required: true
|
|
||||||
uri:
|
|
||||||
# Friendly description to be shown in the UI instead of 'uri'
|
|
||||||
description: 'The nushell windows .msi package URI to publish'
|
|
||||||
# Default value if no value is explicitly provided
|
|
||||||
default: 'https://github.com/nushell/nushell/releases/download/0.66.0/nu-0.66.0-x86_64-pc-windows-msvc.msi'
|
|
||||||
# Input has to be provided for the workflow to run
|
|
||||||
required: true
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
jobs:
|
|
||||||
# This workflow contains a single job
|
|
||||||
rls-winget-pkg:
|
|
||||||
name: Publish winget package manually
|
|
||||||
# The type of runner that the job will run on
|
|
||||||
runs-on: windows-latest
|
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
||||||
steps:
|
|
||||||
# Runs commands using the runners shell
|
|
||||||
- name: Submit package to Windows Package Manager Community Repository Manually
|
|
||||||
run: |
|
|
||||||
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
|
|
||||||
.\wingetcreate.exe update Nushell.Nushell -s -v ${{ github.event.inputs.ver }} -u ${{ github.event.inputs.uri }} -t ${{ secrets.NUSHELL_PAT }}
|
|
||||||
90
.github/workflows/release-pkg.nu
vendored
90
.github/workflows/release-pkg.nu
vendored
@@ -6,32 +6,6 @@
|
|||||||
# REF:
|
# REF:
|
||||||
# 1. https://github.com/volks73/cargo-wix
|
# 1. https://github.com/volks73/cargo-wix
|
||||||
|
|
||||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
|
||||||
# To run this manual for windows
|
|
||||||
# unset CARGO_TARGET_DIR if set
|
|
||||||
# hide-env CARGO_TARGET_DIR
|
|
||||||
# let-env TARGET = 'x86_64-pc-windows-msvc'
|
|
||||||
# let-env TARGET_RUSTFLAGS = ''
|
|
||||||
# let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
|
|
||||||
# let-env GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
|
|
||||||
# let-env OS = 'windows-latest'
|
|
||||||
# You need to run this twice. The first pass makes the output folder and builds everything
|
|
||||||
# The second pass generates the msi file
|
|
||||||
# Pass 1 let-env _EXTRA_ = 'bin'
|
|
||||||
# Pass 2 let-env _EXTRA_ = 'msi'
|
|
||||||
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
|
|
||||||
# let-env Path = ($env.Path | append 'c:\apps\7-zip')
|
|
||||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
|
||||||
# let-env Path = ($env.Path | append 'c:\path\to\aria2c')
|
|
||||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
|
||||||
# let-env Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
|
||||||
# After msi is generated, if 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
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
# The main binary file to be released
|
# The main binary file to be released
|
||||||
let bin = 'nu'
|
let bin = 'nu'
|
||||||
let os = $env.OS
|
let os = $env.OS
|
||||||
@@ -42,13 +16,8 @@ let flags = $env.TARGET_RUSTFLAGS
|
|||||||
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
let dist = $'($env.GITHUB_WORKSPACE)/output'
|
||||||
let version = (open Cargo.toml | get package.version)
|
let version = (open Cargo.toml | get package.version)
|
||||||
|
|
||||||
$'Debugging info:'
|
|
||||||
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
|
|
||||||
|
|
||||||
# $env
|
# $env
|
||||||
|
|
||||||
let USE_UBUNTU = 'ubuntu-20.04'
|
|
||||||
|
|
||||||
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
|
||||||
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
|
||||||
|
|
||||||
@@ -57,9 +26,8 @@ $'Start building ($bin)...'; hr-line
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Ubuntu and macOS
|
# Build for Ubuntu and macOS
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in ['ubuntu-latest', 'macos-latest'] {
|
||||||
if $os == $USE_UBUNTU {
|
if $os == 'ubuntu-latest' {
|
||||||
sudo apt update
|
|
||||||
sudo apt-get install libxcb-composite0-dev -y
|
sudo apt-get install libxcb-composite0-dev -y
|
||||||
}
|
}
|
||||||
if $target == 'aarch64-unknown-linux-gnu' {
|
if $target == 'aarch64-unknown-linux-gnu' {
|
||||||
@@ -70,14 +38,10 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
|
||||||
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
|
||||||
cargo-build-nu $flags
|
cargo-build-nu $flags
|
||||||
} else if $target == 'riscv64gc-unknown-linux-gnu' {
|
|
||||||
sudo apt-get install gcc-riscv64-linux-gnu -y
|
|
||||||
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
|
|
||||||
cargo-build-nu $flags
|
|
||||||
} else {
|
} else {
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
sudo apt install musl-tools -y
|
||||||
cargo-build-nu $flags
|
cargo-build-nu $flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,10 +50,10 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
# Build for Windows without static-link-openssl feature
|
# Build for Windows without static-link-openssl feature
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in ['windows-latest'] {
|
if $os in ['windows-latest'] {
|
||||||
if ($flags | str trim | is-empty) {
|
if ($flags | str trim | empty?) {
|
||||||
cargo build --release --all --target $target
|
cargo build --release --all --target $target --features=extra
|
||||||
} else {
|
} else {
|
||||||
cargo build --release --all --target $target $flags
|
cargo build --release --all --target $target --features=extra $flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +76,11 @@ cp -v README.release.txt $'($dist)/README.txt'
|
|||||||
|
|
||||||
$'(char nl)Check binary release version detail:'; hr-line
|
$'(char nl)Check binary release version detail:'; hr-line
|
||||||
let ver = if $os == 'windows-latest' {
|
let ver = if $os == 'windows-latest' {
|
||||||
(do -i { ./output/nu.exe -c 'version' }) | str join
|
(do -i { ./output/nu.exe -c 'version' }) | str collect
|
||||||
} else {
|
} else {
|
||||||
(do -i { ./output/nu -c 'version' }) | str join
|
(do -i { ./output/nu -c 'version' }) | str collect
|
||||||
}
|
}
|
||||||
if ($ver | str trim | is-empty) {
|
if ($ver | str trim | empty?) {
|
||||||
$'(ansi r)Incompatible nu binary...(ansi reset)'
|
$'(ansi r)Incompatible nu binary...(ansi reset)'
|
||||||
} else { $ver }
|
} else { $ver }
|
||||||
|
|
||||||
@@ -124,28 +88,21 @@ if ($ver | str trim | is-empty) {
|
|||||||
# Create a release archive and send it to output for the following steps
|
# Create a release archive and send it to output for the following steps
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in ['ubuntu-latest', 'macos-latest'] {
|
||||||
|
|
||||||
let files = (ls | get name)
|
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
||||||
let dest = $'($bin)-($version)-($target)'
|
|
||||||
let archive = $'($dist)/($dest).tar.gz'
|
|
||||||
|
|
||||||
mkdir $dest
|
let archive = $'($dist)/($bin)-($version)-($target).tar.gz'
|
||||||
$files | each {|it| mv $it $dest } | ignore
|
tar czf $archive *
|
||||||
|
|
||||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
|
||||||
|
|
||||||
tar -czf $archive $dest
|
|
||||||
print $'archive: ---> ($archive)'; ls $archive
|
print $'archive: ---> ($archive)'; ls $archive
|
||||||
# REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
|
echo $'::set-output name=archive::($archive)'
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
|
||||||
|
|
||||||
} else if $os == 'windows-latest' {
|
} else if $os == 'windows-latest' {
|
||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
$'(char nl)Download less related stuffs...'; hr-line
|
$'(char nl)Download less related stuffs...'; hr-line
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe
|
||||||
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
|
||||||
# Create Windows msi release package
|
# Create Windows msi release package
|
||||||
@@ -156,10 +113,9 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
cd $src; hr-line
|
cd $src; hr-line
|
||||||
# Wix need the binaries be stored in target/release/
|
# Wix need the binaries be stored in target/release/
|
||||||
cp -r $'($dist)/*' target/release/
|
cp -r $'($dist)/*' target/release/
|
||||||
cargo install cargo-wix --version 0.3.4
|
cargo install cargo-wix --version 0.3.2
|
||||||
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
||||||
print $'archive: ---> ($wixRelease)';
|
echo $'::set-output name=archive::($wixRelease)'
|
||||||
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -168,17 +124,17 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
7z a $archive *
|
7z a $archive *
|
||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)';
|
||||||
let pkg = (ls -f $archive | get name)
|
let pkg = (ls -f $archive | get name)
|
||||||
if not ($pkg | is-empty) {
|
if not ($pkg | empty?) {
|
||||||
echo $"archive=($pkg | get 0)" | save --append $env.GITHUB_OUTPUT
|
echo $'::set-output name=archive::($pkg | get 0)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def 'cargo-build-nu' [ options: string ] {
|
def 'cargo-build-nu' [ options: string ] {
|
||||||
if ($options | str trim | is-empty) {
|
if ($options | str trim | empty?) {
|
||||||
cargo build --release --all --target $target --features=static-link-openssl
|
cargo build --release --all --target $target --features=extra,static-link-openssl
|
||||||
} else {
|
} else {
|
||||||
cargo build --release --all --target $target --features=static-link-openssl $options
|
cargo build --release --all --target $target --features=extra,static-link-openssl $options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +143,7 @@ def 'hr-line' [
|
|||||||
--blank-line(-b): bool
|
--blank-line(-b): bool
|
||||||
] {
|
] {
|
||||||
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
|
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
|
||||||
if $blank_line { char nl }
|
if $blank-line { char nl }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get the specified env key's value or ''
|
# Get the specified env key's value or ''
|
||||||
|
|||||||
33
.github/workflows/release.yml
vendored
33
.github/workflows/release.yml
vendored
@@ -27,7 +27,6 @@ jobs:
|
|||||||
- x86_64-unknown-linux-musl
|
- x86_64-unknown-linux-musl
|
||||||
- aarch64-unknown-linux-gnu
|
- aarch64-unknown-linux-gnu
|
||||||
- armv7-unknown-linux-gnueabihf
|
- armv7-unknown-linux-gnueabihf
|
||||||
- riscv64gc-unknown-linux-gnu
|
|
||||||
extra: ['bin']
|
extra: ['bin']
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
@@ -45,37 +44,35 @@ jobs:
|
|||||||
os: windows-latest
|
os: windows-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
|
||||||
os: ubuntu-20.04
|
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.1.0
|
- uses: actions/checkout@v3.0.2
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Install Rust Toolchain Components
|
||||||
run: |
|
uses: actions-rs/toolchain@v1.0.6
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
with:
|
||||||
|
override: true
|
||||||
- name: Setup Rust toolchain and cache
|
profile: minimal
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.4.2
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v1
|
||||||
with:
|
with:
|
||||||
version: 0.72.1
|
version: 0.63.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -91,7 +88,7 @@ jobs:
|
|||||||
|
|
||||||
# REF: https://github.com/marketplace/actions/gh-release
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
- name: Publish Archive
|
- name: Publish Archive
|
||||||
uses: softprops/action-gh-release@v0.1.13
|
uses: softprops/action-gh-release@v1
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
|
|||||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v6
|
- uses: actions/stale@v3
|
||||||
with:
|
with:
|
||||||
#debug-only: true
|
#debug-only: true
|
||||||
ascending: true
|
ascending: true
|
||||||
|
|||||||
13
.github/workflows/typos.yml
vendored
13
.github/workflows/typos.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: Typos
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
name: Spell Check with Typos
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Actions Repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Check spelling
|
|
||||||
uses: crate-ci/typos@master
|
|
||||||
2
.github/workflows/winget-submission.yml
vendored
2
.github/workflows/winget-submission.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Submit package to Windows Package Manager Community Repository
|
- name: Submit package to Windows Package Manager Community Repository
|
||||||
run: |
|
run: |
|
||||||
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
|
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||||
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
|
||||||
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.msi' | Select -ExpandProperty browser_download_url -First 1
|
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.msi' | Select -ExpandProperty browser_download_url -First 1
|
||||||
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}
|
||||||
|
|||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -22,10 +22,6 @@ debian/nu/
|
|||||||
# VSCode's IDE items
|
# VSCode's IDE items
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
# Visual Studio Extension SourceGear Rust items
|
|
||||||
VSWorkspaceSettings.json
|
|
||||||
unstable_cargo_features.txt
|
|
||||||
|
|
||||||
# Helix configuration folder
|
# Helix configuration folder
|
||||||
.helix/*
|
.helix/*
|
||||||
.helix
|
.helix
|
||||||
@@ -33,9 +29,3 @@ unstable_cargo_features.txt
|
|||||||
# Coverage tools
|
# Coverage tools
|
||||||
lcov.info
|
lcov.info
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
|
|
||||||
# Visual Studio
|
|
||||||
.vs/*
|
|
||||||
*.rsproj
|
|
||||||
*.rsproj.user
|
|
||||||
*.sln
|
|
||||||
12
.typos.toml
12
.typos.toml
@@ -1,12 +0,0 @@
|
|||||||
[files]
|
|
||||||
extend-exclude = ["crates/nu-command/tests/commands/table.rs", "*.tsv", "*.json", "*.txt"]
|
|
||||||
|
|
||||||
[default.extend-words]
|
|
||||||
# Ignore false-positives
|
|
||||||
nd = "nd"
|
|
||||||
fo = "fo"
|
|
||||||
ons = "ons"
|
|
||||||
ba = "ba"
|
|
||||||
Plasticos = "Plasticos"
|
|
||||||
IIF = "IIF"
|
|
||||||
numer = "numer"
|
|
||||||
@@ -1,23 +1,8 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Welcome to Nushell and thank you for considering contributing!
|
Welcome to Nushell!
|
||||||
|
|
||||||
## Review Process
|
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)!
|
||||||
|
|
||||||
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
|
|
||||||
This saves both your and our time if we realize the change needs to go another direction before spending time on it.
|
|
||||||
So, please, reach out and tell us what you want to do.
|
|
||||||
This will significantly increase the chance of your PR being accepted.
|
|
||||||
|
|
||||||
The review process can be summarized as follows:
|
|
||||||
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
|
||||||
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
|
||||||
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
|
||||||
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
|
||||||
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
|
||||||
6. When we all agree, the PR will be merged.
|
|
||||||
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
|
||||||
8. Congratulate yourself, you just improved Nushell! :-)
|
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
@@ -31,18 +16,6 @@ cd nushell
|
|||||||
cargo build
|
cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
It is a good practice to cover your changes with a test. Also, try to think about corner cases and various ways how your changes could break. Cover those in the tests as well.
|
|
||||||
|
|
||||||
Tests can be found in different places:
|
|
||||||
* `/tests`
|
|
||||||
* `src/tests`
|
|
||||||
* command examples
|
|
||||||
* crate-specific tests
|
|
||||||
|
|
||||||
The most comprehensive test suite we have is the `nu-test-support` crate. For testing specific features, such as running Nushell in a REPL mode, we have so called "testbins". For simple tests, you can find `run_test()` and `fail_test()` functions.
|
|
||||||
|
|
||||||
### Useful Commands
|
### Useful Commands
|
||||||
|
|
||||||
- Build and run Nushell:
|
- Build and run Nushell:
|
||||||
@@ -51,21 +24,21 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
- Build and run with dataframe support.
|
- Build and run with extra features. Currently extra features include dataframes and sqlite database support.
|
||||||
```shell
|
```shell
|
||||||
cargo run --features=dataframe
|
cargo run --features=extra
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run Clippy on Nushell:
|
- Run Clippy on Nushell:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests:
|
- Run all tests:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo test --workspace
|
cargo test --workspace --features=extra
|
||||||
```
|
```
|
||||||
|
|
||||||
- Run all tests for a specific command
|
- Run all tests for a specific command
|
||||||
@@ -91,11 +64,5 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
|
|||||||
- To view verbose logs when developing, enable the `trace` log level.
|
- To view verbose logs when developing, enable the `trace` log level.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo run --release -- --log-level trace
|
cargo run --release --features=extra -- --log-level trace
|
||||||
```
|
|
||||||
|
|
||||||
- To redirect trace logs to a file, enable the `--log-target file` switch.
|
|
||||||
```shell
|
|
||||||
cargo run --release -- --log-level trace --log-target file
|
|
||||||
open $"($nu.temp-path)/nu-($nu.pid).log"
|
|
||||||
```
|
```
|
||||||
|
|||||||
2267
Cargo.lock
generated
2267
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
119
Cargo.toml
119
Cargo.toml
@@ -3,24 +3,18 @@ authors = ["The Nushell Project Developers"]
|
|||||||
default-run = "nu"
|
default-run = "nu"
|
||||||
description = "A new type of shell"
|
description = "A new type of shell"
|
||||||
documentation = "https://www.nushell.sh/book/"
|
documentation = "https://www.nushell.sh/book/"
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
exclude = ["images"]
|
exclude = ["images"]
|
||||||
homepage = "https://www.nushell.sh"
|
homepage = "https://www.nushell.sh"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu"
|
name = "nu"
|
||||||
|
readme = "README.md"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.76.0"
|
version = "0.66.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[package.metadata.binstall]
|
|
||||||
pkg-url = "{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }"
|
|
||||||
pkg-fmt = "tgz"
|
|
||||||
|
|
||||||
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
|
|
||||||
pkg-fmt = "zip"
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/nu-cli",
|
"crates/nu-cli",
|
||||||
@@ -35,83 +29,59 @@ members = [
|
|||||||
"crates/nu_plugin_example",
|
"crates/nu_plugin_example",
|
||||||
"crates/nu_plugin_query",
|
"crates/nu_plugin_query",
|
||||||
"crates/nu_plugin_custom_values",
|
"crates/nu_plugin_custom_values",
|
||||||
"crates/nu_plugin_formats",
|
|
||||||
"crates/nu-utils",
|
"crates/nu-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.23", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.23.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
miette = "5.1.0"
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.76.0" }
|
nu-cli = { path="./crates/nu-cli", version = "0.66.1" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.76.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.66.1" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.76.0" }
|
nu-command = { path="./crates/nu-command", version = "0.66.1" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.76.0" }
|
nu-engine = { path="./crates/nu-engine", version = "0.66.1" }
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.76.0" }
|
nu-json = { path="./crates/nu-json", version = "0.66.1" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.76.0" }
|
nu-parser = { path="./crates/nu-parser", version = "0.66.1" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.76.0" }
|
nu-path = { path="./crates/nu-path", version = "0.66.1" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.76.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.66.1" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.76.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.66.1" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.76.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.66.1" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.76.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.66.1" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.76.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.66.1" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.76.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.66.1" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.76.0" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.66.1" }
|
||||||
|
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
|
||||||
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
pretty_env_logger = "0.4.0"
|
||||||
|
rayon = "1.5.1"
|
||||||
rayon = "1.6.1"
|
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
simplelog = "0.12.0"
|
|
||||||
time = "0.3.12"
|
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||||
# Our dependencies don't use OpenSSL on Windows
|
# Our dependencies don't use OpenSSL on Windows
|
||||||
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
|
||||||
signal-hook = { version = "0.3.14", default-features = false }
|
signal-hook = { version = "0.3.14", default-features = false }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nu-test-support = { path="./crates/nu-test-support", version = "0.66.1" }
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
assert_cmd = "2.0.2"
|
||||||
|
pretty_assertions = "1.0.0"
|
||||||
|
serial_test = "0.8.0"
|
||||||
|
hamcrest2 = "0.3.0"
|
||||||
|
rstest = "0.15.0"
|
||||||
|
itertools = "0.10.3"
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
|
||||||
nix = { version = "0.25", default-features = false, features = [
|
|
||||||
"signal",
|
|
||||||
"process",
|
|
||||||
"fs",
|
|
||||||
"term",
|
|
||||||
] }
|
|
||||||
atty = "0.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.76.0" }
|
|
||||||
tempfile = "3.2.0"
|
|
||||||
assert_cmd = "2.0.2"
|
|
||||||
criterion = "0.4"
|
|
||||||
pretty_assertions = "1.0.0"
|
|
||||||
serial_test = "1.0.0"
|
|
||||||
hamcrest2 = "0.3.0"
|
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
|
||||||
itertools = "0.10.3"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = [
|
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
||||||
"nu-plugin",
|
default = ["plugin", "which-support", "trash-support"]
|
||||||
"nu-cli/plugin",
|
|
||||||
"nu-parser/plugin",
|
|
||||||
"nu-command/plugin",
|
|
||||||
"nu-protocol/plugin",
|
|
||||||
"nu-engine/plugin",
|
|
||||||
]
|
|
||||||
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
|
|
||||||
extra = ["default"]
|
|
||||||
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
|
||||||
stable = ["default"]
|
stable = ["default"]
|
||||||
|
extra = ["default", "dataframe", "database"]
|
||||||
wasi = []
|
wasi = []
|
||||||
|
|
||||||
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||||
static-link-openssl = ["dep:openssl"]
|
static-link-openssl = ["dep:openssl"]
|
||||||
|
|
||||||
@@ -124,11 +94,11 @@ trash-support = ["nu-command/trash-support"]
|
|||||||
# Dataframe feature for nushell
|
# Dataframe feature for nushell
|
||||||
dataframe = ["nu-command/dataframe"]
|
dataframe = ["nu-command/dataframe"]
|
||||||
|
|
||||||
# SQLite commands for nushell
|
# Database commands for nushell
|
||||||
sqlite = ["nu-command/sqlite"]
|
database = ["nu-command/database"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # Optimize for size
|
opt-level = "s" # Optimize for size
|
||||||
strip = "debuginfo"
|
strip = "debuginfo"
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|
||||||
@@ -150,16 +120,3 @@ debug = false
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
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.git", branch = "main" }
|
|
||||||
|
|
||||||
# Criterion benchmarking setup
|
|
||||||
# Run all benchmarks with `cargo bench`
|
|
||||||
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
|
||||||
[[bench]]
|
|
||||||
name = "benchmarks"
|
|
||||||
harness = false
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# Configuration for cross-rs: https://github.com/cross-rs/cross
|
|
||||||
# Run cross-rs like this:
|
|
||||||
# cross build --target aarch64-unknown-linux-musl --release
|
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-gnu]
|
|
||||||
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-gnu.dockerfile"
|
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-musl]
|
|
||||||
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-musl.dockerfile"
|
|
||||||
24
README.md
24
README.md
@@ -1,12 +1,11 @@
|
|||||||
# Nushell <!-- omit in toc -->
|
# Nushell <!-- omit in toc -->
|
||||||
[](https://crates.io/crates/nu)
|
[](https://crates.io/crates/nu)
|
||||||

|

|
||||||
[](https://discord.gg/NtAbbGn)
|
[](https://discord.gg/NtAbbGn)
|
||||||
[](https://changelog.com/podcast/363)
|
[](https://changelog.com/podcast/363)
|
||||||
[](https://twitter.com/nu_shell)
|
[](https://twitter.com/nu_shell)
|
||||||

|

|
||||||

|

|
||||||
[](https://codecov.io/github/nushell/nushell)
|
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ This project has reached a minimum-viable-product level of quality. Many people
|
|||||||
|
|
||||||
## Learning About Nu
|
## Learning About Nu
|
||||||
|
|
||||||
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/commands/), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/book/command_reference.html), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
||||||
|
|
||||||
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ brew install nushell
|
|||||||
winget install nushell
|
winget install nushell
|
||||||
```
|
```
|
||||||
|
|
||||||
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
To use `Nu` in Github Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
|
||||||
|
|
||||||
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ Additionally, commands can output structured data (you can think of this as a th
|
|||||||
Commands that work in the pipeline fit into one of three categories:
|
Commands that work in the pipeline fit into one of three categories:
|
||||||
|
|
||||||
- Commands that produce a stream (e.g., `ls`)
|
- Commands that produce a stream (e.g., `ls`)
|
||||||
- Commands that filter a stream (e.g., `where type == "dir"`)
|
- Commands that filter a stream (eg, `where type == "dir"`)
|
||||||
- Commands that consume the output of the pipeline (e.g., `table`)
|
- Commands that consume the output of the pipeline (e.g., `table`)
|
||||||
|
|
||||||
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
||||||
@@ -127,13 +126,12 @@ For example, you can load a .toml file as structured data and explore it:
|
|||||||
> open Cargo.toml
|
> open Cargo.toml
|
||||||
╭──────────────────┬────────────────────╮
|
╭──────────────────┬────────────────────╮
|
||||||
│ bin │ [table 1 row] │
|
│ bin │ [table 1 row] │
|
||||||
│ dependencies │ {record 25 fields} │
|
│ dependencies │ {record 24 fields} │
|
||||||
│ dev-dependencies │ {record 8 fields} │
|
│ dev-dependencies │ {record 8 fields} │
|
||||||
│ features │ {record 10 fields} │
|
│ features │ {record 10 fields} │
|
||||||
│ package │ {record 13 fields} │
|
│ package │ {record 13 fields} │
|
||||||
│ patch │ {record 1 field} │
|
|
||||||
│ profile │ {record 3 fields} │
|
│ profile │ {record 3 fields} │
|
||||||
│ target │ {record 3 fields} │
|
│ target │ {record 2 fields} │
|
||||||
│ workspace │ {record 1 field} │
|
│ workspace │ {record 1 field} │
|
||||||
╰──────────────────┴────────────────────╯
|
╰──────────────────┴────────────────────╯
|
||||||
```
|
```
|
||||||
@@ -151,11 +149,11 @@ We can pipe this into a command that gets the contents of one of the columns:
|
|||||||
│ exclude │ [list 1 item] │
|
│ exclude │ [list 1 item] │
|
||||||
│ homepage │ https://www.nushell.sh │
|
│ homepage │ https://www.nushell.sh │
|
||||||
│ license │ MIT │
|
│ license │ MIT │
|
||||||
│ metadata │ {record 1 field} │
|
|
||||||
│ name │ nu │
|
│ name │ nu │
|
||||||
|
│ readme │ README.md │
|
||||||
│ repository │ https://github.com/nushell/nushell │
|
│ repository │ https://github.com/nushell/nushell │
|
||||||
│ rust-version │ 1.60 │
|
│ rust-version │ 1.60 │
|
||||||
│ version │ 0.72.0 │
|
│ version │ 0.63.1 │
|
||||||
╰───────────────┴────────────────────────────────────╯
|
╰───────────────┴────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -163,7 +161,7 @@ And if needed we can drill down further:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml | get package.version
|
> open Cargo.toml | get package.version
|
||||||
0.72.0
|
0.63.1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Plugins
|
### Plugins
|
||||||
@@ -175,8 +173,6 @@ These binaries interact with nu via a simple JSON-RPC protocol where the command
|
|||||||
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
|
||||||
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
|
||||||
|
|
||||||
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins.
|
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
||||||
@@ -210,7 +206,7 @@ Nu is under heavy development and will naturally change as it matures. The chart
|
|||||||
| Functions | | | | X | | Functions and aliases are supported |
|
| Functions | | | | X | | Functions and aliases are supported |
|
||||||
| Variables | | | | X | | Nu supports variables and environment variables |
|
| Variables | | | | X | | Nu supports variables and environment variables |
|
||||||
| Completions | | | | X | | Completions for filepaths |
|
| Completions | | | | X | | Completions for filepaths |
|
||||||
| Type-checking | | | | x | | Commands check basic types, and input/output types |
|
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked |
|
||||||
|
|
||||||
## Officially Supported By
|
## Officially Supported By
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
|
||||||
|
|
||||||
> register ./nu_plugin_query
|
> register -e json ./nu_plugin_query
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
# Criterion benchmarks
|
|
||||||
|
|
||||||
These are benchmarks using [Criterion](https://github.com/bheisler/criterion.rs), a microbenchmarking tool for Rust.
|
|
||||||
|
|
||||||
Run all benchmarks with `cargo bench`
|
|
||||||
|
|
||||||
Or run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
|
||||||
use nu_cli::eval_source;
|
|
||||||
use nu_parser::parse;
|
|
||||||
use nu_plugin::{EncodingType, PluginResponse};
|
|
||||||
use nu_protocol::{PipelineData, Span, Value};
|
|
||||||
use nu_utils::{get_default_config, get_default_env};
|
|
||||||
|
|
||||||
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.
|
|
||||||
// When the *_benchmarks functions were in different files, `cargo bench` would build
|
|
||||||
// an executable for every single one - incredibly slowly. Would be nice to figure out
|
|
||||||
// a way to split things up again.
|
|
||||||
|
|
||||||
fn parser_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let default_env = get_default_env().as_bytes();
|
|
||||||
c.bench_function("parse_default_env_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let default_config = get_default_config().as_bytes();
|
|
||||||
c.bench_function("parse_default_config_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_env.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_env().as_bytes(),
|
|
||||||
"default_env.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_config.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_config().as_bytes(),
|
|
||||||
"default_config.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_benchmarks(c: &mut Criterion) {
|
|
||||||
c.bench_function("eval default_env.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_env().as_bytes(),
|
|
||||||
"default_env.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_config.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_config().as_bytes(),
|
|
||||||
"default_config.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
|
|
||||||
fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
|
||||||
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
|
|
||||||
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
|
|
||||||
|
|
||||||
Value::List {
|
|
||||||
vals: (0..row_cnt)
|
|
||||||
.map(|_| Value::test_record(columns.clone(), vals.clone()))
|
|
||||||
.collect(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encoding_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Encoding");
|
|
||||||
let test_cnt_pairs = [
|
|
||||||
(100, 5),
|
|
||||||
(100, 10),
|
|
||||||
(100, 15),
|
|
||||||
(1000, 5),
|
|
||||||
(1000, 10),
|
|
||||||
(1000, 15),
|
|
||||||
(10000, 5),
|
|
||||||
(10000, 10),
|
|
||||||
(10000, 15),
|
|
||||||
];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} encode {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data =
|
|
||||||
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
b.iter(|| encoder.encode_response(&test_data, &mut res))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decoding_benchmarks(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Decoding");
|
|
||||||
let test_cnt_pairs = [
|
|
||||||
(100, 5),
|
|
||||||
(100, 10),
|
|
||||||
(100, 15),
|
|
||||||
(1000, 5),
|
|
||||||
(1000, 10),
|
|
||||||
(1000, 15),
|
|
||||||
(10000, 5),
|
|
||||||
(10000, 10),
|
|
||||||
(10000, 15),
|
|
||||||
];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} decode for {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data =
|
|
||||||
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
encoder.encode_response(&test_data, &mut res).unwrap();
|
|
||||||
let mut binary_data = std::io::Cursor::new(res);
|
|
||||||
b.iter(|| {
|
|
||||||
binary_data.set_position(0);
|
|
||||||
encoder.decode_response(&mut binary_data)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
parser_benchmarks,
|
|
||||||
eval_benchmarks,
|
|
||||||
encoding_benchmarks,
|
|
||||||
decoding_benchmarks
|
|
||||||
);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/sh
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
echo "Building nushell (nu) with dataframes and all the plugins"
|
echo "Building nushell (nu) with --features=extra and all the plugins"
|
||||||
echo "---------------------------------------------------------------"
|
echo "---------------------------------------------------------------"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@@ -11,14 +10,13 @@ NU_PLUGINS=(
|
|||||||
'nu_plugin_gstat'
|
'nu_plugin_gstat'
|
||||||
'nu_plugin_inc'
|
'nu_plugin_inc'
|
||||||
'nu_plugin_query'
|
'nu_plugin_query'
|
||||||
'nu_plugin_custom_values'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
echo "Building nushell"
|
echo "Building nushell"
|
||||||
cargo build --features=dataframe
|
cargo build --features=extra
|
||||||
for plugin in "${NU_PLUGINS[@]}"
|
for plugin in "${NU_PLUGINS[@]}"
|
||||||
do
|
do
|
||||||
echo '' && cd crates/"$plugin"
|
echo '' && cd crates/$plugin
|
||||||
echo "Building $plugin..."
|
echo "Building $plugin..."
|
||||||
echo "-----------------------------"
|
echo "-----------------------------"
|
||||||
cargo build && cd ../..
|
cargo build && cd ../..
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
@echo off
|
@echo off
|
||||||
@echo -------------------------------------------------------------------
|
@echo -------------------------------------------------------------------
|
||||||
@echo Building nushell (nu.exe) with dataframes and all the plugins
|
@echo Building nushell (nu.exe) with --features=extra and all the plugins
|
||||||
@echo -------------------------------------------------------------------
|
@echo -------------------------------------------------------------------
|
||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
echo Building nushell.exe
|
echo Building nushell.exe
|
||||||
cargo build --features=dataframe
|
cargo build --features=extra
|
||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
@cd crates\nu_plugin_example
|
@cd crates\nu_plugin_example
|
||||||
@@ -24,13 +24,9 @@ cargo build
|
|||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
@cd ..\..\crates\nu_plugin_query
|
@cd ..\..\crates\nu_plugin_query
|
||||||
|
|
||||||
echo Building nu_plugin_query.exe
|
echo Building nu_plugin_query.exe
|
||||||
cargo build
|
cargo build
|
||||||
@echo.
|
@echo.
|
||||||
|
|
||||||
@cd ..\..\crates\nu_plugin_custom_values
|
|
||||||
echo Building nu_plugin_custom_values.exe
|
|
||||||
cargo build
|
|
||||||
@echo.
|
|
||||||
|
|
||||||
@cd ..\..
|
@cd ..\..
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
echo '-------------------------------------------------------------------'
|
echo '-------------------------------------------------------------------'
|
||||||
echo 'Building nushell (nu) with dataframes and all the plugins'
|
echo 'Building nushell (nu) with --features=extra and all the plugins'
|
||||||
echo '-------------------------------------------------------------------'
|
echo '-------------------------------------------------------------------'
|
||||||
|
|
||||||
echo $'(char nl)Building nushell'
|
echo $'(char nl)Building nushell'
|
||||||
echo '----------------------------'
|
echo '----------------------------'
|
||||||
cargo build --features=dataframe
|
cargo build --features=extra
|
||||||
|
|
||||||
let plugins = [
|
let plugins = [
|
||||||
nu_plugin_inc,
|
nu_plugin_inc,
|
||||||
nu_plugin_gstat,
|
nu_plugin_gstat,
|
||||||
nu_plugin_query,
|
nu_plugin_query,
|
||||||
nu_plugin_example,
|
nu_plugin_example,
|
||||||
nu_plugin_custom_values,
|
|
||||||
nu_plugin_formats,
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for plugin in $plugins {
|
for plugin in $plugins {
|
||||||
|
|||||||
17
codecov.yml
17
codecov.yml
@@ -1,17 +0,0 @@
|
|||||||
coverage:
|
|
||||||
status:
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
target: 55%
|
|
||||||
threshold: 2%
|
|
||||||
patch:
|
|
||||||
default:
|
|
||||||
informational: true
|
|
||||||
|
|
||||||
comment:
|
|
||||||
layout: reach, diff, files
|
|
||||||
behavior: default
|
|
||||||
require_base: yes
|
|
||||||
require_head: yes
|
|
||||||
after_n_builds: 2
|
|
||||||
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
|
|
||||||
let start = (date now)
|
|
||||||
# Script to generate coverage locally
|
|
||||||
#
|
|
||||||
# Output: `lcov.info` file
|
|
||||||
#
|
|
||||||
# Relies on `cargo-llvm-cov`. Install via `cargo install cargo-llvm-cov`
|
|
||||||
# https://github.com/taiki-e/cargo-llvm-cov
|
|
||||||
|
|
||||||
# You probably have to run `cargo llvm-cov clean` once manually,
|
|
||||||
# as you have to confirm to install additional tooling for your rustup toolchain.
|
|
||||||
# Else the script might stall waiting for your `y<ENTER>`
|
|
||||||
|
|
||||||
# Some of the internal tests rely on the exact cargo profile
|
|
||||||
# (This is somewhat criminal itself)
|
|
||||||
# but we have to signal to the tests that we use the `ci` `--profile`
|
|
||||||
let-env NUSHELL_CARGO_TARGET = "ci"
|
|
||||||
|
|
||||||
# Manual gathering of coverage to catch invocation of the `nu` binary.
|
|
||||||
# This is relevant for tests using the `nu!` macro from `nu-test-support`
|
|
||||||
# see: https://github.com/taiki-e/cargo-llvm-cov#get-coverage-of-external-tests
|
|
||||||
|
|
||||||
print "Setting up environment variables for coverage"
|
|
||||||
# Enable LLVM coverage tracking through environment variables
|
|
||||||
# show env outputs .ini/.toml style description of the variables
|
|
||||||
# In order to use from toml, we need to make sure our string literals are single quoted
|
|
||||||
# This is especially important when running on Windows since "C:\blah" is treated as an escape
|
|
||||||
cargo llvm-cov show-env | str replace (char dq) (char sq) -a | from toml | load-env
|
|
||||||
|
|
||||||
print "Cleaning up coverage data"
|
|
||||||
cargo llvm-cov clean --workspace
|
|
||||||
|
|
||||||
print "Building with workspace and profile=ci"
|
|
||||||
# Apparently we need to explicitly build the necessary parts
|
|
||||||
# using the `--profile=ci` is basically `debug` build with unnecessary symbols stripped
|
|
||||||
# leads to smaller binaries and potential savings when compiling and running
|
|
||||||
cargo build --workspace --profile=ci
|
|
||||||
|
|
||||||
print "Running tests with --workspace and profile=ci"
|
|
||||||
cargo test --workspace --profile=ci
|
|
||||||
|
|
||||||
# You need to provide the used profile to find the raw data
|
|
||||||
print "Generating coverage report as lcov.info"
|
|
||||||
cargo llvm-cov report --lcov --output-path lcov.info --profile=ci
|
|
||||||
|
|
||||||
let end = (date now)
|
|
||||||
$"Coverage generation took ($end - $start)."
|
|
||||||
|
|
||||||
# To display the coverage in your editor see:
|
|
||||||
#
|
|
||||||
# - https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters
|
|
||||||
# - https://github.com/umaumax/vim-lcov
|
|
||||||
# - https://github.com/andythigpen/nvim-coverage (probably needs some additional config)
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Script to generate coverage locally
|
|
||||||
#
|
|
||||||
# Output: `lcov.info` file
|
|
||||||
#
|
|
||||||
# Relies on `cargo-llvm-cov`. Install via `cargo install cargo-llvm-cov`
|
|
||||||
# https://github.com/taiki-e/cargo-llvm-cov
|
|
||||||
|
|
||||||
# You probably have to run `cargo llvm-cov clean` once manually,
|
|
||||||
# as you have to confirm to install additional tooling for your rustup toolchain.
|
|
||||||
# Else the script might stall waiting for your `y<ENTER>`
|
|
||||||
|
|
||||||
# Some of the internal tests rely on the exact cargo profile
|
|
||||||
# (This is somewhat criminal itself)
|
|
||||||
# but we have to signal to the tests that we use the `ci` `--profile`
|
|
||||||
export NUSHELL_CARGO_TARGET=ci
|
|
||||||
|
|
||||||
# Manual gathering of coverage to catch invocation of the `nu` binary.
|
|
||||||
# This is relevant for tests using the `nu!` macro from `nu-test-support`
|
|
||||||
# see: https://github.com/taiki-e/cargo-llvm-cov#get-coverage-of-external-tests
|
|
||||||
|
|
||||||
# Enable LLVM coverage tracking through environment variables
|
|
||||||
source <(cargo llvm-cov show-env --export-prefix)
|
|
||||||
cargo llvm-cov clean --workspace
|
|
||||||
# Apparently we need to explicitly build the necessary parts
|
|
||||||
# using the `--profile=ci` is basically `debug` build with unnecessary symbols stripped
|
|
||||||
# leads to smaller binaries and potential savings when compiling and running
|
|
||||||
cargo build --workspace --profile=ci
|
|
||||||
cargo test --workspace --profile=ci
|
|
||||||
# You need to provide the used profile to find the raw data
|
|
||||||
cargo llvm-cov report --lcov --output-path lcov.info --profile=ci
|
|
||||||
|
|
||||||
# To display the coverage in your editor see:
|
|
||||||
#
|
|
||||||
# - https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters
|
|
||||||
# - https://github.com/umaumax/vim-lcov
|
|
||||||
# - https://github.com/andythigpen/nvim-coverage (probably needs some additional config)
|
|
||||||
@@ -1,43 +1,36 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
description = "CLI-related functionality for Nushell"
|
description = "CLI-related functionality for Nushell"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.76.0"
|
version = "0.66.1"
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.76.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.66.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.76.0" }
|
nu-command = { path = "../nu-command", version = "0.66.1" }
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
rstest = "0.15.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.76.0" }
|
nu-engine = { path = "../nu-engine", version = "0.66.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.76.0" }
|
nu-path = { path = "../nu-path", version = "0.66.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.76.0" }
|
nu-parser = { path = "../nu-parser", version = "0.66.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.76.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.66.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.76.0" }
|
nu-utils = { path = "../nu-utils", version = "0.66.1" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.76.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.66.1" }
|
||||||
|
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
|
||||||
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
crossterm = "0.23.0"
|
||||||
|
miette = { version = "5.1.0", features = ["fancy"] }
|
||||||
atty = "0.2.14"
|
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
|
||||||
crossterm = "0.24.0"
|
|
||||||
fancy-regex = "0.11.0"
|
|
||||||
fuzzy-matcher = "0.3.7"
|
|
||||||
is_executable = "1.0.1"
|
|
||||||
once_cell = "1.17.0"
|
|
||||||
log = "0.4"
|
|
||||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
|
||||||
percent-encoding = "2"
|
|
||||||
sysinfo = "0.28.0"
|
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
fuzzy-matcher = "0.3.7"
|
||||||
|
|
||||||
|
chrono = "0.4.19"
|
||||||
|
is_executable = "1.0.1"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
log = "0.4"
|
||||||
|
regex = "1.5.4"
|
||||||
|
sysinfo = "0.24.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pub fn evaluate_commands(
|
|||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
is_perf_true: bool,
|
||||||
table_mode: Option<Value>,
|
table_mode: Option<Value>,
|
||||||
) -> Result<Option<i64>> {
|
) -> Result<Option<i64>> {
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
@@ -67,7 +68,9 @@ pub fn evaluate_commands(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
if is_perf_true {
|
||||||
|
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(exit_code)
|
Ok(exit_code)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ pub struct CommandCompletion {
|
|||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
flattened: Vec<(Span, FlatShape)>,
|
flattened: Vec<(Span, FlatShape)>,
|
||||||
flat_shape: FlatShape,
|
flat_shape: FlatShape,
|
||||||
force_completion_after_space: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandCompletion {
|
impl CommandCompletion {
|
||||||
@@ -20,13 +19,11 @@ impl CommandCompletion {
|
|||||||
_: &StateWorkingSet,
|
_: &StateWorkingSet,
|
||||||
flattened: Vec<(Span, FlatShape)>,
|
flattened: Vec<(Span, FlatShape)>,
|
||||||
flat_shape: FlatShape,
|
flat_shape: FlatShape,
|
||||||
force_completion_after_space: bool,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine_state,
|
engine_state,
|
||||||
flattened,
|
flattened,
|
||||||
flat_shape,
|
flat_shape,
|
||||||
force_completion_after_space,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +34,7 @@ impl CommandCompletion {
|
|||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
let mut executables = vec![];
|
let mut executables = vec![];
|
||||||
|
|
||||||
// os agnostic way to get the PATH env var
|
let paths = self.engine_state.get_env_var("PATH");
|
||||||
let paths = self.engine_state.get_path_env_var();
|
|
||||||
|
|
||||||
if let Some(paths) = paths {
|
if let Some(paths) = paths {
|
||||||
if let Ok(paths) = paths.as_list() {
|
if let Ok(paths) = paths.as_list() {
|
||||||
@@ -61,7 +57,7 @@ impl CommandCompletion {
|
|||||||
.matches_str(&x.to_string_lossy(), prefix)),
|
.matches_str(&x.to_string_lossy(), prefix)),
|
||||||
Some(true)
|
Some(true)
|
||||||
)
|
)
|
||||||
&& is_executable::is_executable(item.path())
|
&& is_executable::is_executable(&item.path())
|
||||||
{
|
{
|
||||||
if let Ok(name) = item.file_name().into_string() {
|
if let Ok(name) = item.file_name().into_string() {
|
||||||
executables.push(name);
|
executables.push(name);
|
||||||
@@ -95,7 +91,10 @@ impl CommandCompletion {
|
|||||||
value: String::from_utf8_lossy(&x.0).to_string(),
|
value: String::from_utf8_lossy(&x.0).to_string(),
|
||||||
description: x.1,
|
description: x.1,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +105,10 @@ impl CommandCompletion {
|
|||||||
value: String::from_utf8_lossy(&x).to_string(),
|
value: String::from_utf8_lossy(&x).to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,8 +116,7 @@ impl CommandCompletion {
|
|||||||
|
|
||||||
let partial = working_set.get_span_contents(span);
|
let partial = working_set.get_span_contents(span);
|
||||||
let partial = String::from_utf8_lossy(partial).to_string();
|
let partial = String::from_utf8_lossy(partial).to_string();
|
||||||
|
let results = if find_externals {
|
||||||
if find_externals {
|
|
||||||
let results_external = self
|
let results_external = self
|
||||||
.external_command_completion(&partial, match_algorithm)
|
.external_command_completion(&partial, match_algorithm)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -123,15 +124,15 @@ impl CommandCompletion {
|
|||||||
value: x,
|
value: x,
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let results_strings: Vec<String> =
|
|
||||||
results.clone().into_iter().map(|x| x.value).collect();
|
|
||||||
|
|
||||||
for external in results_external {
|
for external in results_external {
|
||||||
if results_strings.contains(&external.value) {
|
if results.contains(&external) {
|
||||||
results.push(Suggestion {
|
results.push(Suggestion {
|
||||||
value: format!("^{}", external.value),
|
value: format!("^{}", external.value),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -147,7 +148,9 @@ impl CommandCompletion {
|
|||||||
results
|
results
|
||||||
} else {
|
} else {
|
||||||
results
|
results
|
||||||
}
|
};
|
||||||
|
|
||||||
|
results
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +185,10 @@ impl Completer for CommandCompletion {
|
|||||||
let subcommands = if let Some(last) = last {
|
let subcommands = if let Some(last) = last {
|
||||||
self.complete_commands(
|
self.complete_commands(
|
||||||
working_set,
|
working_set,
|
||||||
Span::new(last.0.start, pos),
|
Span {
|
||||||
|
start: last.0.start,
|
||||||
|
end: pos,
|
||||||
|
},
|
||||||
offset,
|
offset,
|
||||||
false,
|
false,
|
||||||
options.match_algorithm,
|
options.match_algorithm,
|
||||||
@@ -201,10 +207,6 @@ impl Completer for CommandCompletion {
|
|||||||
|| ((span.end - span.start) == 0)
|
|| ((span.end - span.start) == 0)
|
||||||
{
|
{
|
||||||
// we're in a gap or at a command
|
// we're in a gap or at a command
|
||||||
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
|
|
||||||
{
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
self.complete_commands(
|
self.complete_commands(
|
||||||
working_set,
|
working_set,
|
||||||
span,
|
span,
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ use crate::completions::{
|
|||||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
||||||
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
|
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
|
||||||
};
|
};
|
||||||
use nu_engine::eval_block;
|
|
||||||
use nu_parser::{flatten_expression, parse, FlatShape};
|
use nu_parser::{flatten_expression, parse, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::PipelineElement,
|
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
BlockId, PipelineData, Span, Value,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
||||||
use std::str;
|
use std::str;
|
||||||
@@ -58,125 +56,98 @@ impl NuCompleter {
|
|||||||
suggestions
|
suggestions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn external_completion(
|
|
||||||
&self,
|
|
||||||
block_id: BlockId,
|
|
||||||
spans: &[String],
|
|
||||||
offset: usize,
|
|
||||||
span: Span,
|
|
||||||
) -> Option<Vec<Suggestion>> {
|
|
||||||
let stack = self.stack.clone();
|
|
||||||
let block = self.engine_state.get_block(block_id);
|
|
||||||
let mut callee_stack = stack.gather_captures(&block.captures);
|
|
||||||
|
|
||||||
// Line
|
|
||||||
if let Some(pos_arg) = block.signature.required_positional.get(0) {
|
|
||||||
if let Some(var_id) = pos_arg.var_id {
|
|
||||||
callee_stack.add_var(
|
|
||||||
var_id,
|
|
||||||
Value::List {
|
|
||||||
vals: spans
|
|
||||||
.iter()
|
|
||||||
.map(|it| Value::string(it, Span::unknown()))
|
|
||||||
.collect(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = eval_block(
|
|
||||||
&self.engine_state,
|
|
||||||
&mut callee_stack,
|
|
||||||
block,
|
|
||||||
PipelineData::empty(),
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(pd) => {
|
|
||||||
let value = pd.into_value(span);
|
|
||||||
if let Value::List { vals, span: _ } = value {
|
|
||||||
let result =
|
|
||||||
map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
|
|
||||||
|
|
||||||
return Some(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => println!("failed to eval completer block: {err}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
||||||
let initial_line = line.to_string();
|
let initial_line = line.to_string();
|
||||||
let alias_total_offset: usize = alias_offset.iter().sum();
|
new_line.push(b'a');
|
||||||
new_line.insert(alias_total_offset + pos, b'a');
|
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let config = self.engine_state.get_config();
|
|
||||||
|
|
||||||
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
|
||||||
|
|
||||||
for pipeline in output.pipelines.into_iter() {
|
for pipeline in output.pipelines.into_iter() {
|
||||||
for pipeline_element in pipeline.elements {
|
for expr in pipeline.expressions {
|
||||||
match pipeline_element {
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
PipelineElement::Expression(_, expr)
|
let span_offset: usize = alias_offset.iter().sum();
|
||||||
| PipelineElement::Redirection(_, _, expr)
|
|
||||||
| PipelineElement::And(_, expr)
|
|
||||||
| PipelineElement::Or(_, expr)
|
|
||||||
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
|
||||||
let span_offset: usize = alias_offset.iter().sum();
|
|
||||||
let mut spans: Vec<String> = vec![];
|
|
||||||
|
|
||||||
for (flat_idx, flat) in flattened.iter().enumerate() {
|
for (flat_idx, flat) in flattened.iter().enumerate() {
|
||||||
// Read the current spam to string
|
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
||||||
let current_span = working_set.get_span_contents(flat.0).to_vec();
|
// Context variables
|
||||||
let current_span_str = String::from_utf8_lossy(¤t_span);
|
let most_left_var =
|
||||||
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
|
|
||||||
// Skip the last 'a' as span item
|
// Create a new span
|
||||||
if flat_idx == flattened.len() - 1 {
|
let new_span = if flat_idx == 0 {
|
||||||
let mut chars = current_span_str.chars();
|
Span {
|
||||||
chars.next_back();
|
start: flat.0.start,
|
||||||
let current_span_str = chars.as_str().to_owned();
|
end: flat.0.end - 1 - span_offset,
|
||||||
spans.push(current_span_str.to_string());
|
|
||||||
} else {
|
|
||||||
spans.push(current_span_str.to_string());
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Span {
|
||||||
|
start: flat.0.start - span_offset,
|
||||||
|
end: flat.0.end - 1 - span_offset,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Complete based on the last span
|
// Parses the prefix
|
||||||
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
// Context variables
|
prefix.remove(pos - (flat.0.start - span_offset));
|
||||||
let most_left_var =
|
|
||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
|
||||||
|
|
||||||
// Create a new span
|
// Variables completion
|
||||||
let new_span = if flat_idx == 0 {
|
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||||
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
|
let mut completer = VariableCompletion::new(
|
||||||
} else {
|
self.engine_state.clone(),
|
||||||
Span::new(
|
self.stack.clone(),
|
||||||
flat.0.start - span_offset,
|
most_left_var.unwrap_or((vec![], vec![])),
|
||||||
flat.0.end - 1 - span_offset,
|
);
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
return self.process_completion(
|
||||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
&mut completer,
|
||||||
let index = pos - (flat.0.start - span_offset);
|
&working_set,
|
||||||
prefix.drain(index..);
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Variables completion
|
// Flags completion
|
||||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
if prefix.starts_with(b"-") {
|
||||||
let mut completer = VariableCompletion::new(
|
let mut completer = FlagCompletion::new(expr);
|
||||||
self.engine_state.clone(),
|
|
||||||
self.stack.clone(),
|
return self.process_completion(
|
||||||
most_left_var.unwrap_or((vec![], vec![])),
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completions that depends on the previous expression (e.g: use, source)
|
||||||
|
if flat_idx > 0 {
|
||||||
|
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
||||||
|
// Read the content for the previous expression
|
||||||
|
let prev_expr_str =
|
||||||
|
working_set.get_span_contents(previous_expr.0).to_vec();
|
||||||
|
|
||||||
|
// Completion for .nu files
|
||||||
|
if prev_expr_str == b"use" || prev_expr_str == b"source" {
|
||||||
|
let mut completer =
|
||||||
|
DotNuCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
);
|
);
|
||||||
|
} else if prev_expr_str == b"ls" {
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
return self.process_completion(
|
return self.process_completion(
|
||||||
&mut completer,
|
&mut completer,
|
||||||
@@ -187,196 +158,94 @@ impl NuCompleter {
|
|||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags completion
|
|
||||||
if prefix.starts_with(b"-") {
|
|
||||||
// Try to complete flag internally
|
|
||||||
let mut completer = FlagCompletion::new(expr.clone());
|
|
||||||
let result = self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix.clone(),
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !result.is_empty() {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We got no results for internal completion
|
|
||||||
// now we can check if external completer is set and use it
|
|
||||||
if let Some(block_id) = config.external_completer {
|
|
||||||
if let Some(external_result) = self
|
|
||||||
.external_completion(block_id, &spans, offset, new_span)
|
|
||||||
{
|
|
||||||
return external_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// specially check if it is currently empty - always complete commands
|
|
||||||
if flat_idx == 0
|
|
||||||
&& working_set.get_span_contents(new_span).is_empty()
|
|
||||||
{
|
|
||||||
let mut completer = CommandCompletion::new(
|
|
||||||
self.engine_state.clone(),
|
|
||||||
&working_set,
|
|
||||||
flattened.clone(),
|
|
||||||
// flat_idx,
|
|
||||||
FlatShape::String,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Completions that depends on the previous expression (e.g: use, source-env)
|
|
||||||
if flat_idx > 0 {
|
|
||||||
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
|
|
||||||
// Read the content for the previous expression
|
|
||||||
let prev_expr_str =
|
|
||||||
working_set.get_span_contents(previous_expr.0).to_vec();
|
|
||||||
|
|
||||||
// Completion for .nu files
|
|
||||||
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
|
|
||||||
{
|
|
||||||
let mut completer =
|
|
||||||
DotNuCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
} else if prev_expr_str == b"ls" {
|
|
||||||
let mut completer =
|
|
||||||
FileCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match other types
|
|
||||||
match &flat.1 {
|
|
||||||
FlatShape::Custom(decl_id) => {
|
|
||||||
let mut completer = CustomCompletion::new(
|
|
||||||
self.engine_state.clone(),
|
|
||||||
self.stack.clone(),
|
|
||||||
*decl_id,
|
|
||||||
initial_line,
|
|
||||||
);
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
FlatShape::Directory => {
|
|
||||||
let mut completer =
|
|
||||||
DirectoryCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
FlatShape::Filepath | FlatShape::GlobPattern => {
|
|
||||||
let mut completer =
|
|
||||||
FileCompletion::new(self.engine_state.clone());
|
|
||||||
|
|
||||||
return self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
flat_shape => {
|
|
||||||
let mut completer = CommandCompletion::new(
|
|
||||||
self.engine_state.clone(),
|
|
||||||
&working_set,
|
|
||||||
flattened.clone(),
|
|
||||||
// flat_idx,
|
|
||||||
flat_shape.clone(),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut out: Vec<_> = self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix.clone(),
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !out.is_empty() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to complete using an external completer (if set)
|
|
||||||
if let Some(block_id) = config.external_completer {
|
|
||||||
if let Some(external_result) = self.external_completion(
|
|
||||||
block_id, &spans, offset, new_span,
|
|
||||||
) {
|
|
||||||
return external_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for file completion
|
|
||||||
let mut completer =
|
|
||||||
FileCompletion::new(self.engine_state.clone());
|
|
||||||
out = self.process_completion(
|
|
||||||
&mut completer,
|
|
||||||
&working_set,
|
|
||||||
prefix,
|
|
||||||
new_span,
|
|
||||||
offset,
|
|
||||||
pos,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !out.is_empty() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match other types
|
||||||
|
match &flat.1 {
|
||||||
|
FlatShape::Custom(decl_id) => {
|
||||||
|
let mut completer = CustomCompletion::new(
|
||||||
|
self.engine_state.clone(),
|
||||||
|
self.stack.clone(),
|
||||||
|
*decl_id,
|
||||||
|
initial_line,
|
||||||
|
);
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FlatShape::Directory => {
|
||||||
|
let mut completer =
|
||||||
|
DirectoryCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
FlatShape::Filepath | FlatShape::GlobPattern => {
|
||||||
|
let mut completer = FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
flat_shape => {
|
||||||
|
let mut completer = CommandCompletion::new(
|
||||||
|
self.engine_state.clone(),
|
||||||
|
&working_set,
|
||||||
|
flattened.clone(),
|
||||||
|
// flat_idx,
|
||||||
|
flat_shape.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out: Vec<_> = self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix.clone(),
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
|
||||||
|
if out.is_empty() {
|
||||||
|
let mut completer =
|
||||||
|
FileCompletion::new(self.engine_state.clone());
|
||||||
|
|
||||||
|
return self.process_completion(
|
||||||
|
&mut completer,
|
||||||
|
&working_set,
|
||||||
|
prefix,
|
||||||
|
new_span,
|
||||||
|
offset,
|
||||||
|
pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
return vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +302,7 @@ fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAl
|
|||||||
}
|
}
|
||||||
// Push the rest to names vector.
|
// Push the rest to names vector.
|
||||||
if pos < input.len() {
|
if pos < input.len() {
|
||||||
vec_names.push(input[pos..].to_owned());
|
vec_names.push((&input[pos..]).to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in &vec_names {
|
for name in &vec_names {
|
||||||
@@ -514,65 +383,3 @@ fn most_left_variable(
|
|||||||
|
|
||||||
Some((var, sublevels))
|
Some((var, sublevels))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_value_completions<'a>(
|
|
||||||
list: impl Iterator<Item = &'a Value>,
|
|
||||||
span: Span,
|
|
||||||
offset: usize,
|
|
||||||
) -> Vec<Suggestion> {
|
|
||||||
list.filter_map(move |x| {
|
|
||||||
// Match for string values
|
|
||||||
if let Ok(s) = x.as_string() {
|
|
||||||
return Some(Suggestion {
|
|
||||||
value: s,
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
|
||||||
start: span.start - offset,
|
|
||||||
end: span.end - offset,
|
|
||||||
},
|
|
||||||
append_whitespace: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match for record values
|
|
||||||
if let Ok((cols, vals)) = x.as_record() {
|
|
||||||
let mut suggestion = Suggestion {
|
|
||||||
value: String::from(""), // Initialize with empty string
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
|
||||||
start: span.start - offset,
|
|
||||||
end: span.end - offset,
|
|
||||||
},
|
|
||||||
append_whitespace: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Iterate the cols looking for `value` and `description`
|
|
||||||
cols.iter().zip(vals).for_each(|it| {
|
|
||||||
// Match `value` column
|
|
||||||
if it.0 == "value" {
|
|
||||||
// Convert the value to string
|
|
||||||
if let Ok(val_str) = it.1.as_string() {
|
|
||||||
// Update the suggestion value
|
|
||||||
suggestion.value = val_str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match `description` column
|
|
||||||
if it.0 == "description" {
|
|
||||||
// Convert the value to string
|
|
||||||
if let Ok(desc_str) = it.1.as_string() {
|
|
||||||
// Update the suggestion value
|
|
||||||
suggestion.description = Some(desc_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Some(suggestion);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ use nu_protocol::{
|
|||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::completer::map_value_completions;
|
|
||||||
|
|
||||||
pub struct CustomCompletion {
|
pub struct CustomCompletion {
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
stack: Stack,
|
stack: Stack,
|
||||||
@@ -28,6 +26,69 @@ impl CustomCompletion {
|
|||||||
sort_by: SortBy::None,
|
sort_by: SortBy::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_completions<'a>(
|
||||||
|
&self,
|
||||||
|
list: impl Iterator<Item = &'a Value>,
|
||||||
|
span: Span,
|
||||||
|
offset: usize,
|
||||||
|
) -> Vec<Suggestion> {
|
||||||
|
list.filter_map(move |x| {
|
||||||
|
// Match for string values
|
||||||
|
if let Ok(s) = x.as_string() {
|
||||||
|
return Some(Suggestion {
|
||||||
|
value: s,
|
||||||
|
description: None,
|
||||||
|
extra: None,
|
||||||
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
|
append_whitespace: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match for record values
|
||||||
|
if let Ok((cols, vals)) = x.as_record() {
|
||||||
|
let mut suggestion = Suggestion {
|
||||||
|
value: String::from(""), // Initialize with empty string
|
||||||
|
description: None,
|
||||||
|
extra: None,
|
||||||
|
span: reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
},
|
||||||
|
append_whitespace: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate the cols looking for `value` and `description`
|
||||||
|
cols.iter().zip(vals).for_each(|it| {
|
||||||
|
// Match `value` column
|
||||||
|
if it.0 == "value" {
|
||||||
|
// Convert the value to string
|
||||||
|
if let Ok(val_str) = it.1.as_string() {
|
||||||
|
// Update the suggestion value
|
||||||
|
suggestion.value = val_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match `description` column
|
||||||
|
if it.0 == "description" {
|
||||||
|
// Convert the value to string
|
||||||
|
if let Ok(desc_str) = it.1.as_string() {
|
||||||
|
// Update the suggestion value
|
||||||
|
suggestion.description = Some(desc_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Some(suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completer for CustomCompletion {
|
impl Completer for CustomCompletion {
|
||||||
@@ -52,13 +113,13 @@ impl Completer for CustomCompletion {
|
|||||||
head: span,
|
head: span,
|
||||||
arguments: vec![
|
arguments: vec![
|
||||||
Argument::Positional(Expression {
|
Argument::Positional(Expression {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
ty: Type::String,
|
ty: Type::String,
|
||||||
expr: Expr::String(self.line.clone()),
|
expr: Expr::String(self.line.clone()),
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
}),
|
}),
|
||||||
Argument::Positional(Expression {
|
Argument::Positional(Expression {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
ty: Type::Int,
|
ty: Type::Int,
|
||||||
expr: Expr::Int(line_pos as i64),
|
expr: Expr::Int(line_pos as i64),
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
@@ -66,16 +127,15 @@ impl Completer for CustomCompletion {
|
|||||||
],
|
],
|
||||||
redirect_stdout: true,
|
redirect_stdout: true,
|
||||||
redirect_stderr: true,
|
redirect_stderr: true,
|
||||||
parser_info: vec![],
|
|
||||||
},
|
},
|
||||||
PipelineData::empty(),
|
PipelineData::new(span),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut custom_completion_options = None;
|
let mut custom_completion_options = None;
|
||||||
|
|
||||||
// Parse result
|
// Parse result
|
||||||
let suggestions = result
|
let suggestions = match result {
|
||||||
.map(|pd| {
|
Ok(pd) => {
|
||||||
let value = pd.into_value(span);
|
let value = pd.into_value(span);
|
||||||
match &value {
|
match &value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@@ -84,7 +144,7 @@ impl Completer for CustomCompletion {
|
|||||||
.and_then(|val| {
|
.and_then(|val| {
|
||||||
val.as_list()
|
val.as_list()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|it| map_value_completions(it.iter(), span, offset))
|
.map(|it| self.map_completions(it.iter(), span, offset))
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let options = value.get_data_by_key("options");
|
let options = value.get_data_by_key("options");
|
||||||
@@ -129,11 +189,12 @@ impl Completer for CustomCompletion {
|
|||||||
|
|
||||||
completions
|
completions
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
Value::List { vals, .. } => self.map_completions(vals.iter(), span, offset),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.unwrap_or_default();
|
_ => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(custom_completion_options) = custom_completion_options {
|
if let Some(custom_completion_options) = custom_completion_options {
|
||||||
filter(&prefix, suggestions, &custom_completion_options)
|
filter(&prefix, suggestions, &custom_completion_options)
|
||||||
|
|||||||
@@ -33,7 +33,14 @@ impl Completer for DirectoryCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
@@ -119,7 +126,7 @@ pub fn directory_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{base_dir_name}{file_name}")
|
format!("{}{}", base_dir_name, file_name)
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@@ -129,13 +136,9 @@ pub fn directory_completion(
|
|||||||
file_name.push(SEP);
|
file_name.push(SEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix files or folders with quotes or hash
|
// Fix files or folders with quotes
|
||||||
if path.contains('\'')
|
if path.contains('\'') || path.contains('"') || path.contains(' ') {
|
||||||
|| path.contains('"')
|
path = format!("`{}`", path);
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl Completer for DotNuCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if the base_dir is a folder
|
// Check if the base_dir is a folder
|
||||||
if base_dir != format!(".{SEP}") {
|
if base_dir != format!(".{}", SEP) {
|
||||||
// Add the base dir into the directories to be searched
|
// Add the base dir into the directories to be searched
|
||||||
search_dirs.push(base_dir.clone());
|
search_dirs.push(base_dir.clone());
|
||||||
|
|
||||||
@@ -70,7 +70,14 @@ impl Completer for DotNuCompletion {
|
|||||||
partial = base_dir_partial;
|
partial = base_dir_partial;
|
||||||
} else {
|
} else {
|
||||||
// Fetch the current folder
|
// Fetch the current folder
|
||||||
let current_folder = self.engine_state.current_work_dir();
|
let current_folder = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
is_current_folder = true;
|
is_current_folder = true;
|
||||||
|
|
||||||
// Add the current folder and the lib dirs into the
|
// Add the current folder and the lib dirs into the
|
||||||
|
|||||||
@@ -30,7 +30,14 @@ impl Completer for FileCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = self.engine_state.current_work_dir();
|
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||||
|
match d.as_string() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -124,7 +131,7 @@ pub fn file_path_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{base_dir_name}{file_name}")
|
format!("{}{}", base_dir_name, file_name)
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@@ -134,25 +141,9 @@ pub fn file_path_completion(
|
|||||||
file_name.push(SEP);
|
file_name.push(SEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix files or folders with quotes or hashes
|
// Fix files or folders with quotes
|
||||||
if path.contains('\'')
|
if path.contains('\'') || path.contains('"') || path.contains(' ') {
|
||||||
|| path.contains('"')
|
path = format!("`{}`", path);
|
||||||
|| path.contains(' ')
|
|
||||||
|| path.contains('#')
|
|
||||||
|| path.contains('(')
|
|
||||||
|| path.contains(')')
|
|
||||||
|| path.starts_with('0')
|
|
||||||
|| path.starts_with('1')
|
|
||||||
|| path.starts_with('2')
|
|
||||||
|| path.starts_with('3')
|
|
||||||
|| path.starts_with('4')
|
|
||||||
|| path.starts_with('5')
|
|
||||||
|| path.starts_with('6')
|
|
||||||
|| path.starts_with('7')
|
|
||||||
|| path.starts_with('8')
|
|
||||||
|| path.starts_with('9')
|
|
||||||
{
|
|
||||||
path = format!("`{path}`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
@@ -180,7 +171,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
|||||||
|
|
||||||
/// Returns whether the base_dir should be prepended to the file path
|
/// Returns whether the base_dir should be prepended to the file path
|
||||||
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
||||||
if base_dir == format!(".{SEP}") {
|
if base_dir == format!(".{}", SEP) {
|
||||||
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
||||||
// input already includes a local folder prefix.
|
// input already includes a local folder prefix.
|
||||||
let manually_entered = {
|
let manually_entered = {
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ use reedline::Suggestion;
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::MatchAlgorithm;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VariableCompletion {
|
pub struct VariableCompletion {
|
||||||
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
|
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
|
||||||
@@ -75,11 +73,10 @@ impl Completer for VariableCompletion {
|
|||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(val.clone(), nested_levels, current_span)
|
nested_suggestions(val.clone(), nested_levels, current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,11 +86,10 @@ impl Completer for VariableCompletion {
|
|||||||
} else {
|
} else {
|
||||||
// No nesting provided, return all env vars
|
// No nesting provided, return all env vars
|
||||||
for env_var in env_vars {
|
for env_var in env_vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
env_var.0.as_bytes(),
|
.matches_u8(env_var.0.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: env_var.0,
|
value: env_var.0,
|
||||||
description: None,
|
description: None,
|
||||||
@@ -115,16 +111,18 @@ impl Completer for VariableCompletion {
|
|||||||
&self.engine_state,
|
&self.engine_state,
|
||||||
&self.stack,
|
&self.stack,
|
||||||
nu_protocol::NU_VARIABLE_ID,
|
nu_protocol::NU_VARIABLE_ID,
|
||||||
nu_protocol::Span::new(current_span.start, current_span.end),
|
nu_protocol::Span {
|
||||||
|
start: current_span.start,
|
||||||
|
end: current_span.end,
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
|
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,18 +134,23 @@ impl Completer for VariableCompletion {
|
|||||||
// Completion other variable types
|
// Completion other variable types
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
// Extract the variable value from the stack
|
// Extract the variable value from the stack
|
||||||
let var = self.stack.get_var(var_id, Span::new(span.start, span.end));
|
let var = self.stack.get_var(
|
||||||
|
var_id,
|
||||||
|
Span {
|
||||||
|
start: span.start,
|
||||||
|
end: span.end,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// If the value exists and it's of type Record
|
// If the value exists and it's of type Record
|
||||||
if let Ok(value) = var {
|
if let Ok(value) = var {
|
||||||
for suggestion in
|
for suggestion in
|
||||||
nested_suggestions(value, self.var_context.1.clone(), current_span)
|
nested_suggestions(value, self.var_context.1.clone(), current_span)
|
||||||
{
|
{
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
suggestion.value.as_bytes(),
|
.matches_u8(suggestion.value.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(suggestion);
|
output.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,11 +162,10 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
// Variable completion (e.g: $en<tab> to complete $env)
|
// Variable completion (e.g: $en<tab> to complete $env)
|
||||||
for builtin in builtins {
|
for builtin in builtins {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options
|
||||||
options.case_sensitive,
|
.match_algorithm
|
||||||
builtin.as_bytes(),
|
.matches_u8(builtin.as_bytes(), &prefix)
|
||||||
&prefix,
|
{
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: builtin.to_string(),
|
value: builtin.to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -185,11 +187,7 @@ impl Completer for VariableCompletion {
|
|||||||
.rev()
|
.rev()
|
||||||
{
|
{
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||||
options.case_sensitive,
|
|
||||||
v.0,
|
|
||||||
&prefix,
|
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -211,11 +209,7 @@ impl Completer for VariableCompletion {
|
|||||||
.rev()
|
.rev()
|
||||||
{
|
{
|
||||||
for v in &overlay_frame.vars {
|
for v in &overlay_frame.vars {
|
||||||
if options.match_algorithm.matches_u8_insensitive(
|
if options.match_algorithm.matches_u8(v.0, &prefix) {
|
||||||
options.case_sensitive,
|
|
||||||
v.0,
|
|
||||||
&prefix,
|
|
||||||
) {
|
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
@@ -262,20 +256,6 @@ fn nested_suggestions(
|
|||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
// Add all the columns as completion
|
|
||||||
for column_name in val.column_names() {
|
|
||||||
output.push(Suggestion {
|
|
||||||
value: column_name.to_string(),
|
|
||||||
description: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
|
||||||
append_whitespace: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => output,
|
_ => output,
|
||||||
}
|
}
|
||||||
@@ -301,7 +281,7 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
|||||||
|
|
||||||
// Current sublevel value not found
|
// Current sublevel value not found
|
||||||
return Value::Nothing {
|
return Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => return val,
|
_ => return val,
|
||||||
@@ -310,13 +290,3 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
|||||||
|
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchAlgorithm {
|
|
||||||
pub fn matches_u8_insensitive(&self, sensitive: bool, haystack: &[u8], needle: &[u8]) -> bool {
|
|
||||||
if sensitive {
|
|
||||||
self.matches_u8(haystack, needle)
|
|
||||||
} else {
|
|
||||||
self.matches_u8(&haystack.to_ascii_lowercase(), &needle.to_ascii_lowercase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::util::{eval_source, report_error};
|
use crate::util::{eval_source, report_error};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
use log::info;
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
use nu_parser::ParseError;
|
use nu_parser::ParseError;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_protocol::Spanned;
|
use nu_protocol::Spanned;
|
||||||
use nu_protocol::{HistoryFileFormat, PipelineData};
|
use nu_protocol::{HistoryFileFormat, PipelineData, Span};
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
use nu_utils::utils::perf;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@@ -23,37 +23,30 @@ pub fn read_plugin_file(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
|
is_perf_true: bool,
|
||||||
) {
|
) {
|
||||||
let start_time = std::time::Instant::now();
|
|
||||||
let mut plug_path = String::new();
|
|
||||||
// Reading signatures from signature file
|
// Reading signatures from signature file
|
||||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||||
add_plugin_file(engine_state, plugin_file, storage_path);
|
add_plugin_file(engine_state, plugin_file, storage_path);
|
||||||
|
|
||||||
let plugin_path = engine_state.plugin_signatures.clone();
|
let plugin_path = engine_state.plugin_signatures.clone();
|
||||||
if let Some(plugin_path) = plugin_path {
|
if let Some(plugin_path) = plugin_path {
|
||||||
let plugin_filename = plugin_path.to_string_lossy();
|
let plugin_filename = plugin_path.to_string_lossy().to_owned();
|
||||||
plug_path = plugin_filename.to_string();
|
|
||||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&contents,
|
&contents,
|
||||||
&plugin_filename,
|
&plugin_filename,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
false,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
if is_perf_true {
|
||||||
&format!("read_plugin_file {}", &plug_path),
|
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
||||||
start_time,
|
}
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@@ -66,11 +59,12 @@ pub fn add_plugin_file(
|
|||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
|
match canonicalize_with(&plugin_file.item, cwd) {
|
||||||
engine_state.plugin_signatures = Some(path)
|
Ok(path) => engine_state.plugin_signatures = Some(path),
|
||||||
} else {
|
Err(_) => {
|
||||||
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
@@ -86,7 +80,7 @@ pub fn eval_config_contents(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) {
|
) {
|
||||||
if config_path.exists() & config_path.is_file() {
|
if config_path.exists() & config_path.is_file() {
|
||||||
let config_filename = config_path.to_string_lossy();
|
let config_filename = config_path.to_string_lossy().to_owned();
|
||||||
|
|
||||||
if let Ok(contents) = std::fs::read(&config_path) {
|
if let Ok(contents) = std::fs::read(&config_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
@@ -94,8 +88,7 @@ pub fn eval_config_contents(
|
|||||||
stack,
|
stack,
|
||||||
&contents,
|
&contents,
|
||||||
&config_filename,
|
&config_filename,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
false,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Merge the environment in case env vars changed in the config
|
// Merge the environment in case env vars changed in the config
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ use crate::util::{eval_source, report_error};
|
|||||||
use log::info;
|
use log::info;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_engine::{convert_env_values, current_dir};
|
use nu_engine::convert_env_values;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_protocol::Type;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, PipelineData, ShellError, Span, Type, Value,
|
Config, PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::stdout_write_all_and_flush;
|
use nu_utils::stdout_write_all_and_flush;
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ pub fn evaluate_file(
|
|||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
|
is_perf_true: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
@@ -27,75 +28,12 @@ pub fn evaluate_file(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let file = std::fs::read(&path).into_diagnostic()?;
|
||||||
|
|
||||||
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!("Could not access file '{}': {:?}", path, e.to_string()),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let file_path_str = file_path.to_str().unwrap_or_else(|| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::NonUtf8Custom(
|
|
||||||
format!(
|
|
||||||
"Input file name '{}' is not valid UTF8",
|
|
||||||
file_path.to_string_lossy()
|
|
||||||
),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let file = std::fs::read(&file_path)
|
|
||||||
.into_diagnostic()
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!(
|
|
||||||
"Could not read file '{}': {:?}",
|
|
||||||
file_path_str,
|
|
||||||
e.to_string()
|
|
||||||
),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
engine_state.start_in_file(Some(file_path_str));
|
|
||||||
|
|
||||||
let parent = file_path.parent().unwrap_or_else(|| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(
|
|
||||||
&working_set,
|
|
||||||
&ShellError::FileNotFoundCustom(
|
|
||||||
format!("The file path '{file_path_str}' does not have a parent"),
|
|
||||||
Span::unknown(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
stack.add_env_var(
|
|
||||||
"FILE_PWD".to_string(),
|
|
||||||
Value::string(parent.to_string_lossy(), Span::unknown()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
trace!("parsing file: {}", file_path_str);
|
trace!("parsing file: {}", path);
|
||||||
let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]);
|
|
||||||
|
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
|
||||||
|
|
||||||
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
if working_set.find_decl(b"main", &Type::Any).is_some() {
|
||||||
let args = format!("main {}", args.join(" "));
|
let args = format!("main {}", args.join(" "));
|
||||||
@@ -104,32 +42,26 @@ pub fn evaluate_file(
|
|||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
&file,
|
&file,
|
||||||
file_path_str,
|
&path,
|
||||||
PipelineData::empty(),
|
PipelineData::new(Span::new(0, 0)),
|
||||||
true,
|
|
||||||
) {
|
) {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
if !eval_source(
|
if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) {
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
args.as_bytes(),
|
|
||||||
"<commandline>",
|
|
||||||
input,
|
|
||||||
true,
|
|
||||||
) {
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
} else if !eval_source(engine_state, stack, &file, file_path_str, input, true) {
|
} else if !eval_source(engine_state, stack, &file, &path, input) {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
if is_perf_true {
|
||||||
|
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_table_or_error(
|
pub fn print_table_or_error(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
mut pipeline_data: PipelineData,
|
mut pipeline_data: PipelineData,
|
||||||
@@ -143,38 +75,37 @@ pub(crate) fn print_table_or_error(
|
|||||||
// Change the engine_state config to use the passed in configuration
|
// Change the engine_state config to use the passed in configuration
|
||||||
engine_state.set_config(config);
|
engine_state.set_config(config);
|
||||||
|
|
||||||
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
Some(decl_id) => {
|
||||||
report_error(&working_set, error);
|
let command = engine_state.get_decl(decl_id);
|
||||||
std::process::exit(1);
|
if command.get_block_id().is_some() {
|
||||||
}
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
|
} else {
|
||||||
|
let table = command.run(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
&Call::new(Span::new(0, 0)),
|
||||||
|
pipeline_data,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
|
match table {
|
||||||
let command = engine_state.get_decl(decl_id);
|
Ok(table) => {
|
||||||
if command.get_block_id().is_some() {
|
print_or_exit(table, engine_state, config);
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
}
|
||||||
} else {
|
Err(error) => {
|
||||||
let table = command.run(
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
&Call::new(Span::new(0, 0)),
|
|
||||||
pipeline_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
match table {
|
report_error(&working_set, &error);
|
||||||
Ok(table) => {
|
|
||||||
print_or_exit(table, engine_state, config);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &error);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
None => {
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Make sure everything has finished
|
// Make sure everything has finished
|
||||||
if let Some(exit_code) = exit_code {
|
if let Some(exit_code) = exit_code {
|
||||||
@@ -200,7 +131,9 @@ fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, co
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let out = item.into_string("\n", config) + "\n";
|
let mut out = item.into_string("\n", config);
|
||||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
|
out.push('\n');
|
||||||
|
|
||||||
|
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{}", err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use {
|
use {
|
||||||
nu_ansi_term::{ansi::RESET, Style},
|
nu_ansi_term::{ansi::RESET, Style},
|
||||||
reedline::{
|
reedline::{
|
||||||
menu_functions::string_difference, Completer, Editor, Menu, MenuEvent, MenuTextStyle,
|
menu_functions::string_difference, Completer, LineBuffer, Menu, MenuEvent, MenuTextStyle,
|
||||||
Painter, Suggestion, UndoBehavior,
|
Painter, Suggestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -372,7 +372,7 @@ impl DescriptionMenu {
|
|||||||
let description = self
|
let description = self
|
||||||
.get_value()
|
.get_value()
|
||||||
.and_then(|suggestion| suggestion.description)
|
.and_then(|suggestion| suggestion.description)
|
||||||
.unwrap_or_default()
|
.unwrap_or_else(|| "".to_string())
|
||||||
.lines()
|
.lines()
|
||||||
.skip(self.skipped_rows)
|
.skip(self.skipped_rows)
|
||||||
.take(self.working_details.description_rows)
|
.take(self.working_details.description_rows)
|
||||||
@@ -411,10 +411,10 @@ impl DescriptionMenu {
|
|||||||
RESET
|
RESET
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(" {example}\r\n")
|
format!(" {}\r\n", example)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!(" {example}\r\n")
|
format!(" {}\r\n", example)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -429,7 +429,7 @@ impl DescriptionMenu {
|
|||||||
examples,
|
examples,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("\r\n\r\nExamples:\r\n{examples}",)
|
format!("\r\n\r\nExamples:\r\n{}", examples,)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +459,7 @@ impl Menu for DescriptionMenu {
|
|||||||
fn can_partially_complete(
|
fn can_partially_complete(
|
||||||
&mut self,
|
&mut self,
|
||||||
_values_updated: bool,
|
_values_updated: bool,
|
||||||
_editor: &mut Editor,
|
_line_buffer: &mut LineBuffer,
|
||||||
_completer: &mut dyn Completer,
|
_completer: &mut dyn Completer,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
false
|
||||||
@@ -481,21 +481,19 @@ impl Menu for DescriptionMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates menu values
|
/// Updates menu values
|
||||||
fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
|
fn update_values(&mut self, line_buffer: &mut LineBuffer, completer: &mut dyn Completer) {
|
||||||
if self.only_buffer_difference {
|
if self.only_buffer_difference {
|
||||||
if let Some(old_string) = &self.input {
|
if let Some(old_string) = &self.input {
|
||||||
let (start, input) = string_difference(editor.get_buffer(), old_string);
|
let (start, input) = string_difference(line_buffer.get_buffer(), old_string);
|
||||||
if !input.is_empty() {
|
if !input.is_empty() {
|
||||||
self.reset_position();
|
self.reset_position();
|
||||||
self.values = completer.complete(input, start);
|
self.values = completer.complete(input, start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let trimmed_buffer = editor.get_buffer().replace('\n', " ");
|
let trimmed_buffer = line_buffer.get_buffer().replace('\n', " ");
|
||||||
self.values = completer.complete(
|
self.values =
|
||||||
trimmed_buffer.as_str(),
|
completer.complete(trimmed_buffer.as_str(), line_buffer.insertion_point());
|
||||||
editor.line_buffer().insertion_point(),
|
|
||||||
);
|
|
||||||
self.reset_position();
|
self.reset_position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -504,7 +502,7 @@ impl Menu for DescriptionMenu {
|
|||||||
/// collected from the completer
|
/// collected from the completer
|
||||||
fn update_working_details(
|
fn update_working_details(
|
||||||
&mut self,
|
&mut self,
|
||||||
editor: &mut Editor,
|
line_buffer: &mut LineBuffer,
|
||||||
completer: &mut dyn Completer,
|
completer: &mut dyn Completer,
|
||||||
painter: &Painter,
|
painter: &Painter,
|
||||||
) {
|
) {
|
||||||
@@ -562,13 +560,13 @@ impl Menu for DescriptionMenu {
|
|||||||
match event {
|
match event {
|
||||||
MenuEvent::Activate(_) => {
|
MenuEvent::Activate(_) => {
|
||||||
self.reset_position();
|
self.reset_position();
|
||||||
self.input = Some(editor.get_buffer().to_string());
|
self.input = Some(line_buffer.get_buffer().to_string());
|
||||||
self.update_values(editor, completer);
|
self.update_values(line_buffer, completer);
|
||||||
}
|
}
|
||||||
MenuEvent::Deactivate => self.active = false,
|
MenuEvent::Deactivate => self.active = false,
|
||||||
MenuEvent::Edit(_) => {
|
MenuEvent::Edit(_) => {
|
||||||
self.reset_position();
|
self.reset_position();
|
||||||
self.update_values(editor, completer);
|
self.update_values(line_buffer, completer);
|
||||||
self.update_examples()
|
self.update_examples()
|
||||||
}
|
}
|
||||||
MenuEvent::NextElement => {
|
MenuEvent::NextElement => {
|
||||||
@@ -610,7 +608,7 @@ impl Menu for DescriptionMenu {
|
|||||||
let description_rows = self
|
let description_rows = self
|
||||||
.get_value()
|
.get_value()
|
||||||
.and_then(|suggestion| suggestion.description)
|
.and_then(|suggestion| suggestion.description)
|
||||||
.unwrap_or_default()
|
.unwrap_or_else(|| "".to_string())
|
||||||
.lines()
|
.lines()
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
@@ -629,28 +627,27 @@ impl Menu for DescriptionMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The buffer gets replaced in the Span location
|
/// The buffer gets replaced in the Span location
|
||||||
fn replace_in_buffer(&self, editor: &mut Editor) {
|
fn replace_in_buffer(&self, line_buffer: &mut LineBuffer) {
|
||||||
if let Some(Suggestion { value, span, .. }) = self.get_value() {
|
if let Some(Suggestion { value, span, .. }) = self.get_value() {
|
||||||
let start = span.start.min(editor.line_buffer().len());
|
let start = span.start.min(line_buffer.len());
|
||||||
let end = span.end.min(editor.line_buffer().len());
|
let end = span.end.min(line_buffer.len());
|
||||||
|
|
||||||
let replacement = if let Some(example_index) = self.example_index {
|
let string_len = if let Some(example_index) = self.example_index {
|
||||||
self.examples
|
let example = self
|
||||||
|
.examples
|
||||||
.get(example_index)
|
.get(example_index)
|
||||||
.expect("the example index is always checked")
|
.expect("the example index is always checked");
|
||||||
|
|
||||||
|
line_buffer.replace(start..end, example);
|
||||||
|
example.len()
|
||||||
} else {
|
} else {
|
||||||
&value
|
line_buffer.replace(start..end, &value);
|
||||||
|
value.len()
|
||||||
};
|
};
|
||||||
|
|
||||||
editor.edit_buffer(
|
let mut offset = line_buffer.insertion_point();
|
||||||
|lb| {
|
offset += string_len.saturating_sub(end.saturating_sub(start));
|
||||||
lb.replace_range(start..end, replacement);
|
line_buffer.set_insertion_point(offset);
|
||||||
let mut offset = lb.insertion_point();
|
|
||||||
offset += lb.len().saturating_sub(end.saturating_sub(start));
|
|
||||||
lb.set_insertion_point(offset);
|
|
||||||
},
|
|
||||||
UndoBehavior::CreateUndoPoint,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl NuHelpCompleter {
|
|||||||
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||||
let mut commands = full_commands
|
let mut commands = full_commands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(sig, _, _, _, _)| {
|
.filter(|(sig, _, _, _)| {
|
||||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig
|
|| sig
|
||||||
@@ -31,7 +31,7 @@ impl NuHelpCompleter {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
commands.sort_by(|(a, _, _, _, _), (b, _, _, _, _)| {
|
commands.sort_by(|(a, _, _, _), (b, _, _, _)| {
|
||||||
let a_distance = levenshtein_distance(line, &a.name);
|
let a_distance = levenshtein_distance(line, &a.name);
|
||||||
let b_distance = levenshtein_distance(line, &b.name);
|
let b_distance = levenshtein_distance(line, &b.name);
|
||||||
a_distance.cmp(&b_distance)
|
a_distance.cmp(&b_distance)
|
||||||
@@ -39,7 +39,7 @@ impl NuHelpCompleter {
|
|||||||
|
|
||||||
commands
|
commands
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(sig, examples, _, _, _)| {
|
.map(|(sig, examples, _, _)| {
|
||||||
let mut long_desc = String::new();
|
let mut long_desc = String::new();
|
||||||
|
|
||||||
let usage = &sig.usage;
|
let usage = &sig.usage;
|
||||||
|
|||||||
@@ -42,14 +42,20 @@ impl Completer for NuMenuCompleter {
|
|||||||
|
|
||||||
if let Some(buffer) = block.signature.get_positional(0) {
|
if let Some(buffer) = block.signature.get_positional(0) {
|
||||||
if let Some(buffer_id) = &buffer.var_id {
|
if let Some(buffer_id) = &buffer.var_id {
|
||||||
let line_buffer = Value::string(parsed.remainder, self.span);
|
let line_buffer = Value::String {
|
||||||
|
val: parsed.remainder.to_string(),
|
||||||
|
span: self.span,
|
||||||
|
};
|
||||||
self.stack.add_var(*buffer_id, line_buffer);
|
self.stack.add_var(*buffer_id, line_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(position) = block.signature.get_positional(1) {
|
if let Some(position) = block.signature.get_positional(1) {
|
||||||
if let Some(position_id) = &position.var_id {
|
if let Some(position_id) = &position.var_id {
|
||||||
let line_buffer = Value::int(pos as i64, self.span);
|
let line_buffer = Value::Int {
|
||||||
|
val: pos as i64,
|
||||||
|
span: self.span,
|
||||||
|
};
|
||||||
self.stack.add_var(*position_id, line_buffer);
|
self.stack.add_var(*position_id, line_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,10 +87,13 @@ fn convert_to_suggestions(
|
|||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
match value {
|
match value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
let text = value
|
let text = match value
|
||||||
.get_data_by_key("value")
|
.get_data_by_key("value")
|
||||||
.and_then(|val| val.as_string().ok())
|
.and_then(|val| val.as_string().ok())
|
||||||
.unwrap_or_else(|| "No value key".to_string());
|
{
|
||||||
|
Some(val) => val,
|
||||||
|
None => "No value key".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
let description = value
|
let description = value
|
||||||
.get_data_by_key("description")
|
.get_data_by_key("description")
|
||||||
@@ -154,7 +163,7 @@ fn convert_to_suggestions(
|
|||||||
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => vec![Suggestion {
|
_ => vec![Suggestion {
|
||||||
value: format!("Not a record: {value:?}"),
|
value: format!("Not a record: {:?}", value),
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type, Value};
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Value};
|
||||||
use reedline::Highlighter;
|
use reedline::Highlighter;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -12,9 +12,7 @@ impl Command for NuHighlight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("nu-highlight")
|
Signature::build("nu-highlight").category(Category::Strings)
|
||||||
.category(Category::Strings)
|
|
||||||
.input_output_types(vec![(Type::String, Type::String)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@@ -35,7 +33,7 @@ impl Command for NuHighlight {
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = std::sync::Arc::new(engine_state.clone());
|
let engine_state = engine_state.clone();
|
||||||
let config = engine_state.get_config().clone();
|
let config = engine_state.get_config().clone();
|
||||||
|
|
||||||
let highlighter = crate::NuHighlighter {
|
let highlighter = crate::NuHighlighter {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -16,7 +15,6 @@ impl Command for Print {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("print")
|
Signature::build("print")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
|
||||||
.rest("rest", SyntaxShape::Any, "the values to print")
|
.rest("rest", SyntaxShape::Any, "the values to print")
|
||||||
.switch(
|
.switch(
|
||||||
"no-newline",
|
"no-newline",
|
||||||
@@ -52,13 +50,14 @@ Since this command has no output, there is no point in piping it with other comm
|
|||||||
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
let no_newline = call.has_flag("no-newline");
|
let no_newline = call.has_flag("no-newline");
|
||||||
let to_stderr = call.has_flag("stderr");
|
let to_stderr = call.has_flag("stderr");
|
||||||
|
let head = call.head;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.into_pipeline_data()
|
arg.into_pipeline_data()
|
||||||
.print(engine_state, stack, no_newline, to_stderr)?;
|
.print(engine_state, stack, no_newline, to_stderr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::new(head))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ pub struct NushellPrompt {
|
|||||||
default_vi_insert_prompt_indicator: Option<String>,
|
default_vi_insert_prompt_indicator: Option<String>,
|
||||||
default_vi_normal_prompt_indicator: Option<String>,
|
default_vi_normal_prompt_indicator: Option<String>,
|
||||||
default_multiline_indicator: Option<String>,
|
default_multiline_indicator: Option<String>,
|
||||||
render_right_prompt_on_last_line: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NushellPrompt {
|
impl Default for NushellPrompt {
|
||||||
@@ -35,7 +34,6 @@ impl NushellPrompt {
|
|||||||
default_vi_insert_prompt_indicator: None,
|
default_vi_insert_prompt_indicator: None,
|
||||||
default_vi_normal_prompt_indicator: None,
|
default_vi_normal_prompt_indicator: None,
|
||||||
default_multiline_indicator: None,
|
default_multiline_indicator: None,
|
||||||
render_right_prompt_on_last_line: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,13 +41,8 @@ impl NushellPrompt {
|
|||||||
self.left_prompt_string = prompt_string;
|
self.left_prompt_string = prompt_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_prompt_right(
|
pub fn update_prompt_right(&mut self, prompt_string: Option<String>) {
|
||||||
&mut self,
|
|
||||||
prompt_string: Option<String>,
|
|
||||||
render_right_prompt_on_last_line: bool,
|
|
||||||
) {
|
|
||||||
self.right_prompt_string = prompt_string;
|
self.right_prompt_string = prompt_string;
|
||||||
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
|
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
|
||||||
@@ -75,7 +68,6 @@ impl NushellPrompt {
|
|||||||
prompt_indicator_string: Option<String>,
|
prompt_indicator_string: Option<String>,
|
||||||
prompt_multiline_indicator_string: Option<String>,
|
prompt_multiline_indicator_string: Option<String>,
|
||||||
prompt_vi: (Option<String>, Option<String>),
|
prompt_vi: (Option<String>, Option<String>),
|
||||||
render_right_prompt_on_last_line: bool,
|
|
||||||
) {
|
) {
|
||||||
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
|
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
|
||||||
|
|
||||||
@@ -86,12 +78,10 @@ impl NushellPrompt {
|
|||||||
|
|
||||||
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
|
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
|
||||||
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
|
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
|
||||||
|
|
||||||
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_wrapped_custom_string(&self, str: String) -> String {
|
fn default_wrapped_custom_string(&self, str: String) -> String {
|
||||||
format!("({str})")
|
format!("({})", str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +95,7 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.left_prompt_string {
|
if let Some(prompt_string) = &self.left_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::default();
|
let default = DefaultPrompt::new();
|
||||||
default
|
default
|
||||||
.render_prompt_left()
|
.render_prompt_left()
|
||||||
.to_string()
|
.to_string()
|
||||||
@@ -118,7 +108,7 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.right_prompt_string {
|
if let Some(prompt_string) = &self.right_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::default();
|
let default = DefaultPrompt::new();
|
||||||
default
|
default
|
||||||
.render_prompt_right()
|
.render_prompt_right()
|
||||||
.to_string()
|
.to_string()
|
||||||
@@ -130,36 +120,32 @@ impl Prompt for NushellPrompt {
|
|||||||
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
||||||
match edit_mode {
|
match edit_mode {
|
||||||
PromptEditMode::Default => match &self.default_prompt_indicator {
|
PromptEditMode::Default => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "〉",
|
None => "〉".into(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "〉",
|
None => "〉".into(),
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
||||||
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => ": ",
|
None => ": ".into(),
|
||||||
},
|
},
|
||||||
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "〉",
|
None => "〉".into(),
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
.into(),
|
|
||||||
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||||
match &self.default_multiline_indicator {
|
match &self.default_multiline_indicator {
|
||||||
Some(indicator) => indicator,
|
Some(indicator) => indicator.as_str().into(),
|
||||||
None => "::: ",
|
None => "::: ".into(),
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_history_search_indicator(
|
fn render_prompt_history_search_indicator(
|
||||||
@@ -176,8 +162,4 @@ impl Prompt for NushellPrompt {
|
|||||||
prefix, history_search.term
|
prefix, history_search.term
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn right_prompt_on_last_line(&self) -> bool {
|
|
||||||
self.render_right_prompt_on_last_line
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::util::report_error;
|
use crate::util::report_error;
|
||||||
use crate::NushellPrompt;
|
use crate::NushellPrompt;
|
||||||
use log::trace;
|
use log::info;
|
||||||
use nu_engine::eval_subexpression;
|
use nu_engine::eval_subexpression;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, PipelineData, Value,
|
Config, PipelineData, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::Prompt;
|
use reedline::Prompt;
|
||||||
|
|
||||||
@@ -25,11 +25,12 @@ fn get_prompt_string(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
is_perf_true: bool,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
stack
|
stack
|
||||||
.get_env_var(engine_state, prompt)
|
.get_env_var(engine_state, prompt)
|
||||||
.and_then(|v| match v {
|
.and_then(|v| match v {
|
||||||
Value::Closure {
|
Value::Block {
|
||||||
val: block_id,
|
val: block_id,
|
||||||
captures,
|
captures,
|
||||||
..
|
..
|
||||||
@@ -37,39 +38,29 @@ fn get_prompt_string(
|
|||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
let mut stack = stack.captures_to_stack(&captures);
|
let mut stack = stack.captures_to_stack(&captures);
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
||||||
let ret_val =
|
let ret_val = eval_subexpression(
|
||||||
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
|
engine_state,
|
||||||
trace!(
|
&mut stack,
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
block,
|
||||||
file!(),
|
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
);
|
||||||
|
if is_perf_true {
|
||||||
|
info!(
|
||||||
|
"get_prompt_string (block) {}:{}:{}",
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ret_val
|
match ret_val {
|
||||||
.map_err(|err| {
|
Ok(ret_val) => Some(ret_val),
|
||||||
|
Err(err) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
})
|
None
|
||||||
.ok()
|
}
|
||||||
}
|
}
|
||||||
Value::Block { val: block_id, .. } => {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
|
|
||||||
let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
|
|
||||||
trace!(
|
|
||||||
"get_prompt_string (block) {}:{}:{}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
|
||||||
|
|
||||||
ret_val
|
|
||||||
.map_err(|err| {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
report_error(&working_set, &err);
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -77,17 +68,20 @@ fn get_prompt_string(
|
|||||||
.and_then(|pipeline_data| {
|
.and_then(|pipeline_data| {
|
||||||
let output = pipeline_data.collect_string("", config).ok();
|
let output = pipeline_data.collect_string("", config).ok();
|
||||||
|
|
||||||
output.map(|mut x| {
|
match output {
|
||||||
// Just remove the very last newline.
|
Some(mut x) => {
|
||||||
if x.ends_with('\n') {
|
// Just remove the very last newline.
|
||||||
x.pop();
|
if x.ends_with('\n') {
|
||||||
}
|
x.pop();
|
||||||
|
}
|
||||||
|
|
||||||
if x.ends_with('\r') {
|
if x.ends_with('\r') {
|
||||||
x.pop();
|
x.pop();
|
||||||
|
}
|
||||||
|
Some(x)
|
||||||
}
|
}
|
||||||
x
|
None => None,
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,39 +90,71 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
nu_prompt: &'prompt mut NushellPrompt,
|
nu_prompt: &'prompt mut NushellPrompt,
|
||||||
|
is_perf_true: bool,
|
||||||
) -> &'prompt dyn Prompt {
|
) -> &'prompt dyn Prompt {
|
||||||
let mut stack = stack.clone();
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
|
let left_prompt_string = get_prompt_string(
|
||||||
|
PROMPT_COMMAND,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
// Now that we have the prompt string lets ansify it.
|
// Now that we have the prompt string lets ansify it.
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
let left_prompt_string = if config.shell_integration {
|
let left_prompt_string = if config.shell_integration {
|
||||||
if let Some(prompt_string) = left_prompt_string {
|
match left_prompt_string {
|
||||||
Some(format!(
|
Some(prompt_string) => Some(format!(
|
||||||
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
"{}{}{}",
|
||||||
))
|
PRE_PROMPT_MARKER, prompt_string, POST_PROMPT_MARKER
|
||||||
} else {
|
)),
|
||||||
left_prompt_string
|
None => left_prompt_string,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
left_prompt_string
|
left_prompt_string
|
||||||
};
|
};
|
||||||
|
|
||||||
let right_prompt_string =
|
let right_prompt_string = get_prompt_string(
|
||||||
get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, &mut stack);
|
PROMPT_COMMAND_RIGHT,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
let prompt_indicator_string =
|
let prompt_indicator_string = get_prompt_string(
|
||||||
get_prompt_string(PROMPT_INDICATOR, config, engine_state, &mut stack);
|
PROMPT_INDICATOR,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
let prompt_multiline_string =
|
let prompt_multiline_string = get_prompt_string(
|
||||||
get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, &mut stack);
|
PROMPT_MULTILINE_INDICATOR,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
let prompt_vi_insert_string =
|
let prompt_vi_insert_string = get_prompt_string(
|
||||||
get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, &mut stack);
|
PROMPT_INDICATOR_VI_INSERT,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
let prompt_vi_normal_string =
|
let prompt_vi_normal_string = get_prompt_string(
|
||||||
get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, &mut stack);
|
PROMPT_INDICATOR_VI_NORMAL,
|
||||||
|
config,
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
is_perf_true,
|
||||||
|
);
|
||||||
|
|
||||||
// apply the other indicators
|
// apply the other indicators
|
||||||
nu_prompt.update_all_prompt_strings(
|
nu_prompt.update_all_prompt_strings(
|
||||||
@@ -137,11 +163,12 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
prompt_indicator_string,
|
prompt_indicator_string,
|
||||||
prompt_multiline_string,
|
prompt_multiline_string,
|
||||||
(prompt_vi_insert_string, prompt_vi_normal_string),
|
(prompt_vi_insert_string, prompt_vi_normal_string),
|
||||||
config.render_right_prompt_on_last_line,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let ret_val = nu_prompt as &dyn Prompt;
|
let ret_val = nu_prompt as &dyn Prompt;
|
||||||
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
if is_perf_true {
|
||||||
|
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||||
|
}
|
||||||
|
|
||||||
ret_val
|
ret_val
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
use super::DescriptionMenu;
|
use super::DescriptionMenu;
|
||||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::lookup_ansi_color_style;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
create_menus,
|
color_value_string, create_menus,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, ShellError, Span, Value,
|
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
|
||||||
|
ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||||
@@ -109,11 +110,11 @@ pub(crate) fn add_menus(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut temp_stack = Stack::new();
|
let mut temp_stack = Stack::new();
|
||||||
let input = PipelineData::Empty;
|
let input = Value::nothing(Span::test_data()).into_pipeline_data();
|
||||||
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
||||||
|
|
||||||
if let PipelineData::Value(value, None) = res {
|
if let PipelineData::Value(value, None) = res {
|
||||||
for menu in create_menus(&value)? {
|
for menu in create_menus(&value, config)? {
|
||||||
line_editor =
|
line_editor =
|
||||||
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
||||||
}
|
}
|
||||||
@@ -158,11 +159,14 @@ macro_rules! add_style {
|
|||||||
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
||||||
$menu = match extract_value($name, $cols, $vals, $span) {
|
$menu = match extract_value($name, $cols, $vals, $span) {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
let style = match text {
|
let text = match text {
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
Value::String { val, .. } => val.clone(),
|
||||||
Value::Record { .. } => color_record_to_nustyle(&text),
|
Value::Record { cols, vals, span } => {
|
||||||
_ => lookup_ansi_color_style("green"),
|
color_value_string(span, cols, vals, $config).into_string("", $config)
|
||||||
|
}
|
||||||
|
_ => "green".to_string(),
|
||||||
};
|
};
|
||||||
|
let style = lookup_ansi_color_style(&text);
|
||||||
$f($menu, style)
|
$f($menu, style)
|
||||||
}
|
}
|
||||||
Err(_) => $menu,
|
Err(_) => $menu,
|
||||||
@@ -247,7 +251,7 @@ pub(crate) fn add_columnar_menu(
|
|||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
|
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
|
||||||
}
|
}
|
||||||
Value::Closure {
|
Value::Block {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@@ -333,7 +337,7 @@ pub(crate) fn add_list_menu(
|
|||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
|
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
|
||||||
}
|
}
|
||||||
Value::Closure {
|
Value::Block {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@@ -455,7 +459,7 @@ pub(crate) fn add_description_menu(
|
|||||||
completer,
|
completer,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Value::Closure {
|
Value::Block {
|
||||||
val,
|
val,
|
||||||
captures,
|
captures,
|
||||||
span,
|
span,
|
||||||
@@ -473,7 +477,7 @@ pub(crate) fn add_description_menu(
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::UnsupportedConfigValue(
|
_ => Err(ShellError::UnsupportedConfigValue(
|
||||||
"closure or omitted value".to_string(),
|
"block or omitted value".to_string(),
|
||||||
menu.source.into_abbreviated_string(config),
|
menu.source.into_abbreviated_string(config),
|
||||||
menu.source.span()?,
|
menu.source.span()?,
|
||||||
)),
|
)),
|
||||||
@@ -487,7 +491,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
|||||||
KeyCode::Tab,
|
KeyCode::Tab,
|
||||||
ReedlineEvent::UntilFound(vec![
|
ReedlineEvent::UntilFound(vec![
|
||||||
ReedlineEvent::Menu("completion_menu".to_string()),
|
ReedlineEvent::Menu("completion_menu".to_string()),
|
||||||
ReedlineEvent::Edit(vec![EditCommand::Complete]),
|
ReedlineEvent::MenuNext,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -651,19 +655,17 @@ fn add_parsed_keybinding(
|
|||||||
let pos1 = char_iter.next();
|
let pos1 = char_iter.next();
|
||||||
let pos2 = char_iter.next();
|
let pos2 = char_iter.next();
|
||||||
|
|
||||||
let char = if let (Some(char), None) = (pos1, pos2) {
|
let char = match (pos1, pos2) {
|
||||||
char
|
(Some(char), None) => Ok(char),
|
||||||
} else {
|
_ => Err(ShellError::UnsupportedConfigValue(
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
|
||||||
"char_<CHAR: unicode codepoint>".to_string(),
|
"char_<CHAR: unicode codepoint>".to_string(),
|
||||||
c.to_string(),
|
c.to_string(),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
));
|
)),
|
||||||
};
|
}?;
|
||||||
|
|
||||||
KeyCode::Char(char)
|
KeyCode::Char(char)
|
||||||
}
|
}
|
||||||
"space" => KeyCode::Char(' '),
|
|
||||||
"down" => KeyCode::Down,
|
"down" => KeyCode::Down,
|
||||||
"up" => KeyCode::Up,
|
"up" => KeyCode::Up,
|
||||||
"left" => KeyCode::Left,
|
"left" => KeyCode::Left,
|
||||||
@@ -680,10 +682,10 @@ fn add_parsed_keybinding(
|
|||||||
let fn_num: u8 = c[1..]
|
let fn_num: u8 = c[1..]
|
||||||
.parse()
|
.parse()
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|num| matches!(num, 1..=20))
|
.filter(|num| matches!(num, 1..=12))
|
||||||
.ok_or(ShellError::UnsupportedConfigValue(
|
.ok_or(ShellError::UnsupportedConfigValue(
|
||||||
"(f1|f2|...|f20)".to_string(),
|
"(f1|f2|...|f12)".to_string(),
|
||||||
format!("unknown function key: {c}"),
|
format!("unknown function key: {}", c),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
))?;
|
))?;
|
||||||
KeyCode::F(fn_num)
|
KeyCode::F(fn_num)
|
||||||
@@ -812,6 +814,7 @@ fn event_from_record(
|
|||||||
) -> Result<ReedlineEvent, ShellError> {
|
) -> Result<ReedlineEvent, ShellError> {
|
||||||
let event = match name {
|
let event = match name {
|
||||||
"none" => ReedlineEvent::None,
|
"none" => ReedlineEvent::None,
|
||||||
|
"actionhandler" => ReedlineEvent::ActionHandler,
|
||||||
"clearscreen" => ReedlineEvent::ClearScreen,
|
"clearscreen" => ReedlineEvent::ClearScreen,
|
||||||
"clearscrollback" => ReedlineEvent::ClearScrollback,
|
"clearscrollback" => ReedlineEvent::ClearScrollback,
|
||||||
"historyhintcomplete" => ReedlineEvent::HistoryHintComplete,
|
"historyhintcomplete" => ReedlineEvent::HistoryHintComplete,
|
||||||
@@ -819,8 +822,6 @@ fn event_from_record(
|
|||||||
"ctrld" => ReedlineEvent::CtrlD,
|
"ctrld" => ReedlineEvent::CtrlD,
|
||||||
"ctrlc" => ReedlineEvent::CtrlC,
|
"ctrlc" => ReedlineEvent::CtrlC,
|
||||||
"enter" => ReedlineEvent::Enter,
|
"enter" => ReedlineEvent::Enter,
|
||||||
"submit" => ReedlineEvent::Submit,
|
|
||||||
"submitornewline" => ReedlineEvent::SubmitOrNewline,
|
|
||||||
"esc" | "escape" => ReedlineEvent::Esc,
|
"esc" | "escape" => ReedlineEvent::Esc,
|
||||||
"up" => ReedlineEvent::Up,
|
"up" => ReedlineEvent::Up,
|
||||||
"down" => ReedlineEvent::Down,
|
"down" => ReedlineEvent::Down,
|
||||||
@@ -961,7 +962,6 @@ fn edit_from_record(
|
|||||||
let char = extract_char(value, config)?;
|
let char = extract_char(value, config)?;
|
||||||
EditCommand::MoveLeftBefore(char)
|
EditCommand::MoveLeftBefore(char)
|
||||||
}
|
}
|
||||||
"complete" => EditCommand::Complete,
|
|
||||||
e => {
|
e => {
|
||||||
return Err(ShellError::UnsupportedConfigValue(
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"reedline EditCommand".to_string(),
|
"reedline EditCommand".to_string(),
|
||||||
@@ -990,7 +990,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_send_event() {
|
fn test_send_event() {
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
||||||
@@ -1010,7 +1013,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_edit_event() {
|
fn test_edit_event() {
|
||||||
let cols = vec!["edit".to_string()];
|
let cols = vec!["edit".to_string()];
|
||||||
let vals = vec![Value::test_string("Clear")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Clear".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
|
||||||
@@ -1034,8 +1040,14 @@ mod test {
|
|||||||
fn test_send_menu() {
|
fn test_send_menu() {
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
@@ -1061,8 +1073,14 @@ mod test {
|
|||||||
// Menu event
|
// Menu event
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let menu_event = Value::Record {
|
let menu_event = Value::Record {
|
||||||
@@ -1073,7 +1091,10 @@ mod test {
|
|||||||
|
|
||||||
// Enter event
|
// Enter event
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let enter_event = Value::Record {
|
let enter_event = Value::Record {
|
||||||
cols,
|
cols,
|
||||||
@@ -1114,8 +1135,14 @@ mod test {
|
|||||||
// Menu event
|
// Menu event
|
||||||
let cols = vec!["send".to_string(), "name".to_string()];
|
let cols = vec!["send".to_string(), "name".to_string()];
|
||||||
let vals = vec![
|
let vals = vec![
|
||||||
Value::test_string("Menu"),
|
Value::String {
|
||||||
Value::test_string("history_menu"),
|
val: "Menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "history_menu".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let menu_event = Value::Record {
|
let menu_event = Value::Record {
|
||||||
@@ -1126,7 +1153,10 @@ mod test {
|
|||||||
|
|
||||||
// Enter event
|
// Enter event
|
||||||
let cols = vec!["send".to_string()];
|
let cols = vec!["send".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let enter_event = Value::Record {
|
let enter_event = Value::Record {
|
||||||
cols,
|
cols,
|
||||||
@@ -1154,7 +1184,10 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_error() {
|
fn test_error() {
|
||||||
let cols = vec!["not_exist".to_string()];
|
let cols = vec!["not_exist".to_string()];
|
||||||
let vals = vec![Value::test_string("Enter")];
|
let vals = vec![Value::String {
|
||||||
|
val: "Enter".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}];
|
||||||
|
|
||||||
let span = Span::test_data();
|
let span = Span::test_data();
|
||||||
let b = EventType::try_from_columns(&cols, &vals, &span);
|
let b = EventType::try_from_columns(&cols, &vals, &span);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,13 @@
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
use nu_color_config::get_shape_color;
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
|
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use nu_protocol::{Config, Span};
|
use nu_protocol::Config;
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
pub engine_state: Arc<EngineState>,
|
pub engine_state: EngineState,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,12 +15,10 @@ impl Highlighter for NuHighlighter {
|
|||||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
|
||||||
let block = {
|
|
||||||
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
|
||||||
block
|
|
||||||
};
|
|
||||||
let (shapes, global_span_offset) = {
|
let (shapes, global_span_offset) = {
|
||||||
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
|
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||||
|
|
||||||
let shapes = flatten_block(&working_set, &block);
|
let shapes = flatten_block(&working_set, &block);
|
||||||
(shapes, self.engine_state.next_span_start())
|
(shapes, self.engine_state.next_span_start())
|
||||||
};
|
};
|
||||||
@@ -30,15 +26,6 @@ impl Highlighter for NuHighlighter {
|
|||||||
let mut output = StyledText::default();
|
let mut output = StyledText::default();
|
||||||
let mut last_seen_span = global_span_offset;
|
let mut last_seen_span = global_span_offset;
|
||||||
|
|
||||||
let global_cursor_offset = _cursor + global_span_offset;
|
|
||||||
let matching_brackets_pos = find_matching_brackets(
|
|
||||||
line,
|
|
||||||
&working_set,
|
|
||||||
&block,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
for shape in &shapes {
|
for shape in &shapes {
|
||||||
if shape.0.end <= last_seen_span
|
if shape.0.end <= last_seen_span
|
||||||
|| last_seen_span < global_span_offset
|
|| last_seen_span < global_span_offset
|
||||||
@@ -57,75 +44,166 @@ impl Highlighter for NuHighlighter {
|
|||||||
let next_token = line
|
let next_token = line
|
||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
macro_rules! add_colored_token_with_bracket_highlight {
|
|
||||||
($shape:expr, $span:expr, $text:expr) => {{
|
|
||||||
let spans = split_span_by_highlight_positions(
|
|
||||||
line,
|
|
||||||
&$span,
|
|
||||||
&matching_brackets_pos,
|
|
||||||
global_span_offset,
|
|
||||||
);
|
|
||||||
spans.iter().for_each(|(part, highlight)| {
|
|
||||||
let start = part.start - $span.start;
|
|
||||||
let end = part.end - $span.start;
|
|
||||||
let text = (&next_token[start..end]).to_string();
|
|
||||||
let mut style = get_shape_color($shape.to_string(), &self.config);
|
|
||||||
if *highlight {
|
|
||||||
style = get_matching_brackets_style(style, &self.config);
|
|
||||||
}
|
|
||||||
output.push((style, text));
|
|
||||||
});
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! add_colored_token {
|
|
||||||
($shape:expr, $text:expr) => {
|
|
||||||
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
FlatShape::Garbage => output.push((
|
||||||
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
// nushell Garbage
|
||||||
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
next_token,
|
||||||
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
)),
|
||||||
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
FlatShape::Nothing => output.push((
|
||||||
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
// nushell Nothing
|
||||||
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
FlatShape::External => add_colored_token!(shape.1, next_token),
|
next_token,
|
||||||
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
)),
|
||||||
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
FlatShape::Binary => {
|
||||||
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
// nushell ?
|
||||||
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
output.push((
|
||||||
FlatShape::String => add_colored_token!(shape.1, next_token),
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
next_token,
|
||||||
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Bool => {
|
||||||
|
// nushell ?
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Int => {
|
||||||
|
// nushell Int
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Float => {
|
||||||
|
// nushell Decimal
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Range => output.push((
|
||||||
|
// nushell DotDot ?
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::InternalCall => output.push((
|
||||||
|
// nushell InternalCommand
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::External => {
|
||||||
|
// nushell ExternalCommand
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::ExternalArg => {
|
||||||
|
// nushell ExternalWord
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Literal => {
|
||||||
|
// nushell ?
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Operator => output.push((
|
||||||
|
// nushell Operator
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::Signature => output.push((
|
||||||
|
// nushell ?
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::String => {
|
||||||
|
// nushell String
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::StringInterpolation => {
|
||||||
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::DateTime => {
|
||||||
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
FlatShape::List => {
|
FlatShape::List => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
FlatShape::Table => {
|
FlatShape::Table => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
FlatShape::Record => {
|
FlatShape::Record => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Block => {
|
FlatShape::Block => {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
// nushell ???
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
FlatShape::Filepath => output.push((
|
||||||
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
// nushell Path
|
||||||
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
next_token,
|
||||||
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
)),
|
||||||
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
FlatShape::Directory => output.push((
|
||||||
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
|
// nushell Directory
|
||||||
FlatShape::And => add_colored_token!(shape.1, next_token),
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
FlatShape::Or => add_colored_token!(shape.1, next_token),
|
next_token,
|
||||||
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
|
)),
|
||||||
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
FlatShape::GlobPattern => output.push((
|
||||||
|
// nushell GlobPattern
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::Variable => output.push((
|
||||||
|
// nushell Variable
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
|
FlatShape::Flag => {
|
||||||
|
// nushell Flag
|
||||||
|
output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
FlatShape::Custom(..) => output.push((
|
||||||
|
get_shape_color(shape.1.to_string(), &self.config),
|
||||||
|
next_token,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
last_seen_span = shape.0.end;
|
||||||
}
|
}
|
||||||
@@ -138,301 +216,3 @@ impl Highlighter for NuHighlighter {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_span_by_highlight_positions(
|
|
||||||
line: &str,
|
|
||||||
span: &Span,
|
|
||||||
highlight_positions: &Vec<usize>,
|
|
||||||
global_span_offset: usize,
|
|
||||||
) -> Vec<(Span, bool)> {
|
|
||||||
let mut start = span.start;
|
|
||||||
let mut result: Vec<(Span, bool)> = Vec::new();
|
|
||||||
for pos in highlight_positions {
|
|
||||||
if start <= *pos && pos < &span.end {
|
|
||||||
if start < *pos {
|
|
||||||
result.push((Span::new(start, *pos), false));
|
|
||||||
}
|
|
||||||
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
|
||||||
let end = span_str
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
.map(|c| pos + get_char_length(c))
|
|
||||||
.unwrap_or(pos + 1);
|
|
||||||
result.push((Span::new(*pos, end), true));
|
|
||||||
start = end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if start < span.end {
|
|
||||||
result.push((Span::new(start, span.end), false));
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_matching_brackets(
|
|
||||||
line: &str,
|
|
||||||
working_set: &StateWorkingSet,
|
|
||||||
block: &Block,
|
|
||||||
global_span_offset: usize,
|
|
||||||
global_cursor_offset: usize,
|
|
||||||
) -> Vec<usize> {
|
|
||||||
const BRACKETS: &str = "{}[]()";
|
|
||||||
|
|
||||||
// calculate first bracket position
|
|
||||||
let global_end_offset = line.len() + global_span_offset;
|
|
||||||
let global_bracket_pos =
|
|
||||||
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
|
|
||||||
// cursor is at the end of a non-empty string -- find block end at the previous position
|
|
||||||
if let Some(last_char) = line.chars().last() {
|
|
||||||
global_cursor_offset - get_char_length(last_char)
|
|
||||||
} else {
|
|
||||||
global_cursor_offset
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// cursor is in the middle of a string -- find block end at the current position
|
|
||||||
global_cursor_offset
|
|
||||||
};
|
|
||||||
|
|
||||||
// check that position contains bracket
|
|
||||||
let match_idx = global_bracket_pos - global_span_offset;
|
|
||||||
if match_idx >= line.len()
|
|
||||||
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
|
|
||||||
{
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// find matching bracket by finding matching block end
|
|
||||||
let matching_block_end = find_matching_block_end_in_block(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
block,
|
|
||||||
global_span_offset,
|
|
||||||
global_bracket_pos,
|
|
||||||
);
|
|
||||||
if let Some(pos) = matching_block_end {
|
|
||||||
let matching_idx = pos - global_span_offset;
|
|
||||||
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
|
|
||||||
return if global_bracket_pos < pos {
|
|
||||||
vec![global_bracket_pos, pos]
|
|
||||||
} else {
|
|
||||||
vec![pos, global_bracket_pos]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_matching_block_end_in_block(
|
|
||||||
line: &str,
|
|
||||||
working_set: &StateWorkingSet,
|
|
||||||
block: &Block,
|
|
||||||
global_span_offset: usize,
|
|
||||||
global_cursor_offset: usize,
|
|
||||||
) -> Option<usize> {
|
|
||||||
for p in &block.pipelines {
|
|
||||||
for e in &p.elements {
|
|
||||||
match e {
|
|
||||||
PipelineElement::Expression(_, e)
|
|
||||||
| PipelineElement::Redirection(_, _, e)
|
|
||||||
| PipelineElement::And(_, e)
|
|
||||||
| PipelineElement::Or(_, e)
|
|
||||||
| PipelineElement::SeparateRedirection { out: (_, e), .. } => {
|
|
||||||
if e.span.contains(global_cursor_offset) {
|
|
||||||
if let Some(pos) = find_matching_block_end_in_expr(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
e,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
) {
|
|
||||||
return Some(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_matching_block_end_in_expr(
|
|
||||||
line: &str,
|
|
||||||
working_set: &StateWorkingSet,
|
|
||||||
expression: &Expression,
|
|
||||||
global_span_offset: usize,
|
|
||||||
global_cursor_offset: usize,
|
|
||||||
) -> Option<usize> {
|
|
||||||
macro_rules! find_in_expr_or_continue {
|
|
||||||
($inner_expr:ident) => {
|
|
||||||
if let Some(pos) = find_matching_block_end_in_expr(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
$inner_expr,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
) {
|
|
||||||
return Some(pos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
|
|
||||||
{
|
|
||||||
let expr_first = expression.span.start;
|
|
||||||
let span_str = &line
|
|
||||||
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
|
|
||||||
let expr_last = span_str
|
|
||||||
.chars()
|
|
||||||
.last()
|
|
||||||
.map(|c| expression.span.end - get_char_length(c))
|
|
||||||
.unwrap_or(expression.span.start);
|
|
||||||
|
|
||||||
return match &expression.expr {
|
|
||||||
Expr::Bool(_) => None,
|
|
||||||
Expr::Int(_) => None,
|
|
||||||
Expr::Float(_) => None,
|
|
||||||
Expr::Binary(_) => None,
|
|
||||||
Expr::Range(..) => None,
|
|
||||||
Expr::Var(_) => None,
|
|
||||||
Expr::VarDecl(_) => None,
|
|
||||||
Expr::ExternalCall(..) => None,
|
|
||||||
Expr::Operator(_) => None,
|
|
||||||
Expr::UnaryNot(_) => None,
|
|
||||||
Expr::Keyword(..) => None,
|
|
||||||
Expr::ValueWithUnit(..) => None,
|
|
||||||
Expr::DateTime(_) => None,
|
|
||||||
Expr::Filepath(_) => None,
|
|
||||||
Expr::Directory(_) => None,
|
|
||||||
Expr::GlobPattern(_) => None,
|
|
||||||
Expr::String(_) => None,
|
|
||||||
Expr::CellPath(_) => None,
|
|
||||||
Expr::ImportPattern(_) => None,
|
|
||||||
Expr::Overlay(_) => None,
|
|
||||||
Expr::Signature(_) => None,
|
|
||||||
Expr::Nothing => None,
|
|
||||||
Expr::Garbage => None,
|
|
||||||
|
|
||||||
Expr::Table(hdr, rows) => {
|
|
||||||
if expr_last == global_cursor_offset {
|
|
||||||
// cursor is at table end
|
|
||||||
Some(expr_first)
|
|
||||||
} else if expr_first == global_cursor_offset {
|
|
||||||
// cursor is at table start
|
|
||||||
Some(expr_last)
|
|
||||||
} else {
|
|
||||||
// cursor is inside table
|
|
||||||
for inner_expr in hdr {
|
|
||||||
find_in_expr_or_continue!(inner_expr);
|
|
||||||
}
|
|
||||||
for row in rows {
|
|
||||||
for inner_expr in row {
|
|
||||||
find_in_expr_or_continue!(inner_expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Record(exprs) => {
|
|
||||||
if expr_last == global_cursor_offset {
|
|
||||||
// cursor is at record end
|
|
||||||
Some(expr_first)
|
|
||||||
} else if expr_first == global_cursor_offset {
|
|
||||||
// cursor is at record start
|
|
||||||
Some(expr_last)
|
|
||||||
} else {
|
|
||||||
// cursor is inside record
|
|
||||||
for (k, v) in exprs {
|
|
||||||
find_in_expr_or_continue!(k);
|
|
||||||
find_in_expr_or_continue!(v);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Call(call) => {
|
|
||||||
for arg in &call.arguments {
|
|
||||||
let opt_expr = match arg {
|
|
||||||
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
|
||||||
Argument::Positional(inner_expr) => Some(inner_expr),
|
|
||||||
Argument::Unknown(inner_expr) => Some(inner_expr),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(inner_expr) = opt_expr {
|
|
||||||
find_in_expr_or_continue!(inner_expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
&b.head,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
),
|
|
||||||
|
|
||||||
Expr::BinaryOp(lhs, op, rhs) => {
|
|
||||||
find_in_expr_or_continue!(lhs);
|
|
||||||
find_in_expr_or_continue!(op);
|
|
||||||
find_in_expr_or_continue!(rhs);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Block(block_id)
|
|
||||||
| Expr::Closure(block_id)
|
|
||||||
| Expr::RowCondition(block_id)
|
|
||||||
| Expr::Subexpression(block_id) => {
|
|
||||||
if expr_last == global_cursor_offset {
|
|
||||||
// cursor is at block end
|
|
||||||
Some(expr_first)
|
|
||||||
} else if expr_first == global_cursor_offset {
|
|
||||||
// cursor is at block start
|
|
||||||
Some(expr_last)
|
|
||||||
} else {
|
|
||||||
// cursor is inside block
|
|
||||||
let nested_block = working_set.get_block(*block_id);
|
|
||||||
find_matching_block_end_in_block(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
nested_block,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::StringInterpolation(inner_expr) => {
|
|
||||||
for inner_expr in inner_expr {
|
|
||||||
find_in_expr_or_continue!(inner_expr);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::List(inner_expr) => {
|
|
||||||
if expr_last == global_cursor_offset {
|
|
||||||
// cursor is at list end
|
|
||||||
Some(expr_first)
|
|
||||||
} else if expr_first == global_cursor_offset {
|
|
||||||
// cursor is at list start
|
|
||||||
Some(expr_last)
|
|
||||||
} else {
|
|
||||||
// cursor is inside list
|
|
||||||
for inner_expr in inner_expr {
|
|
||||||
find_in_expr_or_continue!(inner_expr);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
|
|
||||||
s[index..].chars().next()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_char_length(c: char) -> usize {
|
|
||||||
c.to_string().len()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
use crate::repl::eval_hook;
|
use log::trace;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
use nu_protocol::CliError;
|
use nu_protocol::CliError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
PipelineData, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use nu_utils::utils::perf;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
@@ -44,7 +43,7 @@ fn gather_env_vars(
|
|||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
&ShellError::GenericError(
|
&ShellError::GenericError(
|
||||||
format!("Environment variable was not captured: {env_str}"),
|
format!("Environment variable was not captured: {}", env_str),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(msg.into()),
|
Some(msg.into()),
|
||||||
@@ -80,7 +79,8 @@ fn gather_env_vars(
|
|||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"Retrieving current directory failed: {init_cwd:?} not a valid utf-8 path"
|
"Retrieving current directory failed: {:?} not a valid utf-8 path",
|
||||||
|
init_cwd
|
||||||
)),
|
)),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
),
|
),
|
||||||
@@ -203,9 +203,8 @@ pub fn eval_source(
|
|||||||
source: &[u8],
|
source: &[u8],
|
||||||
fname: &str,
|
fname: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
allow_return: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let start_time = std::time::Instant::now();
|
trace!("eval_source");
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
@@ -231,48 +230,24 @@ pub fn eval_source(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = if allow_return {
|
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||||
eval_block_with_early_return(engine_state, stack, &block, input, false, false)
|
Ok(mut pipeline_data) => {
|
||||||
} else {
|
if let PipelineData::ExternalStream { exit_code, .. } = &mut pipeline_data {
|
||||||
eval_block(engine_state, stack, &block, input, false, false)
|
if let Some(exit_code) = exit_code.take().and_then(|it| it.last()) {
|
||||||
};
|
stack.add_env_var("LAST_EXIT_CODE".to_string(), exit_code);
|
||||||
|
} else {
|
||||||
match b {
|
set_last_exit_code(stack, 0);
|
||||||
Ok(pipeline_data) => {
|
|
||||||
let config = engine_state.get_config();
|
|
||||||
let result;
|
|
||||||
if let PipelineData::ExternalStream {
|
|
||||||
stdout: stream,
|
|
||||||
stderr: stderr_stream,
|
|
||||||
exit_code,
|
|
||||||
..
|
|
||||||
} = pipeline_data
|
|
||||||
{
|
|
||||||
result = print_if_stream(stream, stderr_stream, false, exit_code);
|
|
||||||
} else if let Some(hook) = config.hooks.display_output.clone() {
|
|
||||||
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
|
|
||||||
Err(err) => {
|
|
||||||
result = Err(err);
|
|
||||||
}
|
|
||||||
Ok(val) => {
|
|
||||||
result = val.print(engine_state, stack, false, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = pipeline_data.print(engine_state, stack, true, false);
|
set_last_exit_code(stack, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
match result {
|
if let Err(err) = pipeline_data.print(engine_state, stack, false, false) {
|
||||||
Err(err) => {
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
Ok(exit_code) => {
|
|
||||||
set_last_exit_code(stack, exit_code);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset vt processing, aka ansi because illbehaved externals can break it
|
// reset vt processing, aka ansi because illbehaved externals can break it
|
||||||
@@ -291,14 +266,6 @@ pub fn eval_source(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
|
||||||
&format!("eval_source {}", &fname),
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -306,7 +273,10 @@ pub fn eval_source(
|
|||||||
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
|
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"LAST_EXIT_CODE".to_string(),
|
"LAST_EXIT_CODE".to_string(),
|
||||||
Value::int(exit_code, Span::unknown()),
|
Value::Int {
|
||||||
|
val: exit_code,
|
||||||
|
span: Span { start: 0, end: 0 },
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,19 +302,27 @@ pub fn report_error_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
pub fn get_init_cwd() -> PathBuf {
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
match std::env::current_dir() {
|
||||||
std::env::var("PWD")
|
Ok(cwd) => cwd,
|
||||||
.map(Into::into)
|
Err(_) => match std::env::var("PWD") {
|
||||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
Ok(cwd) => PathBuf::from(cwd),
|
||||||
})
|
Err(_) => match nu_path::home_dir() {
|
||||||
|
Some(cwd) => cwd,
|
||||||
|
None => PathBuf::new(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
||||||
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
match nu_engine::env::current_dir(engine_state, stack) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
Ok(p) => p,
|
||||||
report_error(&working_set, &e);
|
Err(e) => {
|
||||||
get_init_cwd()
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
})
|
report_error(&working_set, &e);
|
||||||
|
get_init_cwd()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use nu_parser::{parse, ParseError};
|
use nu_parser::{parse, ParseError};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct NuValidator {
|
pub struct NuValidator {
|
||||||
pub engine_state: Arc<EngineState>,
|
pub engine_state: EngineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator for NuValidator {
|
impl Validator for NuValidator {
|
||||||
|
|||||||
65
crates/nu-cli/tests/alias.rs
Normal file
65
crates/nu-cli/tests/alias.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_command_and_flags() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls -l"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("ll t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_basic_command() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls "#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("ll t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_of_another_alias() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls -la"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
|
||||||
|
// Create the second alias
|
||||||
|
let alias = r#"alias lf = ll -f"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("lf t", 4);
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
@@ -1,867 +0,0 @@
|
|||||||
pub mod support;
|
|
||||||
|
|
||||||
use nu_cli::NuCompleter;
|
|
||||||
use nu_parser::parse;
|
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
|
||||||
use reedline::{Completer, Suggestion};
|
|
||||||
use rstest::{fixture, rstest};
|
|
||||||
use support::{completions_helpers::new_quote_engine, file, folder, match_suggestions, new_engine};
|
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn completer() -> NuCompleter {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Add record value as example
|
|
||||||
let record = "def tst [--mod -s] {}";
|
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn completer_strings() -> NuCompleter {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Add record value as example
|
|
||||||
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
|
||||||
def my-command [animal: string@animals] { print $animal }"#;
|
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn extern_completer() -> NuCompleter {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Add record value as example
|
|
||||||
let record = r#"
|
|
||||||
def animals [] { [ "cat", "dog", "eel" ] }
|
|
||||||
extern spam [
|
|
||||||
animal: string@animals
|
|
||||||
--foo (-f): string@animals
|
|
||||||
-b: string@animals
|
|
||||||
]
|
|
||||||
"#;
|
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn variables_dollar_sign_with_varialblecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "$ ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
assert_eq!(7, suggestions.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
|
||||||
let suggestions = completer.complete("tst --", 6);
|
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
|
||||||
// dbg!(&expected, &suggestions);
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
|
||||||
let suggestions = completer.complete("tst -", 5);
|
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
|
||||||
let suggestions = completer_strings.complete("my-c ", 4);
|
|
||||||
let expected: Vec<String> = vec!["my-command".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn variables_customcompletion_subcommands_with_customcompletion_2(
|
|
||||||
mut completer_strings: NuCompleter,
|
|
||||||
) {
|
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dotnu_completions() {
|
|
||||||
// Create a new engine
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Test source completion
|
|
||||||
let completion_str = "source-env ".to_string();
|
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
|
||||||
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
|
|
||||||
|
|
||||||
// Test use completion
|
|
||||||
let completion_str = "use ".to_string();
|
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
|
||||||
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn external_completer_trailing_space() {
|
|
||||||
// https://github.com/nushell/nushell/issues/6378
|
|
||||||
let block = "let external_completer = {|spans| $spans}";
|
|
||||||
let input = "gh alias ".to_string();
|
|
||||||
|
|
||||||
let suggestions = run_external_completion(block, &input);
|
|
||||||
assert_eq!(3, suggestions.len());
|
|
||||||
assert_eq!("gh", suggestions.get(0).unwrap().value);
|
|
||||||
assert_eq!("alias", suggestions.get(1).unwrap().value);
|
|
||||||
assert_eq!("", suggestions.get(2).unwrap().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn external_completer_no_trailing_space() {
|
|
||||||
let block = "let external_completer = {|spans| $spans}";
|
|
||||||
let input = "gh alias".to_string();
|
|
||||||
|
|
||||||
let suggestions = run_external_completion(block, &input);
|
|
||||||
assert_eq!(2, suggestions.len());
|
|
||||||
assert_eq!("gh", suggestions.get(0).unwrap().value);
|
|
||||||
assert_eq!("alias", suggestions.get(1).unwrap().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn external_completer_pass_flags() {
|
|
||||||
let block = "let external_completer = {|spans| $spans}";
|
|
||||||
let input = "gh api --".to_string();
|
|
||||||
|
|
||||||
let suggestions = run_external_completion(block, &input);
|
|
||||||
assert_eq!(3, suggestions.len());
|
|
||||||
assert_eq!("gh", suggestions.get(0).unwrap().value);
|
|
||||||
assert_eq!("api", suggestions.get(1).unwrap().value);
|
|
||||||
assert_eq!("--", suggestions.get(2).unwrap().value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn file_completions() {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, dir_str, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Test completions for the current folder
|
|
||||||
let target_dir = format!("cp {dir_str}");
|
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
||||||
|
|
||||||
// Create the expected values
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
file(dir.join("nushell")),
|
|
||||||
folder(dir.join("test_a")),
|
|
||||||
folder(dir.join("test_b")),
|
|
||||||
folder(dir.join("another")),
|
|
||||||
file(dir.join("custom_completion.nu")),
|
|
||||||
file(dir.join(".hidden_file")),
|
|
||||||
folder(dir.join(".hidden_folder")),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Match the results
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
|
||||||
|
|
||||||
// Test completions for a file
|
|
||||||
let target_dir = format!("cp {}", folder(dir.join("another")));
|
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
||||||
|
|
||||||
// Create the expected values
|
|
||||||
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
|
||||||
|
|
||||||
// Match the results
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_ls_with_filecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "ls ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn command_open_with_filecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "open ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_rm_with_globcompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "rm ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_cp_with_globcompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "cp ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_save_with_filecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "save ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_touch_with_filecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "touch ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn command_watch_with_filecompletion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "watch ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn file_completion_quoted() {
|
|
||||||
let (_, _, engine, stack) = new_quote_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "open ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"`te st.txt`".to_string(),
|
|
||||||
"`te#st.txt`".to_string(),
|
|
||||||
"`te'st.txt`".to_string(),
|
|
||||||
"`te(st).txt`".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn flag_completions() {
|
|
||||||
// Create a new engine
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
// Test completions for the 'ls' flags
|
|
||||||
let suggestions = completer.complete("ls -", 4);
|
|
||||||
|
|
||||||
assert_eq!(16, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
|
||||||
"--all".into(),
|
|
||||||
"--directory".into(),
|
|
||||||
"--du".into(),
|
|
||||||
"--full-paths".into(),
|
|
||||||
"--help".into(),
|
|
||||||
"--long".into(),
|
|
||||||
"--mime-type".into(),
|
|
||||||
"--short-names".into(),
|
|
||||||
"-D".into(),
|
|
||||||
"-a".into(),
|
|
||||||
"-d".into(),
|
|
||||||
"-f".into(),
|
|
||||||
"-h".into(),
|
|
||||||
"-l".into(),
|
|
||||||
"-m".into(),
|
|
||||||
"-s".into(),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn folder_with_directorycompletions() {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, dir_str, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Test completions for the current folder
|
|
||||||
let target_dir = format!("cd {dir_str}");
|
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
||||||
|
|
||||||
// Create the expected values
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
folder(dir.join("test_a")),
|
|
||||||
folder(dir.join("test_b")),
|
|
||||||
folder(dir.join("another")),
|
|
||||||
folder(dir.join(".hidden_folder")),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Match the results
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn variables_completions() {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Add record value as example
|
|
||||||
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
|
|
||||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Test completions for $nu
|
|
||||||
let suggestions = completer.complete("$nu.", 4);
|
|
||||||
|
|
||||||
assert_eq!(9, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
|
||||||
"config-path".into(),
|
|
||||||
"env-path".into(),
|
|
||||||
"history-path".into(),
|
|
||||||
"home-path".into(),
|
|
||||||
"loginshell-path".into(),
|
|
||||||
"os-info".into(),
|
|
||||||
"pid".into(),
|
|
||||||
"scope".into(),
|
|
||||||
"temp-path".into(),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for $nu.h (filter)
|
|
||||||
let suggestions = completer.complete("$nu.h", 5);
|
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for custom var
|
|
||||||
let suggestions = completer.complete("$actor.", 7);
|
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for custom var (filtering)
|
|
||||||
let suggestions = completer.complete("$actor.n", 8);
|
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec!["name".into()];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for $env
|
|
||||||
let suggestions = completer.complete("$env.", 5);
|
|
||||||
|
|
||||||
assert_eq!(3, suggestions.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected: Vec<String> = vec!["PWD".into(), "Path".into(), "TEST".into()];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
|
|
||||||
// Test completions for $env
|
|
||||||
let suggestions = completer.complete("$env.T", 6);
|
|
||||||
|
|
||||||
assert_eq!(1, suggestions.len());
|
|
||||||
|
|
||||||
let expected: Vec<String> = vec!["TEST".into()];
|
|
||||||
|
|
||||||
// Match results
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_of_command_and_flags() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ll = ls -l"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let suggestions = completer.complete("ll t", 4);
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_of_basic_command() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ll = ls "#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let suggestions = completer.complete("ll t", 4);
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_of_another_alias() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ll = ls -la"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
|
|
||||||
// Create the second alias
|
|
||||||
let alias = r#"alias lf = ll -f"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let suggestions = completer.complete("lf t", 4);
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
|
|
||||||
// Create a new engine
|
|
||||||
let (dir, _, mut engine_state, mut stack) = new_engine();
|
|
||||||
let (_, delta) = {
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]);
|
|
||||||
assert!(err.is_none());
|
|
||||||
|
|
||||||
(block, working_set.render())
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(engine_state.merge_delta(delta).is_ok());
|
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
|
||||||
assert!(engine_state.merge_env(&mut stack, &dir).is_ok());
|
|
||||||
|
|
||||||
let latest_block_id = engine_state.num_blocks() - 1;
|
|
||||||
|
|
||||||
// Change config adding the external completer
|
|
||||||
let mut config = engine_state.get_config().clone();
|
|
||||||
config.external_completer = Some(latest_block_id);
|
|
||||||
engine_state.set_config(&config);
|
|
||||||
|
|
||||||
// Instantiate a new completer
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
|
|
||||||
|
|
||||||
completer.complete(input, input.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unknown_command_completion() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let target_dir = "thiscommanddoesnotexist ";
|
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
|
||||||
let suggestions = completer.complete("tst -h", 5);
|
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
|
||||||
let suggestions = completer_strings.complete("my-command c", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
|
||||||
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
|
||||||
let suggestions = completer.complete("tst -h | ls", 5);
|
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn filecompletions_triggers_after_cursor() {
|
|
||||||
let (_, _, engine, stack) = new_engine();
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
let suggestions = completer.complete("cp test_c", 3);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a\\".to_string(),
|
|
||||||
"test_b\\".to_string(),
|
|
||||||
"another\\".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder\\".to_string(),
|
|
||||||
];
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
let expected_paths: Vec<String> = vec![
|
|
||||||
"nushell".to_string(),
|
|
||||||
"test_a/".to_string(),
|
|
||||||
"test_b/".to_string(),
|
|
||||||
"another/".to_string(),
|
|
||||||
"custom_completion.nu".to_string(),
|
|
||||||
".hidden_file".to_string(),
|
|
||||||
".hidden_folder/".to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam ", 5);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam --foo=", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam --foo ", 11);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -f ", 8);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -b ", 8);
|
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
|
||||||
let suggestions = extern_completer.complete("spam -", 6);
|
|
||||||
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
|
||||||
match_suggestions(expected, suggestions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
|
||||||
#[rstest]
|
|
||||||
fn alias_offset_bug_7648() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Issue #7648
|
|
||||||
// Nushell crashes when an alias name is shorter than the alias command
|
|
||||||
// and the alias command is a external command
|
|
||||||
// This happens because of offset is not correct.
|
|
||||||
// This crashes before PR #7779
|
|
||||||
let _suggestions = completer.complete("e", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
|
||||||
#[rstest]
|
|
||||||
fn alias_offset_bug_7754() {
|
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
|
||||||
|
|
||||||
// Create an alias
|
|
||||||
let alias = r#"alias ll = ls -l"#;
|
|
||||||
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
|
||||||
|
|
||||||
// Issue #7754
|
|
||||||
// Nushell crashes when an alias name is shorter than the alias command
|
|
||||||
// and the alias command contains pipes.
|
|
||||||
// This crashes before PR #7756
|
|
||||||
let _suggestions = completer.complete("ll -a | c", 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_path_env_var_8003() {
|
|
||||||
// Create a new engine
|
|
||||||
let (_, _, engine, _) = new_engine();
|
|
||||||
// Get the path env var in a platform agnostic way
|
|
||||||
let the_path = engine.get_path_env_var();
|
|
||||||
// Make sure it's not empty
|
|
||||||
assert!(the_path.is_some());
|
|
||||||
}
|
|
||||||
69
crates/nu-cli/tests/custom_completions.rs
Normal file
69
crates/nu-cli/tests/custom_completions.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use rstest::{fixture, rstest};
|
||||||
|
use support::{match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn completer() -> NuCompleter {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Add record value as example
|
||||||
|
let record = "def tst [--mod -s] {}";
|
||||||
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn completer_strings() -> NuCompleter {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Add record value as example
|
||||||
|
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
||||||
|
def my-command [animal: string@animals] { print $animal }"#;
|
||||||
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
NuCompleter::new(std::sync::Arc::new(engine), stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn variables_completions_double_dash_argument(mut completer: NuCompleter) {
|
||||||
|
let suggestions = completer.complete("tst --", 6);
|
||||||
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
||||||
|
// dbg!(&expected, &suggestions);
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn variables_completions_single_dash_argument(mut completer: NuCompleter) {
|
||||||
|
let suggestions = completer.complete("tst -", 5);
|
||||||
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn variables_completions_command(mut completer_strings: NuCompleter) {
|
||||||
|
let suggestions = completer_strings.complete("my-command ", 9);
|
||||||
|
let expected: Vec<String> = vec!["my-command".into()];
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn variables_completions_subcommands(mut completer_strings: NuCompleter) {
|
||||||
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn variables_completions_subcommands_2(mut completer_strings: NuCompleter) {
|
||||||
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
28
crates/nu-cli/tests/dotnu_completions.rs
Normal file
28
crates/nu-cli/tests/dotnu_completions.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::new_engine;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotnu_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
// Instatiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Test source completion
|
||||||
|
let completion_str = "source ".to_string();
|
||||||
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
|
assert_eq!(1, suggestions.len());
|
||||||
|
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
|
||||||
|
|
||||||
|
// Test use completion
|
||||||
|
let completion_str = "use ".to_string();
|
||||||
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
|
assert_eq!(1, suggestions.len());
|
||||||
|
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
|
||||||
|
}
|
||||||
272
crates/nu-cli/tests/file_completions.rs
Normal file
272
crates/nu-cli/tests/file_completions.rs
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{file, folder, match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn file_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, dir_str, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
// Instatiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Test completions for the current folder
|
||||||
|
let target_dir = format!("cp {}", dir_str);
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
file(dir.join("nushell")),
|
||||||
|
folder(dir.join("test_a")),
|
||||||
|
folder(dir.join("test_b")),
|
||||||
|
folder(dir.join("another")),
|
||||||
|
file(dir.join("custom_completion.nu")),
|
||||||
|
file(dir.join(".hidden_file")),
|
||||||
|
folder(dir.join(".hidden_folder")),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
|
||||||
|
// Test completions for a file
|
||||||
|
let target_dir = format!("cp {}", folder(dir.join("another")));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_ls_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "ls ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn command_open_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "open ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_rm_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "rm ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_cp_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "cp ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_save_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "save ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_touch_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "touch ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_watch_completion() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
let target_dir = "watch ";
|
||||||
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a\\".to_string(),
|
||||||
|
"test_b\\".to_string(),
|
||||||
|
"another\\".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder\\".to_string(),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
"nushell".to_string(),
|
||||||
|
"test_a/".to_string(),
|
||||||
|
"test_b/".to_string(),
|
||||||
|
"another/".to_string(),
|
||||||
|
"custom_completion.nu".to_string(),
|
||||||
|
".hidden_file".to_string(),
|
||||||
|
".hidden_folder/".to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match_suggestions(expected_paths, suggestions)
|
||||||
|
}
|
||||||
38
crates/nu-cli/tests/flag_completions.rs
Normal file
38
crates/nu-cli/tests/flag_completions.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flag_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
// Instatiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
// Test completions for the 'ls' flags
|
||||||
|
let suggestions = completer.complete("ls -", 4);
|
||||||
|
|
||||||
|
assert_eq!(14, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec![
|
||||||
|
"--all".into(),
|
||||||
|
"--directory".into(),
|
||||||
|
"--du".into(),
|
||||||
|
"--full-paths".into(),
|
||||||
|
"--help".into(),
|
||||||
|
"--long".into(),
|
||||||
|
"--short-names".into(),
|
||||||
|
"-D".into(),
|
||||||
|
"-a".into(),
|
||||||
|
"-d".into(),
|
||||||
|
"-f".into(),
|
||||||
|
"-h".into(),
|
||||||
|
"-l".into(),
|
||||||
|
"-s".into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
29
crates/nu-cli/tests/folder_completions.rs
Normal file
29
crates/nu-cli/tests/folder_completions.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{folder, match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn folder_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, dir_str, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
// Instatiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Test completions for the current folder
|
||||||
|
let target_dir = format!("cd {}", dir_str);
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
// Create the expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
folder(dir.join("test_a")),
|
||||||
|
folder(dir.join("test_b")),
|
||||||
|
folder(dir.join("another")),
|
||||||
|
folder(dir.join(".hidden_folder")),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match the results
|
||||||
|
match_suggestions(expected_paths, suggestions);
|
||||||
|
}
|
||||||
@@ -33,69 +33,20 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
|||||||
"PWD".to_string(),
|
"PWD".to_string(),
|
||||||
Value::String {
|
Value::String {
|
||||||
val: dir_str.clone(),
|
val: dir_str.clone(),
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
span: nu_protocol::Span {
|
||||||
|
start: 0,
|
||||||
|
end: dir_str.len(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"TEST".to_string(),
|
"TEST".to_string(),
|
||||||
Value::String {
|
Value::String {
|
||||||
val: "NUSHELL".to_string(),
|
val: "NUSHELL".to_string(),
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
span: nu_protocol::Span {
|
||||||
},
|
start: 0,
|
||||||
);
|
end: dir_str.len(),
|
||||||
#[cfg(windows)]
|
},
|
||||||
stack.add_env_var(
|
|
||||||
"Path".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "c:\\some\\path;c:\\some\\other\\path".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
stack.add_env_var(
|
|
||||||
"PATH".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "/some/path:/some/other/path".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
|
||||||
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
|
||||||
assert!(merge_result.is_ok());
|
|
||||||
|
|
||||||
(dir, dir_str, engine_state, stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
|
||||||
// Target folder inside assets
|
|
||||||
let dir = fs::fixtures().join("quoted_completions");
|
|
||||||
let mut dir_str = dir
|
|
||||||
.clone()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap_or_default();
|
|
||||||
dir_str.push(SEP);
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
|
||||||
let mut engine_state = create_default_context();
|
|
||||||
|
|
||||||
// New stack
|
|
||||||
let mut stack = Stack::new();
|
|
||||||
|
|
||||||
// Add pwd as env var
|
|
||||||
stack.add_env_var(
|
|
||||||
"PWD".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: dir_str.clone(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
stack.add_env_var(
|
|
||||||
"TEST".to_string(),
|
|
||||||
Value::String {
|
|
||||||
val: "NUSHELL".to_string(),
|
|
||||||
span: nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -153,7 +104,9 @@ pub fn merge_input(
|
|||||||
(block, working_set.render())
|
(block, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
engine_state.merge_delta(delta)?;
|
if let Err(err) = engine_state.merge_delta(delta) {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
assert!(eval_block(
|
assert!(eval_block(
|
||||||
engine_state,
|
engine_state,
|
||||||
@@ -161,7 +114,7 @@ pub fn merge_input(
|
|||||||
&block,
|
&block,
|
||||||
PipelineData::Value(
|
PipelineData::Value(
|
||||||
Value::Nothing {
|
Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
},
|
},
|
||||||
None
|
None
|
||||||
),
|
),
|
||||||
|
|||||||
88
crates/nu-cli/tests/variables_completions.rs
Normal file
88
crates/nu-cli/tests/variables_completions.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
pub mod support;
|
||||||
|
|
||||||
|
use nu_cli::NuCompleter;
|
||||||
|
use reedline::Completer;
|
||||||
|
use support::{match_suggestions, new_engine};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variables_completions() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Add record value as example
|
||||||
|
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
|
||||||
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instatiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Test completions for $nu
|
||||||
|
let suggestions = completer.complete("$nu.", 4);
|
||||||
|
|
||||||
|
assert_eq!(9, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec![
|
||||||
|
"config-path".into(),
|
||||||
|
"env-path".into(),
|
||||||
|
"history-path".into(),
|
||||||
|
"home-path".into(),
|
||||||
|
"loginshell-path".into(),
|
||||||
|
"os-info".into(),
|
||||||
|
"pid".into(),
|
||||||
|
"scope".into(),
|
||||||
|
"temp-path".into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
|
// Test completions for $nu.h (filter)
|
||||||
|
let suggestions = completer.complete("$nu.h", 5);
|
||||||
|
|
||||||
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
|
// Test completions for custom var
|
||||||
|
let suggestions = completer.complete("$actor.", 7);
|
||||||
|
|
||||||
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
|
// Test completions for custom var (filtering)
|
||||||
|
let suggestions = completer.complete("$actor.n", 8);
|
||||||
|
|
||||||
|
assert_eq!(1, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec!["name".into()];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
|
// Test completions for $env
|
||||||
|
let suggestions = completer.complete("$env.", 5);
|
||||||
|
|
||||||
|
assert_eq!(2, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec!["PWD".into(), "TEST".into()];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
|
||||||
|
// Test completions for $env
|
||||||
|
let suggestions = completer.complete("$env.T", 6);
|
||||||
|
|
||||||
|
assert_eq!(1, suggestions.len());
|
||||||
|
|
||||||
|
let expected: Vec<String> = vec!["TEST".into()];
|
||||||
|
|
||||||
|
// Match results
|
||||||
|
match_suggestions(expected, suggestions);
|
||||||
|
}
|
||||||
@@ -1,25 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
description = "Color configuration code used by Nushell"
|
description = "Color configuration code used by Nushell"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-config"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.76.0"
|
version = "0.66.1"
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
nu-protocol = { path = "../nu-protocol", version = "0.66.1" }
|
||||||
# used only for text_style Alignments
|
|
||||||
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
|
||||||
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.76.0" }
|
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.76.0" }
|
nu-json = { path = "../nu-json", version = "0.66.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.76.0" }
|
nu-table = { path = "../nu-table", version = "0.66.1" }
|
||||||
nu-json = { path="../nu-json", version = "0.76.0" }
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.76.0" }
|
|
||||||
|
|||||||
@@ -1,89 +1,418 @@
|
|||||||
use crate::{
|
use crate::nu_style::{color_from_hex, color_string_to_nustyle};
|
||||||
nu_style::{color_from_hex, lookup_style},
|
use nu_ansi_term::{Color, Style};
|
||||||
parse_nustyle, NuStyle,
|
use nu_protocol::Config;
|
||||||
};
|
use nu_table::{Alignment, TextStyle};
|
||||||
use nu_ansi_term::Style;
|
|
||||||
use nu_protocol::Value;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn lookup_ansi_color_style(s: &str) -> Style {
|
pub fn lookup_ansi_color_style(s: &str) -> Style {
|
||||||
if s.starts_with('#') {
|
if s.starts_with('#') {
|
||||||
color_from_hex(s)
|
match color_from_hex(s) {
|
||||||
.ok()
|
Ok(c) => match c {
|
||||||
.and_then(|c| c.map(|c| c.normal()))
|
Some(c) => c.normal(),
|
||||||
.unwrap_or_default()
|
None => Style::default(),
|
||||||
|
},
|
||||||
|
Err(_) => Style::default(),
|
||||||
|
}
|
||||||
} else if s.starts_with('{') {
|
} else if s.starts_with('{') {
|
||||||
color_string_to_nustyle(s.to_string())
|
color_string_to_nustyle(s.to_string())
|
||||||
} else {
|
} else {
|
||||||
lookup_style(s)
|
match s {
|
||||||
|
"g" | "green" => Color::Green.normal(),
|
||||||
|
"gb" | "green_bold" => Color::Green.bold(),
|
||||||
|
"gu" | "green_underline" => Color::Green.underline(),
|
||||||
|
"gi" | "green_italic" => Color::Green.italic(),
|
||||||
|
"gd" | "green_dimmed" => Color::Green.dimmed(),
|
||||||
|
"gr" | "green_reverse" => Color::Green.reverse(),
|
||||||
|
"gbl" | "green_blink" => Color::Green.blink(),
|
||||||
|
"gst" | "green_strike" => Color::Green.strikethrough(),
|
||||||
|
|
||||||
|
"lg" | "light_green" => Color::LightGreen.normal(),
|
||||||
|
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
|
||||||
|
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
|
||||||
|
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
|
||||||
|
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
|
||||||
|
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
|
||||||
|
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
|
||||||
|
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
|
||||||
|
|
||||||
|
"r" | "red" => Color::Red.normal(),
|
||||||
|
"rb" | "red_bold" => Color::Red.bold(),
|
||||||
|
"ru" | "red_underline" => Color::Red.underline(),
|
||||||
|
"ri" | "red_italic" => Color::Red.italic(),
|
||||||
|
"rd" | "red_dimmed" => Color::Red.dimmed(),
|
||||||
|
"rr" | "red_reverse" => Color::Red.reverse(),
|
||||||
|
"rbl" | "red_blink" => Color::Red.blink(),
|
||||||
|
"rst" | "red_strike" => Color::Red.strikethrough(),
|
||||||
|
|
||||||
|
"lr" | "light_red" => Color::LightRed.normal(),
|
||||||
|
"lrb" | "light_red_bold" => Color::LightRed.bold(),
|
||||||
|
"lru" | "light_red_underline" => Color::LightRed.underline(),
|
||||||
|
"lri" | "light_red_italic" => Color::LightRed.italic(),
|
||||||
|
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
|
||||||
|
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
|
||||||
|
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
|
||||||
|
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
|
||||||
|
|
||||||
|
"u" | "blue" => Color::Blue.normal(),
|
||||||
|
"ub" | "blue_bold" => Color::Blue.bold(),
|
||||||
|
"uu" | "blue_underline" => Color::Blue.underline(),
|
||||||
|
"ui" | "blue_italic" => Color::Blue.italic(),
|
||||||
|
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
|
||||||
|
"ur" | "blue_reverse" => Color::Blue.reverse(),
|
||||||
|
"ubl" | "blue_blink" => Color::Blue.blink(),
|
||||||
|
"ust" | "blue_strike" => Color::Blue.strikethrough(),
|
||||||
|
|
||||||
|
"lu" | "light_blue" => Color::LightBlue.normal(),
|
||||||
|
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
|
||||||
|
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
|
||||||
|
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
|
||||||
|
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
|
||||||
|
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
|
||||||
|
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
|
||||||
|
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
|
||||||
|
|
||||||
|
"b" | "black" => Color::Black.normal(),
|
||||||
|
"bb" | "black_bold" => Color::Black.bold(),
|
||||||
|
"bu" | "black_underline" => Color::Black.underline(),
|
||||||
|
"bi" | "black_italic" => Color::Black.italic(),
|
||||||
|
"bd" | "black_dimmed" => Color::Black.dimmed(),
|
||||||
|
"br" | "black_reverse" => Color::Black.reverse(),
|
||||||
|
"bbl" | "black_blink" => Color::Black.blink(),
|
||||||
|
"bst" | "black_strike" => Color::Black.strikethrough(),
|
||||||
|
|
||||||
|
"ligr" | "light_gray" => Color::LightGray.normal(),
|
||||||
|
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
|
||||||
|
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
|
||||||
|
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
|
||||||
|
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
|
||||||
|
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
|
||||||
|
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
|
||||||
|
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
|
||||||
|
|
||||||
|
"y" | "yellow" => Color::Yellow.normal(),
|
||||||
|
"yb" | "yellow_bold" => Color::Yellow.bold(),
|
||||||
|
"yu" | "yellow_underline" => Color::Yellow.underline(),
|
||||||
|
"yi" | "yellow_italic" => Color::Yellow.italic(),
|
||||||
|
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
|
||||||
|
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
|
||||||
|
"ybl" | "yellow_blink" => Color::Yellow.blink(),
|
||||||
|
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
|
||||||
|
|
||||||
|
"ly" | "light_yellow" => Color::LightYellow.normal(),
|
||||||
|
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
|
||||||
|
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
|
||||||
|
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
|
||||||
|
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
|
||||||
|
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
|
||||||
|
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
|
||||||
|
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
|
||||||
|
|
||||||
|
"p" | "purple" => Color::Purple.normal(),
|
||||||
|
"pb" | "purple_bold" => Color::Purple.bold(),
|
||||||
|
"pu" | "purple_underline" => Color::Purple.underline(),
|
||||||
|
"pi" | "purple_italic" => Color::Purple.italic(),
|
||||||
|
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
|
||||||
|
"pr" | "purple_reverse" => Color::Purple.reverse(),
|
||||||
|
"pbl" | "purple_blink" => Color::Purple.blink(),
|
||||||
|
"pst" | "purple_strike" => Color::Purple.strikethrough(),
|
||||||
|
|
||||||
|
"lp" | "light_purple" => Color::LightPurple.normal(),
|
||||||
|
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
|
||||||
|
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
|
||||||
|
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
|
||||||
|
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
|
||||||
|
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
|
||||||
|
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
|
||||||
|
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
|
||||||
|
|
||||||
|
"c" | "cyan" => Color::Cyan.normal(),
|
||||||
|
"cb" | "cyan_bold" => Color::Cyan.bold(),
|
||||||
|
"cu" | "cyan_underline" => Color::Cyan.underline(),
|
||||||
|
"ci" | "cyan_italic" => Color::Cyan.italic(),
|
||||||
|
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
|
||||||
|
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
|
||||||
|
"cbl" | "cyan_blink" => Color::Cyan.blink(),
|
||||||
|
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
|
||||||
|
|
||||||
|
"lc" | "light_cyan" => Color::LightCyan.normal(),
|
||||||
|
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
|
||||||
|
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
|
||||||
|
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
|
||||||
|
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
|
||||||
|
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
|
||||||
|
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
|
||||||
|
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
|
||||||
|
|
||||||
|
"w" | "white" => Color::White.normal(),
|
||||||
|
"wb" | "white_bold" => Color::White.bold(),
|
||||||
|
"wu" | "white_underline" => Color::White.underline(),
|
||||||
|
"wi" | "white_italic" => Color::White.italic(),
|
||||||
|
"wd" | "white_dimmed" => Color::White.dimmed(),
|
||||||
|
"wr" | "white_reverse" => Color::White.reverse(),
|
||||||
|
"wbl" | "white_blink" => Color::White.blink(),
|
||||||
|
"wst" | "white_strike" => Color::White.strikethrough(),
|
||||||
|
|
||||||
|
"dgr" | "dark_gray" => Color::DarkGray.normal(),
|
||||||
|
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
|
||||||
|
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
|
||||||
|
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
|
||||||
|
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
|
||||||
|
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
|
||||||
|
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
|
||||||
|
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
|
||||||
|
|
||||||
|
"def" | "default" => Color::Default.normal(),
|
||||||
|
"defb" | "default_bold" => Color::Default.bold(),
|
||||||
|
"defu" | "default_underline" => Color::Default.underline(),
|
||||||
|
"defi" | "default_italic" => Color::Default.italic(),
|
||||||
|
"defd" | "default_dimmed" => Color::Default.dimmed(),
|
||||||
|
"defr" | "default_reverse" => Color::Default.reverse(),
|
||||||
|
|
||||||
|
_ => Color::White.normal(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style> {
|
fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) {
|
||||||
let mut hm: HashMap<String, Style> = HashMap::new();
|
// eprintln!("key: {}, val: {}", &key, &val);
|
||||||
|
let color = lookup_ansi_color_style(val);
|
||||||
|
if let Some(v) = hm.get_mut(key) {
|
||||||
|
*v = color;
|
||||||
|
} else {
|
||||||
|
hm.insert(key.to_string(), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in colors {
|
pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
|
||||||
parse_map_entry(&mut hm, key, value);
|
let config = config;
|
||||||
|
|
||||||
|
// create the hashmap
|
||||||
|
let mut hm: HashMap<String, Style> = HashMap::new();
|
||||||
|
// set some defaults
|
||||||
|
// hm.insert("primitive_line".to_string(), Color::White.normal());
|
||||||
|
// hm.insert("primitive_pattern".to_string(), Color::White.normal());
|
||||||
|
// hm.insert("primitive_path".to_string(), Color::White.normal());
|
||||||
|
hm.insert("separator".to_string(), Color::White.normal());
|
||||||
|
hm.insert(
|
||||||
|
"leading_trailing_space_bg".to_string(),
|
||||||
|
Style::default().on(Color::Rgb(128, 128, 128)),
|
||||||
|
);
|
||||||
|
hm.insert("header".to_string(), Color::Green.bold());
|
||||||
|
hm.insert("empty".to_string(), Color::Blue.normal());
|
||||||
|
hm.insert("bool".to_string(), Color::White.normal());
|
||||||
|
hm.insert("int".to_string(), Color::White.normal());
|
||||||
|
hm.insert("filesize".to_string(), Color::White.normal());
|
||||||
|
hm.insert("duration".to_string(), Color::White.normal());
|
||||||
|
hm.insert("date".to_string(), Color::White.normal());
|
||||||
|
hm.insert("range".to_string(), Color::White.normal());
|
||||||
|
hm.insert("float".to_string(), Color::White.normal());
|
||||||
|
hm.insert("string".to_string(), Color::White.normal());
|
||||||
|
hm.insert("nothing".to_string(), Color::White.normal());
|
||||||
|
hm.insert("binary".to_string(), Color::White.normal());
|
||||||
|
hm.insert("cellpath".to_string(), Color::White.normal());
|
||||||
|
hm.insert("row_index".to_string(), Color::Green.bold());
|
||||||
|
hm.insert("record".to_string(), Color::White.normal());
|
||||||
|
hm.insert("list".to_string(), Color::White.normal());
|
||||||
|
hm.insert("block".to_string(), Color::White.normal());
|
||||||
|
hm.insert("hints".to_string(), Color::DarkGray.normal());
|
||||||
|
|
||||||
|
for (key, value) in &config.color_config {
|
||||||
|
let value = value
|
||||||
|
.as_string()
|
||||||
|
.expect("the only values for config color must be strings");
|
||||||
|
update_hashmap(key, &value, &mut hm);
|
||||||
|
|
||||||
|
// eprintln!(
|
||||||
|
// "config: {}:{}\t\t\thashmap: {}:{:?}",
|
||||||
|
// &key, &value, &key, &hm[key]
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
hm
|
hm
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_map_entry(hm: &mut HashMap<String, Style>, key: &str, value: &Value) {
|
// This function will assign a text style to a primitive, or really any string that's
|
||||||
let value = match value {
|
// in the hashmap. The hashmap actually contains the style to be applied.
|
||||||
Value::String { val, .. } => Some(lookup_ansi_color_style(val)),
|
pub fn style_primitive(primitive: &str, color_hm: &HashMap<String, Style>) -> TextStyle {
|
||||||
Value::Record { cols, vals, .. } => get_style_from_value(cols, vals).map(parse_nustyle),
|
match primitive {
|
||||||
_ => None,
|
"bool" => {
|
||||||
};
|
let style = color_hm.get(primitive);
|
||||||
if let Some(value) = value {
|
match style {
|
||||||
hm.entry(key.to_owned()).or_insert(value);
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
}
|
None => TextStyle::basic_left(),
|
||||||
}
|
|
||||||
|
|
||||||
fn get_style_from_value(cols: &[String], vals: &[Value]) -> Option<NuStyle> {
|
|
||||||
let mut was_set = false;
|
|
||||||
let mut style = NuStyle::from(Style::default());
|
|
||||||
for (col, val) in cols.iter().zip(vals) {
|
|
||||||
match col.as_str() {
|
|
||||||
"bg" => {
|
|
||||||
if let Value::String { val, .. } = val {
|
|
||||||
style.bg = Some(val.clone());
|
|
||||||
was_set = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"fg" => {
|
|
||||||
if let Value::String { val, .. } = val {
|
|
||||||
style.fg = Some(val.clone());
|
|
||||||
was_set = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"attr" => {
|
|
||||||
if let Value::String { val, .. } = val {
|
|
||||||
style.attr = Some(val.clone());
|
|
||||||
was_set = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if was_set {
|
"int" => {
|
||||||
Some(style)
|
let style = color_hm.get(primitive);
|
||||||
} else {
|
match style {
|
||||||
None
|
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
||||||
|
None => TextStyle::basic_right(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"filesize" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
||||||
|
None => TextStyle::basic_right(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"duration" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"date" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"range" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"float" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
||||||
|
None => TextStyle::basic_right(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"string" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"nothing" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not sure what to do with error
|
||||||
|
// "error" => {}
|
||||||
|
"binary" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"cellpath" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"row_index" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
||||||
|
None => TextStyle::new()
|
||||||
|
.alignment(Alignment::Right)
|
||||||
|
.fg(Color::Green)
|
||||||
|
.bold(Some(true)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"record" | "list" | "block" => {
|
||||||
|
let style = color_hm.get(primitive);
|
||||||
|
match style {
|
||||||
|
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
None => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// types in nushell but not in engine-q
|
||||||
|
// "Line" => {
|
||||||
|
// let style = color_hm.get("Primitive::Line");
|
||||||
|
// match style {
|
||||||
|
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
// None => TextStyle::basic_left(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "GlobPattern" => {
|
||||||
|
// let style = color_hm.get("Primitive::GlobPattern");
|
||||||
|
// match style {
|
||||||
|
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
// None => TextStyle::basic_left(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "FilePath" => {
|
||||||
|
// let style = color_hm.get("Primitive::FilePath");
|
||||||
|
// match style {
|
||||||
|
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
// None => TextStyle::basic_left(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "BeginningOfStream" => {
|
||||||
|
// let style = color_hm.get("Primitive::BeginningOfStream");
|
||||||
|
// match style {
|
||||||
|
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
// None => TextStyle::basic_left(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// "EndOfStream" => {
|
||||||
|
// let style = color_hm.get("Primitive::EndOfStream");
|
||||||
|
// match style {
|
||||||
|
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
||||||
|
// None => TextStyle::basic_left(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
_ => TextStyle::basic_left(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_string_to_nustyle(color_string: String) -> Style {
|
#[test]
|
||||||
// eprintln!("color_string: {}", &color_string);
|
fn test_hm() {
|
||||||
if color_string.is_empty() {
|
use nu_ansi_term::{Color, Style};
|
||||||
return Style::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
|
let mut hm: HashMap<String, Style> = HashMap::new();
|
||||||
Ok(s) => s,
|
hm.insert("primitive_int".to_string(), Color::White.normal());
|
||||||
Err(_) => return Style::default(),
|
hm.insert("primitive_decimal".to_string(), Color::White.normal());
|
||||||
};
|
hm.insert("primitive_filesize".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_string".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_line".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_columnpath".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_pattern".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_boolean".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_date".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_duration".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_range".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_path".to_string(), Color::White.normal());
|
||||||
|
hm.insert("primitive_binary".to_string(), Color::White.normal());
|
||||||
|
hm.insert("separator".to_string(), Color::White.normal());
|
||||||
|
hm.insert("header_align".to_string(), Color::Green.bold());
|
||||||
|
hm.insert("header".to_string(), Color::Green.bold());
|
||||||
|
hm.insert("header_style".to_string(), Style::default());
|
||||||
|
hm.insert("row_index".to_string(), Color::Green.bold());
|
||||||
|
hm.insert(
|
||||||
|
"leading_trailing_space_bg".to_string(),
|
||||||
|
Style::default().on(Color::Rgb(128, 128, 128)),
|
||||||
|
);
|
||||||
|
|
||||||
parse_nustyle(nu_style)
|
update_hashmap("primitive_int", "green", &mut hm);
|
||||||
|
|
||||||
|
assert_eq!(hm["primitive_int"], Color::Green.normal());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
mod color_config;
|
mod color_config;
|
||||||
mod matching_brackets_style;
|
|
||||||
mod nu_style;
|
mod nu_style;
|
||||||
mod shape_color;
|
mod shape_color;
|
||||||
mod style_computer;
|
|
||||||
mod text_style;
|
|
||||||
|
|
||||||
pub use color_config::*;
|
pub use color_config::*;
|
||||||
pub use matching_brackets_style::*;
|
|
||||||
pub use nu_style::*;
|
pub use nu_style::*;
|
||||||
pub use shape_color::*;
|
pub use shape_color::*;
|
||||||
pub use style_computer::*;
|
|
||||||
pub use text_style::*;
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
use crate::color_config::lookup_ansi_color_style;
|
|
||||||
use nu_ansi_term::Style;
|
|
||||||
use nu_protocol::Config;
|
|
||||||
|
|
||||||
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
|
|
||||||
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
|
|
||||||
|
|
||||||
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
|
|
||||||
Some(int_color) => match int_color.as_string() {
|
|
||||||
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
|
|
||||||
Err(_) => default_style,
|
|
||||||
},
|
|
||||||
None => default_style,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_styles(base: Style, extra: Style) -> Style {
|
|
||||||
Style {
|
|
||||||
foreground: extra.foreground.or(base.foreground),
|
|
||||||
background: extra.background.or(base.background),
|
|
||||||
is_bold: extra.is_bold || base.is_bold,
|
|
||||||
is_dimmed: extra.is_dimmed || base.is_dimmed,
|
|
||||||
is_italic: extra.is_italic || base.is_italic,
|
|
||||||
is_underline: extra.is_underline || base.is_underline,
|
|
||||||
is_blink: extra.is_blink || base.is_blink,
|
|
||||||
is_reverse: extra.is_reverse || base.is_reverse,
|
|
||||||
is_hidden: extra.is_hidden || base.is_hidden,
|
|
||||||
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +1,88 @@
|
|||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use nu_protocol::Value;
|
use serde::Deserialize;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
|
#[derive(Deserialize, PartialEq, Eq, Debug)]
|
||||||
pub struct NuStyle {
|
pub struct NuStyle {
|
||||||
pub fg: Option<String>,
|
pub fg: Option<String>,
|
||||||
pub bg: Option<String>,
|
pub bg: Option<String>,
|
||||||
pub attr: Option<String>,
|
pub attr: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Style> for NuStyle {
|
|
||||||
fn from(s: Style) -> Self {
|
|
||||||
Self {
|
|
||||||
bg: s.background.and_then(color_to_string),
|
|
||||||
fg: s.foreground.and_then(color_to_string),
|
|
||||||
attr: style_get_attr(s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn style_get_attr(s: Style) -> Option<String> {
|
|
||||||
macro_rules! check {
|
|
||||||
($attrs:expr, $b:expr, $c:expr) => {
|
|
||||||
if $b {
|
|
||||||
$attrs.push($c);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut attrs = String::new();
|
|
||||||
|
|
||||||
check!(attrs, s.is_blink, 'l');
|
|
||||||
check!(attrs, s.is_bold, 'b');
|
|
||||||
check!(attrs, s.is_dimmed, 'd');
|
|
||||||
check!(attrs, s.is_reverse, 'r');
|
|
||||||
check!(attrs, s.is_strikethrough, 's');
|
|
||||||
check!(attrs, s.is_underline, 'u');
|
|
||||||
|
|
||||||
if attrs.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(attrs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color_to_string(color: Color) -> Option<String> {
|
|
||||||
match color {
|
|
||||||
Color::Black => Some(String::from("black")),
|
|
||||||
Color::DarkGray => Some(String::from("dark_gray")),
|
|
||||||
Color::Red => Some(String::from("red")),
|
|
||||||
Color::LightRed => Some(String::from("light_red")),
|
|
||||||
Color::Green => Some(String::from("green")),
|
|
||||||
Color::LightGreen => Some(String::from("light_green")),
|
|
||||||
Color::Yellow => Some(String::from("yellow")),
|
|
||||||
Color::LightYellow => Some(String::from("light_yellow")),
|
|
||||||
Color::Blue => Some(String::from("blue")),
|
|
||||||
Color::LightBlue => Some(String::from("light_blue")),
|
|
||||||
Color::Purple => Some(String::from("purple")),
|
|
||||||
Color::LightPurple => Some(String::from("light_purple")),
|
|
||||||
Color::Magenta => Some(String::from("magenta")),
|
|
||||||
Color::LightMagenta => Some(String::from("light_magenta")),
|
|
||||||
Color::Cyan => Some(String::from("cyan")),
|
|
||||||
Color::LightCyan => Some(String::from("light_cyan")),
|
|
||||||
Color::White => Some(String::from("white")),
|
|
||||||
Color::LightGray => Some(String::from("light_gray")),
|
|
||||||
Color::Default => Some(String::from("default")),
|
|
||||||
Color::Rgb(r, g, b) => Some(format!("#{r:X}{g:X}{b:X}")),
|
|
||||||
Color::Fixed(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
|
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
|
||||||
let mut style = Style {
|
// get the nu_ansi_term::Color foreground color
|
||||||
foreground: nu_style.fg.and_then(|fg| lookup_color_str(&fg)),
|
let fg_color = match nu_style.fg {
|
||||||
background: nu_style.bg.and_then(|bg| lookup_color_str(&bg)),
|
Some(fg) => color_from_hex(&fg).expect("error with foreground color"),
|
||||||
..Default::default()
|
_ => None,
|
||||||
|
};
|
||||||
|
// get the nu_ansi_term::Color background color
|
||||||
|
let bg_color = match nu_style.bg {
|
||||||
|
Some(bg) => color_from_hex(&bg).expect("error with background color"),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
// get the attributes
|
||||||
|
let color_attr = match nu_style.attr {
|
||||||
|
Some(attr) => attr,
|
||||||
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(attrs) = nu_style.attr {
|
// setup the attributes available in nu_ansi_term::Style
|
||||||
fill_modifiers(&attrs, &mut style)
|
let mut bold = false;
|
||||||
}
|
let mut dimmed = false;
|
||||||
|
let mut italic = false;
|
||||||
|
let mut underline = false;
|
||||||
|
let mut blink = false;
|
||||||
|
let mut reverse = false;
|
||||||
|
let mut hidden = false;
|
||||||
|
let mut strikethrough = false;
|
||||||
|
|
||||||
style
|
// since we can combine styles like bold-italic, iterate through the chars
|
||||||
}
|
// and set the bools for later use in the nu_ansi_term::Style application
|
||||||
|
for ch in color_attr.to_lowercase().chars() {
|
||||||
// Converts the color_config records, { fg, bg, attr }, into a Style.
|
match ch {
|
||||||
pub fn color_record_to_nustyle(value: &Value) -> Style {
|
'l' => blink = true,
|
||||||
let mut fg = None;
|
'b' => bold = true,
|
||||||
let mut bg = None;
|
'd' => dimmed = true,
|
||||||
let mut attr = None;
|
'h' => hidden = true,
|
||||||
let v = value.as_record();
|
'i' => italic = true,
|
||||||
if let Ok((cols, inner_vals)) = v {
|
'r' => reverse = true,
|
||||||
for (k, v) in cols.iter().zip(inner_vals) {
|
's' => strikethrough = true,
|
||||||
// Because config already type-checked the color_config records, this doesn't bother giving errors
|
'u' => underline = true,
|
||||||
// if there are unrecognised keys or bad values.
|
'n' => (),
|
||||||
if let Ok(v) = v.as_string() {
|
_ => (),
|
||||||
match k.as_str() {
|
|
||||||
"fg" => fg = Some(v),
|
|
||||||
|
|
||||||
"bg" => bg = Some(v),
|
|
||||||
|
|
||||||
"attr" => attr = Some(v),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_nustyle(NuStyle { fg, bg, attr })
|
// here's where we build the nu_ansi_term::Style
|
||||||
|
Style {
|
||||||
|
foreground: fg_color,
|
||||||
|
background: bg_color,
|
||||||
|
is_blink: blink,
|
||||||
|
is_bold: bold,
|
||||||
|
is_dimmed: dimmed,
|
||||||
|
is_hidden: hidden,
|
||||||
|
is_italic: italic,
|
||||||
|
is_reverse: reverse,
|
||||||
|
is_strikethrough: strikethrough,
|
||||||
|
is_underline: underline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color_string_to_nustyle(color_string: String) -> Style {
|
||||||
|
// eprintln!("color_string: {}", &color_string);
|
||||||
|
if color_string.chars().count() < 1 {
|
||||||
|
Style::default()
|
||||||
|
} else {
|
||||||
|
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => NuStyle {
|
||||||
|
fg: None,
|
||||||
|
bg: None,
|
||||||
|
attr: None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
parse_nustyle(nu_style)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color_from_hex(
|
pub fn color_from_hex(
|
||||||
@@ -126,471 +101,3 @@ pub fn color_from_hex(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_style(s: &str) -> Style {
|
|
||||||
match s {
|
|
||||||
"g" | "green" => Color::Green.normal(),
|
|
||||||
"gb" | "green_bold" => Color::Green.bold(),
|
|
||||||
"gu" | "green_underline" => Color::Green.underline(),
|
|
||||||
"gi" | "green_italic" => Color::Green.italic(),
|
|
||||||
"gd" | "green_dimmed" => Color::Green.dimmed(),
|
|
||||||
"gr" | "green_reverse" => Color::Green.reverse(),
|
|
||||||
"gbl" | "green_blink" => Color::Green.blink(),
|
|
||||||
"gst" | "green_strike" => Color::Green.strikethrough(),
|
|
||||||
|
|
||||||
"lg" | "light_green" => Color::LightGreen.normal(),
|
|
||||||
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
|
|
||||||
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
|
|
||||||
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
|
|
||||||
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
|
|
||||||
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
|
|
||||||
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
|
|
||||||
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
|
|
||||||
|
|
||||||
"r" | "red" => Color::Red.normal(),
|
|
||||||
"rb" | "red_bold" => Color::Red.bold(),
|
|
||||||
"ru" | "red_underline" => Color::Red.underline(),
|
|
||||||
"ri" | "red_italic" => Color::Red.italic(),
|
|
||||||
"rd" | "red_dimmed" => Color::Red.dimmed(),
|
|
||||||
"rr" | "red_reverse" => Color::Red.reverse(),
|
|
||||||
"rbl" | "red_blink" => Color::Red.blink(),
|
|
||||||
"rst" | "red_strike" => Color::Red.strikethrough(),
|
|
||||||
|
|
||||||
"lr" | "light_red" => Color::LightRed.normal(),
|
|
||||||
"lrb" | "light_red_bold" => Color::LightRed.bold(),
|
|
||||||
"lru" | "light_red_underline" => Color::LightRed.underline(),
|
|
||||||
"lri" | "light_red_italic" => Color::LightRed.italic(),
|
|
||||||
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
|
|
||||||
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
|
|
||||||
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
|
|
||||||
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
|
|
||||||
|
|
||||||
"u" | "blue" => Color::Blue.normal(),
|
|
||||||
"ub" | "blue_bold" => Color::Blue.bold(),
|
|
||||||
"uu" | "blue_underline" => Color::Blue.underline(),
|
|
||||||
"ui" | "blue_italic" => Color::Blue.italic(),
|
|
||||||
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
|
|
||||||
"ur" | "blue_reverse" => Color::Blue.reverse(),
|
|
||||||
"ubl" | "blue_blink" => Color::Blue.blink(),
|
|
||||||
"ust" | "blue_strike" => Color::Blue.strikethrough(),
|
|
||||||
|
|
||||||
"lu" | "light_blue" => Color::LightBlue.normal(),
|
|
||||||
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
|
|
||||||
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
|
|
||||||
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
|
|
||||||
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
|
|
||||||
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
|
|
||||||
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
|
|
||||||
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
|
|
||||||
|
|
||||||
"b" | "black" => Color::Black.normal(),
|
|
||||||
"bb" | "black_bold" => Color::Black.bold(),
|
|
||||||
"bu" | "black_underline" => Color::Black.underline(),
|
|
||||||
"bi" | "black_italic" => Color::Black.italic(),
|
|
||||||
"bd" | "black_dimmed" => Color::Black.dimmed(),
|
|
||||||
"br" | "black_reverse" => Color::Black.reverse(),
|
|
||||||
"bbl" | "black_blink" => Color::Black.blink(),
|
|
||||||
"bst" | "black_strike" => Color::Black.strikethrough(),
|
|
||||||
|
|
||||||
"ligr" | "light_gray" => Color::LightGray.normal(),
|
|
||||||
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
|
|
||||||
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
|
|
||||||
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
|
|
||||||
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
|
|
||||||
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
|
|
||||||
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
|
|
||||||
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
|
|
||||||
|
|
||||||
"y" | "yellow" => Color::Yellow.normal(),
|
|
||||||
"yb" | "yellow_bold" => Color::Yellow.bold(),
|
|
||||||
"yu" | "yellow_underline" => Color::Yellow.underline(),
|
|
||||||
"yi" | "yellow_italic" => Color::Yellow.italic(),
|
|
||||||
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
|
|
||||||
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
|
|
||||||
"ybl" | "yellow_blink" => Color::Yellow.blink(),
|
|
||||||
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
|
|
||||||
|
|
||||||
"ly" | "light_yellow" => Color::LightYellow.normal(),
|
|
||||||
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
|
|
||||||
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
|
|
||||||
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
|
|
||||||
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
|
|
||||||
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
|
|
||||||
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
|
|
||||||
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
|
|
||||||
|
|
||||||
"p" | "purple" => Color::Purple.normal(),
|
|
||||||
"pb" | "purple_bold" => Color::Purple.bold(),
|
|
||||||
"pu" | "purple_underline" => Color::Purple.underline(),
|
|
||||||
"pi" | "purple_italic" => Color::Purple.italic(),
|
|
||||||
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
|
|
||||||
"pr" | "purple_reverse" => Color::Purple.reverse(),
|
|
||||||
"pbl" | "purple_blink" => Color::Purple.blink(),
|
|
||||||
"pst" | "purple_strike" => Color::Purple.strikethrough(),
|
|
||||||
|
|
||||||
"lp" | "light_purple" => Color::LightPurple.normal(),
|
|
||||||
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
|
|
||||||
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
|
|
||||||
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
|
|
||||||
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
|
|
||||||
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
|
|
||||||
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
|
|
||||||
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
|
|
||||||
|
|
||||||
"c" | "cyan" => Color::Cyan.normal(),
|
|
||||||
"cb" | "cyan_bold" => Color::Cyan.bold(),
|
|
||||||
"cu" | "cyan_underline" => Color::Cyan.underline(),
|
|
||||||
"ci" | "cyan_italic" => Color::Cyan.italic(),
|
|
||||||
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
|
|
||||||
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
|
|
||||||
"cbl" | "cyan_blink" => Color::Cyan.blink(),
|
|
||||||
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
|
|
||||||
|
|
||||||
"lc" | "light_cyan" => Color::LightCyan.normal(),
|
|
||||||
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
|
|
||||||
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
|
|
||||||
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
|
|
||||||
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
|
|
||||||
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
|
|
||||||
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
|
|
||||||
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
|
|
||||||
|
|
||||||
"w" | "white" => Color::White.normal(),
|
|
||||||
"wb" | "white_bold" => Color::White.bold(),
|
|
||||||
"wu" | "white_underline" => Color::White.underline(),
|
|
||||||
"wi" | "white_italic" => Color::White.italic(),
|
|
||||||
"wd" | "white_dimmed" => Color::White.dimmed(),
|
|
||||||
"wr" | "white_reverse" => Color::White.reverse(),
|
|
||||||
"wbl" | "white_blink" => Color::White.blink(),
|
|
||||||
"wst" | "white_strike" => Color::White.strikethrough(),
|
|
||||||
|
|
||||||
"dgr" | "dark_gray" => Color::DarkGray.normal(),
|
|
||||||
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
|
|
||||||
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
|
|
||||||
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
|
|
||||||
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
|
|
||||||
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
|
|
||||||
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
|
|
||||||
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
|
|
||||||
|
|
||||||
"def" | "default" => Color::Default.normal(),
|
|
||||||
"defb" | "default_bold" => Color::Default.bold(),
|
|
||||||
"defu" | "default_underline" => Color::Default.underline(),
|
|
||||||
"defi" | "default_italic" => Color::Default.italic(),
|
|
||||||
"defd" | "default_dimmed" => Color::Default.dimmed(),
|
|
||||||
"defr" | "default_reverse" => Color::Default.reverse(),
|
|
||||||
|
|
||||||
// Add xterm 256 colors adding an x prefix where the name conflicts
|
|
||||||
"xblack" | "xterm_black" => Color::Fixed(0).normal(),
|
|
||||||
"maroon" | "xterm_maroon" => Color::Fixed(1).normal(),
|
|
||||||
"xgreen" | "xterm_green" => Color::Fixed(2).normal(),
|
|
||||||
"olive" | "xterm_olive" => Color::Fixed(3).normal(),
|
|
||||||
"navy" | "xterm_navy" => Color::Fixed(4).normal(),
|
|
||||||
"xpurplea" | "xterm_purplea" => Color::Fixed(5).normal(),
|
|
||||||
"teal" | "xterm_teal" => Color::Fixed(6).normal(),
|
|
||||||
"silver" | "xterm_silver" => Color::Fixed(7).normal(),
|
|
||||||
"grey" | "xterm_grey" => Color::Fixed(8).normal(),
|
|
||||||
"xred" | "xterm_red" => Color::Fixed(9).normal(),
|
|
||||||
"lime" | "xterm_lime" => Color::Fixed(10).normal(),
|
|
||||||
"xyellow" | "xterm_yellow" => Color::Fixed(11).normal(),
|
|
||||||
"xblue" | "xterm_blue" => Color::Fixed(12).normal(),
|
|
||||||
"fuchsia" | "xterm_fuchsia" => Color::Fixed(13).normal(),
|
|
||||||
"aqua" | "xterm_aqua" => Color::Fixed(14).normal(),
|
|
||||||
"xwhite" | "xterm_white" => Color::Fixed(15).normal(),
|
|
||||||
"grey0" | "xterm_grey0" => Color::Fixed(16).normal(),
|
|
||||||
"navyblue" | "xterm_navyblue" => Color::Fixed(17).normal(),
|
|
||||||
"darkblue" | "xterm_darkblue" => Color::Fixed(18).normal(),
|
|
||||||
"blue3a" | "xterm_blue3a" => Color::Fixed(19).normal(),
|
|
||||||
"blue3b" | "xterm_blue3b" => Color::Fixed(20).normal(),
|
|
||||||
"blue1" | "xterm_blue1" => Color::Fixed(21).normal(),
|
|
||||||
"darkgreen" | "xterm_darkgreen" => Color::Fixed(22).normal(),
|
|
||||||
"deepskyblue4a" | "xterm_deepskyblue4a" => Color::Fixed(23).normal(),
|
|
||||||
"deepskyblue4b" | "xterm_deepskyblue4b" => Color::Fixed(24).normal(),
|
|
||||||
"deepskyblue4c" | "xterm_deepskyblue4c" => Color::Fixed(25).normal(),
|
|
||||||
"dodgerblue3" | "xterm_dodgerblue3" => Color::Fixed(26).normal(),
|
|
||||||
"dodgerblue2" | "xterm_dodgerblue2" => Color::Fixed(27).normal(),
|
|
||||||
"green4" | "xterm_green4" => Color::Fixed(28).normal(),
|
|
||||||
"springgreen4" | "xterm_springgreen4" => Color::Fixed(29).normal(),
|
|
||||||
"turquoise4" | "xterm_turquoise4" => Color::Fixed(30).normal(),
|
|
||||||
"deepskyblue3a" | "xterm_deepskyblue3a" => Color::Fixed(31).normal(),
|
|
||||||
"deepskyblue3b" | "xterm_deepskyblue3b" => Color::Fixed(32).normal(),
|
|
||||||
"dodgerblue1" | "xterm_dodgerblue1" => Color::Fixed(33).normal(),
|
|
||||||
"green3a" | "xterm_green3a" => Color::Fixed(34).normal(),
|
|
||||||
"springgreen3a" | "xterm_springgreen3a" => Color::Fixed(35).normal(),
|
|
||||||
"darkcyan" | "xterm_darkcyan" => Color::Fixed(36).normal(),
|
|
||||||
"lightseagreen" | "xterm_lightseagreen" => Color::Fixed(37).normal(),
|
|
||||||
"deepskyblue2" | "xterm_deepskyblue2" => Color::Fixed(38).normal(),
|
|
||||||
"deepskyblue1" | "xterm_deepskyblue1" => Color::Fixed(39).normal(),
|
|
||||||
"green3b" | "xterm_green3b" => Color::Fixed(40).normal(),
|
|
||||||
"springgreen3b" | "xterm_springgreen3b" => Color::Fixed(41).normal(),
|
|
||||||
"springgreen2a" | "xterm_springgreen2a" => Color::Fixed(42).normal(),
|
|
||||||
"cyan3" | "xterm_cyan3" => Color::Fixed(43).normal(),
|
|
||||||
"darkturquoise" | "xterm_darkturquoise" => Color::Fixed(44).normal(),
|
|
||||||
"turquoise2" | "xterm_turquoise2" => Color::Fixed(45).normal(),
|
|
||||||
"green1" | "xterm_green1" => Color::Fixed(46).normal(),
|
|
||||||
"springgreen2b" | "xterm_springgreen2b" => Color::Fixed(47).normal(),
|
|
||||||
"springgreen1" | "xterm_springgreen1" => Color::Fixed(48).normal(),
|
|
||||||
"mediumspringgreen" | "xterm_mediumspringgreen" => Color::Fixed(49).normal(),
|
|
||||||
"cyan2" | "xterm_cyan2" => Color::Fixed(50).normal(),
|
|
||||||
"cyan1" | "xterm_cyan1" => Color::Fixed(51).normal(),
|
|
||||||
"darkreda" | "xterm_darkreda" => Color::Fixed(52).normal(),
|
|
||||||
"deeppink4a" | "xterm_deeppink4a" => Color::Fixed(53).normal(),
|
|
||||||
"purple4a" | "xterm_purple4a" => Color::Fixed(54).normal(),
|
|
||||||
"purple4b" | "xterm_purple4b" => Color::Fixed(55).normal(),
|
|
||||||
"purple3" | "xterm_purple3" => Color::Fixed(56).normal(),
|
|
||||||
"blueviolet" | "xterm_blueviolet" => Color::Fixed(57).normal(),
|
|
||||||
"orange4a" | "xterm_orange4a" => Color::Fixed(58).normal(),
|
|
||||||
"grey37" | "xterm_grey37" => Color::Fixed(59).normal(),
|
|
||||||
"mediumpurple4" | "xterm_mediumpurple4" => Color::Fixed(60).normal(),
|
|
||||||
"slateblue3a" | "xterm_slateblue3a" => Color::Fixed(61).normal(),
|
|
||||||
"slateblue3b" | "xterm_slateblue3b" => Color::Fixed(62).normal(),
|
|
||||||
"royalblue1" | "xterm_royalblue1" => Color::Fixed(63).normal(),
|
|
||||||
"chartreuse4" | "xterm_chartreuse4" => Color::Fixed(64).normal(),
|
|
||||||
"darkseagreen4a" | "xterm_darkseagreen4a" => Color::Fixed(65).normal(),
|
|
||||||
"paleturquoise4" | "xterm_paleturquoise4" => Color::Fixed(66).normal(),
|
|
||||||
"steelblue" | "xterm_steelblue" => Color::Fixed(67).normal(),
|
|
||||||
"steelblue3" | "xterm_steelblue3" => Color::Fixed(68).normal(),
|
|
||||||
"cornflowerblue" | "xterm_cornflowerblue" => Color::Fixed(69).normal(),
|
|
||||||
"chartreuse3a" | "xterm_chartreuse3a" => Color::Fixed(70).normal(),
|
|
||||||
"darkseagreen4b" | "xterm_darkseagreen4b" => Color::Fixed(71).normal(),
|
|
||||||
"cadetbluea" | "xterm_cadetbluea" => Color::Fixed(72).normal(),
|
|
||||||
"cadetblueb" | "xterm_cadetblueb" => Color::Fixed(73).normal(),
|
|
||||||
"skyblue3" | "xterm_skyblue3" => Color::Fixed(74).normal(),
|
|
||||||
"steelblue1a" | "xterm_steelblue1a" => Color::Fixed(75).normal(),
|
|
||||||
"chartreuse3b" | "xterm_chartreuse3b" => Color::Fixed(76).normal(),
|
|
||||||
"palegreen3a" | "xterm_palegreen3a" => Color::Fixed(77).normal(),
|
|
||||||
"seagreen3" | "xterm_seagreen3" => Color::Fixed(78).normal(),
|
|
||||||
"aquamarine3" | "xterm_aquamarine3" => Color::Fixed(79).normal(),
|
|
||||||
"mediumturquoise" | "xterm_mediumturquoise" => Color::Fixed(80).normal(),
|
|
||||||
"steelblue1b" | "xterm_steelblue1b" => Color::Fixed(81).normal(),
|
|
||||||
"chartreuse2a" | "xterm_chartreuse2a" => Color::Fixed(82).normal(),
|
|
||||||
"seagreen2" | "xterm_seagreen2" => Color::Fixed(83).normal(),
|
|
||||||
"seagreen1a" | "xterm_seagreen1a" => Color::Fixed(84).normal(),
|
|
||||||
"seagreen1b" | "xterm_seagreen1b" => Color::Fixed(85).normal(),
|
|
||||||
"aquamarine1a" | "xterm_aquamarine1a" => Color::Fixed(86).normal(),
|
|
||||||
"darkslategray2" | "xterm_darkslategray2" => Color::Fixed(87).normal(),
|
|
||||||
"darkredb" | "xterm_darkredb" => Color::Fixed(88).normal(),
|
|
||||||
"deeppink4b" | "xterm_deeppink4b" => Color::Fixed(89).normal(),
|
|
||||||
"darkmagentaa" | "xterm_darkmagentaa" => Color::Fixed(90).normal(),
|
|
||||||
"darkmagentab" | "xterm_darkmagentab" => Color::Fixed(91).normal(),
|
|
||||||
"darkvioleta" | "xterm_darkvioleta" => Color::Fixed(92).normal(),
|
|
||||||
"xpurpleb" | "xterm_purpleb" => Color::Fixed(93).normal(),
|
|
||||||
"orange4b" | "xterm_orange4b" => Color::Fixed(94).normal(),
|
|
||||||
"lightpink4" | "xterm_lightpink4" => Color::Fixed(95).normal(),
|
|
||||||
"plum4" | "xterm_plum4" => Color::Fixed(96).normal(),
|
|
||||||
"mediumpurple3a" | "xterm_mediumpurple3a" => Color::Fixed(97).normal(),
|
|
||||||
"mediumpurple3b" | "xterm_mediumpurple3b" => Color::Fixed(98).normal(),
|
|
||||||
"slateblue1" | "xterm_slateblue1" => Color::Fixed(99).normal(),
|
|
||||||
"yellow4a" | "xterm_yellow4a" => Color::Fixed(100).normal(),
|
|
||||||
"wheat4" | "xterm_wheat4" => Color::Fixed(101).normal(),
|
|
||||||
"grey53" | "xterm_grey53" => Color::Fixed(102).normal(),
|
|
||||||
"lightslategrey" | "xterm_lightslategrey" => Color::Fixed(103).normal(),
|
|
||||||
"mediumpurple" | "xterm_mediumpurple" => Color::Fixed(104).normal(),
|
|
||||||
"lightslateblue" | "xterm_lightslateblue" => Color::Fixed(105).normal(),
|
|
||||||
"yellow4b" | "xterm_yellow4b" => Color::Fixed(106).normal(),
|
|
||||||
"darkolivegreen3a" | "xterm_darkolivegreen3a" => Color::Fixed(107).normal(),
|
|
||||||
"darkseagreen" | "xterm_darkseagreen" => Color::Fixed(108).normal(),
|
|
||||||
"lightskyblue3a" | "xterm_lightskyblue3a" => Color::Fixed(109).normal(),
|
|
||||||
"lightskyblue3b" | "xterm_lightskyblue3b" => Color::Fixed(110).normal(),
|
|
||||||
"skyblue2" | "xterm_skyblue2" => Color::Fixed(111).normal(),
|
|
||||||
"chartreuse2b" | "xterm_chartreuse2b" => Color::Fixed(112).normal(),
|
|
||||||
"darkolivegreen3b" | "xterm_darkolivegreen3b" => Color::Fixed(113).normal(),
|
|
||||||
"palegreen3b" | "xterm_palegreen3b" => Color::Fixed(114).normal(),
|
|
||||||
"darkseagreen3a" | "xterm_darkseagreen3a" => Color::Fixed(115).normal(),
|
|
||||||
"darkslategray3" | "xterm_darkslategray3" => Color::Fixed(116).normal(),
|
|
||||||
"skyblue1" | "xterm_skyblue1" => Color::Fixed(117).normal(),
|
|
||||||
"chartreuse1" | "xterm_chartreuse1" => Color::Fixed(118).normal(),
|
|
||||||
"lightgreena" | "xterm_lightgreena" => Color::Fixed(119).normal(),
|
|
||||||
"lightgreenb" | "xterm_lightgreenb" => Color::Fixed(120).normal(),
|
|
||||||
"palegreen1a" | "xterm_palegreen1a" => Color::Fixed(121).normal(),
|
|
||||||
"aquamarine1b" | "xterm_aquamarine1b" => Color::Fixed(122).normal(),
|
|
||||||
"darkslategray1" | "xterm_darkslategray1" => Color::Fixed(123).normal(),
|
|
||||||
"red3a" | "xterm_red3a" => Color::Fixed(124).normal(),
|
|
||||||
"deeppink4c" | "xterm_deeppink4c" => Color::Fixed(125).normal(),
|
|
||||||
"mediumvioletred" | "xterm_mediumvioletred" => Color::Fixed(126).normal(),
|
|
||||||
"magenta3" | "xterm_magenta3" => Color::Fixed(127).normal(),
|
|
||||||
"darkvioletb" | "xterm_darkvioletb" => Color::Fixed(128).normal(),
|
|
||||||
"purplec" | "xterm_purplec" => Color::Fixed(129).normal(),
|
|
||||||
"darkorange3a" | "xterm_darkorange3a" => Color::Fixed(130).normal(),
|
|
||||||
"indianreda" | "xterm_indianreda" => Color::Fixed(131).normal(),
|
|
||||||
"hotpink3a" | "xterm_hotpink3a" => Color::Fixed(132).normal(),
|
|
||||||
"mediumorchid3" | "xterm_mediumorchid3" => Color::Fixed(133).normal(),
|
|
||||||
"mediumorchid" | "xterm_mediumorchid" => Color::Fixed(134).normal(),
|
|
||||||
"mediumpurple2a" | "xterm_mediumpurple2a" => Color::Fixed(135).normal(),
|
|
||||||
"darkgoldenrod" | "xterm_darkgoldenrod" => Color::Fixed(136).normal(),
|
|
||||||
"lightsalmon3a" | "xterm_lightsalmon3a" => Color::Fixed(137).normal(),
|
|
||||||
"rosybrown" | "xterm_rosybrown" => Color::Fixed(138).normal(),
|
|
||||||
"grey63" | "xterm_grey63" => Color::Fixed(139).normal(),
|
|
||||||
"mediumpurple2b" | "xterm_mediumpurple2b" => Color::Fixed(140).normal(),
|
|
||||||
"mediumpurple1" | "xterm_mediumpurple1" => Color::Fixed(141).normal(),
|
|
||||||
"gold3a" | "xterm_gold3a" => Color::Fixed(142).normal(),
|
|
||||||
"darkkhaki" | "xterm_darkkhaki" => Color::Fixed(143).normal(),
|
|
||||||
"navajowhite3" | "xterm_navajowhite3" => Color::Fixed(144).normal(),
|
|
||||||
"grey69" | "xterm_grey69" => Color::Fixed(145).normal(),
|
|
||||||
"lightsteelblue3" | "xterm_lightsteelblue3" => Color::Fixed(146).normal(),
|
|
||||||
"lightsteelblue" | "xterm_lightsteelblue" => Color::Fixed(147).normal(),
|
|
||||||
"yellow3a" | "xterm_yellow3a" => Color::Fixed(148).normal(),
|
|
||||||
"darkolivegreen3c" | "xterm_darkolivegreen3c" => Color::Fixed(149).normal(),
|
|
||||||
"darkseagreen3b" | "xterm_darkseagreen3b" => Color::Fixed(150).normal(),
|
|
||||||
"darkseagreen2a" | "xterm_darkseagreen2a" => Color::Fixed(151).normal(),
|
|
||||||
"lightcyan3" | "xterm_lightcyan3" => Color::Fixed(152).normal(),
|
|
||||||
"lightskyblue1" | "xterm_lightskyblue1" => Color::Fixed(153).normal(),
|
|
||||||
"greenyellow" | "xterm_greenyellow" => Color::Fixed(154).normal(),
|
|
||||||
"darkolivegreen2" | "xterm_darkolivegreen2" => Color::Fixed(155).normal(),
|
|
||||||
"palegreen1b" | "xterm_palegreen1b" => Color::Fixed(156).normal(),
|
|
||||||
"darkseagreen2b" | "xterm_darkseagreen2b" => Color::Fixed(157).normal(),
|
|
||||||
"darkseagreen1a" | "xterm_darkseagreen1a" => Color::Fixed(158).normal(),
|
|
||||||
"paleturquoise1" | "xterm_paleturquoise1" => Color::Fixed(159).normal(),
|
|
||||||
"red3b" | "xterm_red3b" => Color::Fixed(160).normal(),
|
|
||||||
"deeppink3a" | "xterm_deeppink3a" => Color::Fixed(161).normal(),
|
|
||||||
"deeppink3b" | "xterm_deeppink3b" => Color::Fixed(162).normal(),
|
|
||||||
"magenta3a" | "xterm_magenta3a" => Color::Fixed(163).normal(),
|
|
||||||
"magenta3b" | "xterm_magenta3b" => Color::Fixed(164).normal(),
|
|
||||||
"magenta2a" | "xterm_magenta2a" => Color::Fixed(165).normal(),
|
|
||||||
"darkorange3b" | "xterm_darkorange3b" => Color::Fixed(166).normal(),
|
|
||||||
"indianredb" | "xterm_indianredb" => Color::Fixed(167).normal(),
|
|
||||||
"hotpink3b" | "xterm_hotpink3b" => Color::Fixed(168).normal(),
|
|
||||||
"hotpink2" | "xterm_hotpink2" => Color::Fixed(169).normal(),
|
|
||||||
"orchid" | "xterm_orchid" => Color::Fixed(170).normal(),
|
|
||||||
"mediumorchid1a" | "xterm_mediumorchid1a" => Color::Fixed(171).normal(),
|
|
||||||
"orange3" | "xterm_orange3" => Color::Fixed(172).normal(),
|
|
||||||
"lightsalmon3b" | "xterm_lightsalmon3b" => Color::Fixed(173).normal(),
|
|
||||||
"lightpink3" | "xterm_lightpink3" => Color::Fixed(174).normal(),
|
|
||||||
"pink3" | "xterm_pink3" => Color::Fixed(175).normal(),
|
|
||||||
"plum3" | "xterm_plum3" => Color::Fixed(176).normal(),
|
|
||||||
"violet" | "xterm_violet" => Color::Fixed(177).normal(),
|
|
||||||
"gold3b" | "xterm_gold3b" => Color::Fixed(178).normal(),
|
|
||||||
"lightgoldenrod3" | "xterm_lightgoldenrod3" => Color::Fixed(179).normal(),
|
|
||||||
"tan" | "xterm_tan" => Color::Fixed(180).normal(),
|
|
||||||
"mistyrose3" | "xterm_mistyrose3" => Color::Fixed(181).normal(),
|
|
||||||
"thistle3" | "xterm_thistle3" => Color::Fixed(182).normal(),
|
|
||||||
"plum2" | "xterm_plum2" => Color::Fixed(183).normal(),
|
|
||||||
"yellow3b" | "xterm_yellow3b" => Color::Fixed(184).normal(),
|
|
||||||
"khaki3" | "xterm_khaki3" => Color::Fixed(185).normal(),
|
|
||||||
"lightgoldenrod2" | "xterm_lightgoldenrod2" => Color::Fixed(186).normal(),
|
|
||||||
"lightyellow3" | "xterm_lightyellow3" => Color::Fixed(187).normal(),
|
|
||||||
"grey84" | "xterm_grey84" => Color::Fixed(188).normal(),
|
|
||||||
"lightsteelblue1" | "xterm_lightsteelblue1" => Color::Fixed(189).normal(),
|
|
||||||
"yellow2" | "xterm_yellow2" => Color::Fixed(190).normal(),
|
|
||||||
"darkolivegreen1a" | "xterm_darkolivegreen1a" => Color::Fixed(191).normal(),
|
|
||||||
"darkolivegreen1b" | "xterm_darkolivegreen1b" => Color::Fixed(192).normal(),
|
|
||||||
"darkseagreen1b" | "xterm_darkseagreen1b" => Color::Fixed(193).normal(),
|
|
||||||
"honeydew2" | "xterm_honeydew2" => Color::Fixed(194).normal(),
|
|
||||||
"lightcyan1" | "xterm_lightcyan1" => Color::Fixed(195).normal(),
|
|
||||||
"red1" | "xterm_red1" => Color::Fixed(196).normal(),
|
|
||||||
"deeppink2" | "xterm_deeppink2" => Color::Fixed(197).normal(),
|
|
||||||
"deeppink1a" | "xterm_deeppink1a" => Color::Fixed(198).normal(),
|
|
||||||
"deeppink1b" | "xterm_deeppink1b" => Color::Fixed(199).normal(),
|
|
||||||
"magenta2b" | "xterm_magenta2b" => Color::Fixed(200).normal(),
|
|
||||||
"magenta1" | "xterm_magenta1" => Color::Fixed(201).normal(),
|
|
||||||
"orangered1" | "xterm_orangered1" => Color::Fixed(202).normal(),
|
|
||||||
"indianred1a" | "xterm_indianred1a" => Color::Fixed(203).normal(),
|
|
||||||
"indianred1b" | "xterm_indianred1b" => Color::Fixed(204).normal(),
|
|
||||||
"hotpinka" | "xterm_hotpinka" => Color::Fixed(205).normal(),
|
|
||||||
"hotpinkb" | "xterm_hotpinkb" => Color::Fixed(206).normal(),
|
|
||||||
"mediumorchid1b" | "xterm_mediumorchid1b" => Color::Fixed(207).normal(),
|
|
||||||
"darkorange" | "xterm_darkorange" => Color::Fixed(208).normal(),
|
|
||||||
"salmon1" | "xterm_salmon1" => Color::Fixed(209).normal(),
|
|
||||||
"lightcoral" | "xterm_lightcoral" => Color::Fixed(210).normal(),
|
|
||||||
"palevioletred1" | "xterm_palevioletred1" => Color::Fixed(211).normal(),
|
|
||||||
"orchid2" | "xterm_orchid2" => Color::Fixed(212).normal(),
|
|
||||||
"orchid1" | "xterm_orchid1" => Color::Fixed(213).normal(),
|
|
||||||
"orange1" | "xterm_orange1" => Color::Fixed(214).normal(),
|
|
||||||
"sandybrown" | "xterm_sandybrown" => Color::Fixed(215).normal(),
|
|
||||||
"lightsalmon1" | "xterm_lightsalmon1" => Color::Fixed(216).normal(),
|
|
||||||
"lightpink1" | "xterm_lightpink1" => Color::Fixed(217).normal(),
|
|
||||||
"pink1" | "xterm_pink1" => Color::Fixed(218).normal(),
|
|
||||||
"plum1" | "xterm_plum1" => Color::Fixed(219).normal(),
|
|
||||||
"gold1" | "xterm_gold1" => Color::Fixed(220).normal(),
|
|
||||||
"lightgoldenrod2a" | "xterm_lightgoldenrod2a" => Color::Fixed(221).normal(),
|
|
||||||
"lightgoldenrod2b" | "xterm_lightgoldenrod2b" => Color::Fixed(222).normal(),
|
|
||||||
"navajowhite1" | "xterm_navajowhite1" => Color::Fixed(223).normal(),
|
|
||||||
"mistyrose1" | "xterm_mistyrose1" => Color::Fixed(224).normal(),
|
|
||||||
"thistle1" | "xterm_thistle1" => Color::Fixed(225).normal(),
|
|
||||||
"yellow1" | "xterm_yellow1" => Color::Fixed(226).normal(),
|
|
||||||
"lightgoldenrod1" | "xterm_lightgoldenrod1" => Color::Fixed(227).normal(),
|
|
||||||
"khaki1" | "xterm_khaki1" => Color::Fixed(228).normal(),
|
|
||||||
"wheat1" | "xterm_wheat1" => Color::Fixed(229).normal(),
|
|
||||||
"cornsilk1" | "xterm_cornsilk1" => Color::Fixed(230).normal(),
|
|
||||||
"grey100" | "xterm_grey100" => Color::Fixed(231).normal(),
|
|
||||||
"grey3" | "xterm_grey3" => Color::Fixed(232).normal(),
|
|
||||||
"grey7" | "xterm_grey7" => Color::Fixed(233).normal(),
|
|
||||||
"grey11" | "xterm_grey11" => Color::Fixed(234).normal(),
|
|
||||||
"grey15" | "xterm_grey15" => Color::Fixed(235).normal(),
|
|
||||||
"grey19" | "xterm_grey19" => Color::Fixed(236).normal(),
|
|
||||||
"grey23" | "xterm_grey23" => Color::Fixed(237).normal(),
|
|
||||||
"grey27" | "xterm_grey27" => Color::Fixed(238).normal(),
|
|
||||||
"grey30" | "xterm_grey30" => Color::Fixed(239).normal(),
|
|
||||||
"grey35" | "xterm_grey35" => Color::Fixed(240).normal(),
|
|
||||||
"grey39" | "xterm_grey39" => Color::Fixed(241).normal(),
|
|
||||||
"grey42" | "xterm_grey42" => Color::Fixed(242).normal(),
|
|
||||||
"grey46" | "xterm_grey46" => Color::Fixed(243).normal(),
|
|
||||||
"grey50" | "xterm_grey50" => Color::Fixed(244).normal(),
|
|
||||||
"grey54" | "xterm_grey54" => Color::Fixed(245).normal(),
|
|
||||||
"grey58" | "xterm_grey58" => Color::Fixed(246).normal(),
|
|
||||||
"grey62" | "xterm_grey62" => Color::Fixed(247).normal(),
|
|
||||||
"grey66" | "xterm_grey66" => Color::Fixed(248).normal(),
|
|
||||||
"grey70" | "xterm_grey70" => Color::Fixed(249).normal(),
|
|
||||||
"grey74" | "xterm_grey74" => Color::Fixed(250).normal(),
|
|
||||||
"grey78" | "xterm_grey78" => Color::Fixed(251).normal(),
|
|
||||||
"grey82" | "xterm_grey82" => Color::Fixed(252).normal(),
|
|
||||||
"grey85" | "xterm_grey85" => Color::Fixed(253).normal(),
|
|
||||||
"grey89" | "xterm_grey89" => Color::Fixed(254).normal(),
|
|
||||||
"grey93" | "xterm_grey93" => Color::Fixed(255).normal(),
|
|
||||||
_ => Color::White.normal(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_color(s: &str) -> Option<Color> {
|
|
||||||
let color = match s {
|
|
||||||
"g" | "green" => Color::Green,
|
|
||||||
"lg" | "light_green" => Color::LightGreen,
|
|
||||||
"r" | "red" => Color::Red,
|
|
||||||
"lr" | "light_red" => Color::LightRed,
|
|
||||||
"u" | "blue" => Color::Blue,
|
|
||||||
"lu" | "light_blue" => Color::LightBlue,
|
|
||||||
"b" | "black" => Color::Black,
|
|
||||||
"ligr" | "light_gray" => Color::LightGray,
|
|
||||||
"y" | "yellow" => Color::Yellow,
|
|
||||||
"ly" | "light_yellow" => Color::LightYellow,
|
|
||||||
"p" | "purple" => Color::Purple,
|
|
||||||
"lp" | "light_purple" => Color::LightPurple,
|
|
||||||
"c" | "cyan" => Color::Cyan,
|
|
||||||
"lc" | "light_cyan" => Color::LightCyan,
|
|
||||||
"w" | "white" => Color::White,
|
|
||||||
"dgr" | "dark_gray" => Color::DarkGray,
|
|
||||||
"def" | "default" => Color::Default,
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_modifiers(attrs: &str, style: &mut Style) {
|
|
||||||
// setup the attributes available in nu_ansi_term::Style
|
|
||||||
//
|
|
||||||
// since we can combine styles like bold-italic, iterate through the chars
|
|
||||||
// and set the bools for later use in the nu_ansi_term::Style application
|
|
||||||
for ch in attrs.to_lowercase().chars() {
|
|
||||||
match ch {
|
|
||||||
'l' => style.is_blink = true,
|
|
||||||
'b' => style.is_bold = true,
|
|
||||||
'd' => style.is_dimmed = true,
|
|
||||||
'h' => style.is_hidden = true,
|
|
||||||
'i' => style.is_italic = true,
|
|
||||||
'r' => style.is_reverse = true,
|
|
||||||
's' => style.is_strikethrough = true,
|
|
||||||
'u' => style.is_underline = true,
|
|
||||||
'n' => (),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_color_str(s: &str) -> Option<Color> {
|
|
||||||
if s.starts_with('#') {
|
|
||||||
color_from_hex(s).ok().and_then(|c| c)
|
|
||||||
} else {
|
|
||||||
lookup_color(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,56 +1,41 @@
|
|||||||
use crate::{color_config::lookup_ansi_color_style, color_record_to_nustyle};
|
use crate::color_config::lookup_ansi_color_style;
|
||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use nu_protocol::{Config, Value};
|
use nu_protocol::Config;
|
||||||
|
|
||||||
// The default colors for shapes, used when there is no config for them.
|
|
||||||
pub fn default_shape_color(shape: String) -> Style {
|
|
||||||
match shape.as_ref() {
|
|
||||||
"shape_and" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_binary" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_block" => Style::new().fg(Color::Blue).bold(),
|
|
||||||
"shape_bool" => Style::new().fg(Color::LightCyan),
|
|
||||||
"shape_custom" => Style::new().fg(Color::Green),
|
|
||||||
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_directory" => Style::new().fg(Color::Cyan),
|
|
||||||
"shape_external" => Style::new().fg(Color::Cyan),
|
|
||||||
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
|
|
||||||
"shape_filepath" => Style::new().fg(Color::Cyan),
|
|
||||||
"shape_flag" => Style::new().fg(Color::Blue).bold(),
|
|
||||||
"shape_float" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
|
|
||||||
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_int" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_literal" => Style::new().fg(Color::Blue),
|
|
||||||
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
|
||||||
"shape_operator" => Style::new().fg(Color::Yellow),
|
|
||||||
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_range" => Style::new().fg(Color::Yellow).bold(),
|
|
||||||
"shape_record" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
|
|
||||||
"shape_signature" => Style::new().fg(Color::Green).bold(),
|
|
||||||
"shape_string" => Style::new().fg(Color::Green),
|
|
||||||
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
|
|
||||||
"shape_table" => Style::new().fg(Color::Blue).bold(),
|
|
||||||
"shape_variable" => Style::new().fg(Color::Purple),
|
|
||||||
_ => Style::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_shape_color(shape: String, conf: &Config) -> Style {
|
pub fn get_shape_color(shape: String, conf: &Config) -> Style {
|
||||||
match conf.color_config.get(shape.as_str()) {
|
match conf.color_config.get(shape.as_str()) {
|
||||||
Some(int_color) => {
|
Some(int_color) => match int_color.as_string() {
|
||||||
// Shapes do not use color_config closures, currently.
|
Ok(int_color) => lookup_ansi_color_style(&int_color),
|
||||||
match int_color {
|
Err(_) => Style::default(),
|
||||||
Value::Record { .. } => color_record_to_nustyle(int_color),
|
},
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(val),
|
None => match shape.as_ref() {
|
||||||
// Defer to the default in the event of incorrect types being given
|
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
|
||||||
// (i.e. treat null, etc. as the value being unset)
|
"shape_binary" => Style::new().fg(Color::Purple).bold(),
|
||||||
_ => default_shape_color(shape),
|
"shape_bool" => Style::new().fg(Color::LightCyan),
|
||||||
}
|
"shape_int" => Style::new().fg(Color::Purple).bold(),
|
||||||
}
|
"shape_float" => Style::new().fg(Color::Purple).bold(),
|
||||||
None => default_shape_color(shape),
|
"shape_range" => Style::new().fg(Color::Yellow).bold(),
|
||||||
|
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_external" => Style::new().fg(Color::Cyan),
|
||||||
|
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
|
||||||
|
"shape_literal" => Style::new().fg(Color::Blue),
|
||||||
|
"shape_operator" => Style::new().fg(Color::Yellow),
|
||||||
|
"shape_signature" => Style::new().fg(Color::Green).bold(),
|
||||||
|
"shape_string" => Style::new().fg(Color::Green),
|
||||||
|
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_list" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_table" => Style::new().fg(Color::Blue).bold(),
|
||||||
|
"shape_record" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_block" => Style::new().fg(Color::Blue).bold(),
|
||||||
|
"shape_filepath" => Style::new().fg(Color::Cyan),
|
||||||
|
"shape_directory" => Style::new().fg(Color::Cyan),
|
||||||
|
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
|
||||||
|
"shape_variable" => Style::new().fg(Color::Purple),
|
||||||
|
"shape_flag" => Style::new().fg(Color::Blue).bold(),
|
||||||
|
"shape_custom" => Style::new().fg(Color::Green),
|
||||||
|
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
||||||
|
_ => Style::default(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,285 +0,0 @@
|
|||||||
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
|
|
||||||
use nu_ansi_term::{Color, Style};
|
|
||||||
use nu_engine::eval_block;
|
|
||||||
use nu_protocol::{
|
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
|
||||||
CliError, IntoPipelineData, Value,
|
|
||||||
};
|
|
||||||
use tabled::alignment::AlignmentHorizontal;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
fmt::{Debug, Formatter, Result},
|
|
||||||
};
|
|
||||||
|
|
||||||
// ComputableStyle represents the valid user style types: a single color value, or a closure which
|
|
||||||
// takes an input value and produces a color value. The latter represents a value which
|
|
||||||
// is computed at use-time.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ComputableStyle {
|
|
||||||
Static(Style),
|
|
||||||
Closure(Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
// macro used for adding initial values to the style hashmap
|
|
||||||
macro_rules! initial {
|
|
||||||
($a:expr, $b:expr) => {
|
|
||||||
($a.to_string(), ComputableStyle::Static($b))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// An alias for the mapping used internally by StyleComputer.
|
|
||||||
pub type StyleMapping = HashMap<String, ComputableStyle>;
|
|
||||||
//
|
|
||||||
// A StyleComputer is an all-in-one way to compute styles. A nu command can
|
|
||||||
// simply create it with from_config(), and then use it with compute().
|
|
||||||
// It stores the engine state and stack needed to run closures that
|
|
||||||
// may be defined as a user style.
|
|
||||||
//
|
|
||||||
pub struct StyleComputer<'a> {
|
|
||||||
engine_state: &'a EngineState,
|
|
||||||
stack: &'a Stack,
|
|
||||||
map: StyleMapping,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StyleComputer<'a> {
|
|
||||||
// This is NOT meant to be used in most cases - please use from_config() instead.
|
|
||||||
// This only exists for testing purposes.
|
|
||||||
pub fn new(
|
|
||||||
engine_state: &'a EngineState,
|
|
||||||
stack: &'a Stack,
|
|
||||||
map: StyleMapping,
|
|
||||||
) -> StyleComputer<'a> {
|
|
||||||
StyleComputer {
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
map,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The main method. Takes a string name which maps to a color_config style name,
|
|
||||||
// and a Nu value to pipe into any closures that may have been defined there.
|
|
||||||
pub fn compute(&self, style_name: &str, value: &Value) -> Style {
|
|
||||||
match self.map.get(style_name) {
|
|
||||||
// Static values require no computation.
|
|
||||||
Some(ComputableStyle::Static(s)) => *s,
|
|
||||||
// Closures are run here.
|
|
||||||
Some(ComputableStyle::Closure(Value::Closure {
|
|
||||||
val: block_id,
|
|
||||||
captures,
|
|
||||||
span,
|
|
||||||
})) => {
|
|
||||||
let block = self.engine_state.get_block(*block_id).clone();
|
|
||||||
// Because captures_to_stack() clones, we don't need to use with_env() here
|
|
||||||
// (contrast with_env() usage in `each` or `do`).
|
|
||||||
let mut stack = self.stack.captures_to_stack(captures);
|
|
||||||
|
|
||||||
// Support 1-argument blocks as well as 0-argument blocks.
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the block.
|
|
||||||
match eval_block(
|
|
||||||
self.engine_state,
|
|
||||||
&mut stack,
|
|
||||||
&block,
|
|
||||||
value.clone().into_pipeline_data(),
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
) {
|
|
||||||
Ok(v) => {
|
|
||||||
let value = v.into_value(*span);
|
|
||||||
// These should be the same color data forms supported by color_config.
|
|
||||||
match value {
|
|
||||||
Value::Record { .. } => color_record_to_nustyle(&value),
|
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
|
||||||
_ => Style::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
|
|
||||||
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
|
|
||||||
// currently hook closure errors behave roughly the same.
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!(
|
|
||||||
"Error: {:?}",
|
|
||||||
CliError(&e, &StateWorkingSet::new(self.engine_state))
|
|
||||||
);
|
|
||||||
Style::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
|
|
||||||
// so this is just a fallback.
|
|
||||||
_ => Style::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used only by the `table` command.
|
|
||||||
pub fn style_primitive(&self, value: &Value) -> TextStyle {
|
|
||||||
let s = self.compute(&value.get_type().to_string(), value);
|
|
||||||
match *value {
|
|
||||||
Value::Bool { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Int { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
|
||||||
|
|
||||||
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
|
||||||
|
|
||||||
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
|
||||||
|
|
||||||
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Range { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Float { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
|
||||||
|
|
||||||
Value::String { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Nothing { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Binary { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::CellPath { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
|
||||||
|
|
||||||
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
|
|
||||||
TextStyle::with_style(AlignmentHorizontal::Left, s)
|
|
||||||
}
|
|
||||||
_ => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The main constructor.
|
|
||||||
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
|
|
||||||
let config = engine_state.get_config();
|
|
||||||
|
|
||||||
// Create the hashmap
|
|
||||||
let mut map: StyleMapping = HashMap::from([
|
|
||||||
initial!("separator", Color::White.normal()),
|
|
||||||
initial!(
|
|
||||||
"leading_trailing_space_bg",
|
|
||||||
Style::default().on(Color::Rgb(128, 128, 128))
|
|
||||||
),
|
|
||||||
initial!("header", Color::White.normal()),
|
|
||||||
initial!("empty", Color::White.normal()),
|
|
||||||
initial!("bool", Color::White.normal()),
|
|
||||||
initial!("int", Color::White.normal()),
|
|
||||||
initial!("filesize", Color::White.normal()),
|
|
||||||
initial!("duration", Color::White.normal()),
|
|
||||||
initial!("date", Color::White.normal()),
|
|
||||||
initial!("range", Color::White.normal()),
|
|
||||||
initial!("float", Color::White.normal()),
|
|
||||||
initial!("string", Color::White.normal()),
|
|
||||||
initial!("nothing", Color::White.normal()),
|
|
||||||
initial!("binary", Color::White.normal()),
|
|
||||||
initial!("cellpath", Color::White.normal()),
|
|
||||||
initial!("row_index", Color::Green.bold()),
|
|
||||||
initial!("record", Color::White.normal()),
|
|
||||||
initial!("list", Color::White.normal()),
|
|
||||||
initial!("block", Color::White.normal()),
|
|
||||||
initial!("hints", Color::DarkGray.normal()),
|
|
||||||
]);
|
|
||||||
|
|
||||||
for (key, value) in &config.color_config {
|
|
||||||
match value {
|
|
||||||
Value::Closure { .. } => {
|
|
||||||
map.insert(key.to_string(), ComputableStyle::Closure(value.clone()));
|
|
||||||
}
|
|
||||||
Value::Record { .. } => {
|
|
||||||
map.insert(
|
|
||||||
key.to_string(),
|
|
||||||
ComputableStyle::Static(color_record_to_nustyle(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Value::String { val, .. } => {
|
|
||||||
// update the stylemap with the found key
|
|
||||||
let color = lookup_ansi_color_style(val.as_str());
|
|
||||||
if let Some(v) = map.get_mut(key) {
|
|
||||||
*v = ComputableStyle::Static(color);
|
|
||||||
} else {
|
|
||||||
map.insert(key.to_string(), ComputableStyle::Static(color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This should never occur.
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StyleComputer::new(engine_state, stack, map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because EngineState doesn't have Debug (Dec 2022),
|
|
||||||
// this incomplete representation must be used.
|
|
||||||
impl<'a> Debug for StyleComputer<'a> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
||||||
f.debug_struct("StyleComputer")
|
|
||||||
.field("map", &self.map)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_computable_style_static() {
|
|
||||||
use nu_protocol::Span;
|
|
||||||
|
|
||||||
let style1 = Style::default().italic();
|
|
||||||
let style2 = Style::default().underline();
|
|
||||||
// Create a "dummy" style_computer for this test.
|
|
||||||
let dummy_engine_state = EngineState::new();
|
|
||||||
let dummy_stack = Stack::new();
|
|
||||||
let style_computer = StyleComputer::new(
|
|
||||||
&dummy_engine_state,
|
|
||||||
&dummy_stack,
|
|
||||||
HashMap::from([
|
|
||||||
("string".into(), ComputableStyle::Static(style1)),
|
|
||||||
("row_index".into(), ComputableStyle::Static(style2)),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
style_computer.compute("string", &Value::nothing(Span::unknown())),
|
|
||||||
style1
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
style_computer.compute("row_index", &Value::nothing(Span::unknown())),
|
|
||||||
style2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Because each closure currently runs in a separate environment, checks that the closures have run
|
|
||||||
// must use the filesystem.
|
|
||||||
#[test]
|
|
||||||
fn test_computable_style_closure_basic() {
|
|
||||||
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
|
||||||
Playground::setup("computable_style_closure_basic", |dirs, _| {
|
|
||||||
let inp = [
|
|
||||||
r#"let-env config = {
|
|
||||||
color_config: {
|
|
||||||
string: {|e| touch ($e + '.obj'); 'red' }
|
|
||||||
}
|
|
||||||
};"#,
|
|
||||||
"[bell book candle] | table | ignore",
|
|
||||||
"ls | get name | to nuon",
|
|
||||||
];
|
|
||||||
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
|
||||||
assert_eq!(actual_repl.err, "");
|
|
||||||
assert_eq!(actual_repl.out, "[bell.obj, book.obj, candle.obj]");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_computable_style_closure_errors() {
|
|
||||||
use nu_test_support::{nu, nu_repl_code};
|
|
||||||
let inp = [
|
|
||||||
r#"let-env config = {
|
|
||||||
color_config: {
|
|
||||||
string: {|e| $e + 2 }
|
|
||||||
}
|
|
||||||
};"#,
|
|
||||||
"[bell] | table",
|
|
||||||
];
|
|
||||||
let actual_repl = nu!(cwd: ".", nu_repl_code(&inp));
|
|
||||||
// Check that the error was printed
|
|
||||||
assert!(actual_repl.err.contains("type mismatch for operator"));
|
|
||||||
// Check that the value was printed
|
|
||||||
assert!(actual_repl.out.contains("bell"));
|
|
||||||
}
|
|
||||||
@@ -1,168 +1,136 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
build = "build.rs"
|
|
||||||
description = "Nushell's built-in commands"
|
description = "Nushell's built-in commands"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
version = "0.66.1"
|
||||||
version = "0.76.0"
|
build = "build.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[lib]
|
|
||||||
bench = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.66.1" }
|
||||||
|
nu-engine = { path = "../nu-engine", version = "0.66.1" }
|
||||||
|
nu-glob = { path = "../nu-glob", version = "0.66.1" }
|
||||||
|
nu-json = { path = "../nu-json", version = "0.66.1" }
|
||||||
|
nu-parser = { path = "../nu-parser", version = "0.66.1" }
|
||||||
|
nu-path = { path = "../nu-path", version = "0.66.1" }
|
||||||
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.66.1" }
|
||||||
|
nu-protocol = { path = "../nu-protocol", version = "0.66.1" }
|
||||||
|
nu-system = { path = "../nu-system", version = "0.66.1" }
|
||||||
|
nu-table = { path = "../nu-table", version = "0.66.1" }
|
||||||
|
nu-term-grid = { path = "../nu-term-grid", version = "0.66.1" }
|
||||||
|
nu-test-support = { path = "../nu-test-support", version = "0.66.1" }
|
||||||
|
nu-utils = { path = "../nu-utils", version = "0.66.1" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.76.0" }
|
|
||||||
nu-engine = { path = "../nu-engine", version = "0.76.0" }
|
|
||||||
nu-explore = { path = "../nu-explore", version = "0.76.0" }
|
|
||||||
nu-glob = { path = "../nu-glob", version = "0.76.0" }
|
|
||||||
nu-json = { path = "../nu-json", version = "0.76.0" }
|
|
||||||
nu-parser = { path = "../nu-parser", version = "0.76.0" }
|
|
||||||
nu-path = { path = "../nu-path", version = "0.76.0" }
|
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.76.0" }
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.76.0" }
|
|
||||||
nu-system = { path = "../nu-system", version = "0.76.0" }
|
|
||||||
nu-table = { path = "../nu-table", version = "0.76.0" }
|
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.76.0" }
|
|
||||||
nu-utils = { path = "../nu-utils", version = "0.76.0" }
|
|
||||||
num-format = { version = "0.4.3" }
|
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
Inflector = "0.11"
|
|
||||||
alphanumeric-sort = "1.4.4"
|
alphanumeric-sort = "1.4.4"
|
||||||
atty = "0.2.14"
|
base64 = "0.13.0"
|
||||||
base64 = "0.21.0"
|
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
calamine = "0.19.1"
|
calamine = "0.18.0"
|
||||||
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
chrono-humanize = "0.2.1"
|
chrono-humanize = "0.2.1"
|
||||||
chrono-tz = "0.8.1"
|
chrono-tz = "0.6.1"
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.23.0"
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
dialoguer = { default-features = false, version = "0.10.3" }
|
dialoguer = "0.9.0"
|
||||||
digest = { default-features = false, version = "0.10.0" }
|
digest = "0.10.0"
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
|
eml-parser = "0.1.0"
|
||||||
encoding_rs = "0.8.30"
|
encoding_rs = "0.8.30"
|
||||||
fancy-regex = "0.11.0"
|
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
filetime = "0.2.15"
|
filetime = "0.2.15"
|
||||||
fs_extra = "1.3.0"
|
fs_extra = "1.2.0"
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
indexmap = { version = "1.7", features = ["serde-1"] }
|
ical = "0.7.0"
|
||||||
indicatif = "0.17.2"
|
indexmap = { version="1.7", features=["serde-1"] }
|
||||||
|
Inflector = "0.11"
|
||||||
is-root = "0.1.2"
|
is-root = "0.1.2"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
lscolors = { version = "0.10.0", features = ["crossterm"]}
|
||||||
md5 = { package = "md-5", version = "0.10.0" }
|
md5 = { package = "md-5", version = "0.10.0" }
|
||||||
|
meval = "0.2.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
mime_guess = "2.0.4"
|
|
||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
num = { version = "0.4.0", optional = true }
|
num = { version = "0.4.0", optional = true }
|
||||||
num-traits = "0.2.14"
|
|
||||||
once_cell = "1.17"
|
|
||||||
open = "3.2.0"
|
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
powierza-coefficient = "1.0.2"
|
powierza-coefficient = "1.0.1"
|
||||||
quick-xml = "0.27"
|
quick-xml = "0.23.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rayon = "1.6.1"
|
rayon = "1.5.1"
|
||||||
regex = "1.7.1"
|
regex = "1.5.4"
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
||||||
roxmltree = "0.18.0"
|
roxmltree = "0.14.0"
|
||||||
rust-embed = "6.3.0"
|
rust-embed = "6.3.0"
|
||||||
same-file = "1.0.6"
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde_ini = "0.2.0"
|
||||||
serde_urlencoded = "0.7.0"
|
serde_urlencoded = "0.7.0"
|
||||||
serde_yaml = "0.9.4"
|
serde_yaml = "0.8.16"
|
||||||
sha2 = "0.10.0"
|
sha2 = "0.10.0"
|
||||||
# Disable default features b/c the default features build Git (very slow to compile)
|
# Disable default features b/c the default features build Git (very slow to compile)
|
||||||
percent-encoding = "2.2.0"
|
shadow-rs = { version = "0.16.1", default-features = false }
|
||||||
reedline = { version = "0.16.0", features = ["bashisms", "sqlite"] }
|
strip-ansi-escapes = "0.1.1"
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
sysinfo = "0.24.6"
|
||||||
shadow-rs = { version = "0.20.0", default-features = false }
|
|
||||||
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
|
||||||
sysinfo = "0.28.0"
|
|
||||||
tabled = "0.10.0"
|
|
||||||
terminal_size = "0.2.1"
|
terminal_size = "0.2.1"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
titlecase = "2.0.0"
|
titlecase = "2.0.0"
|
||||||
toml = "0.7.1"
|
toml = "0.5.8"
|
||||||
unicode-segmentation = "1.10.0"
|
unicode-segmentation = "1.8.0"
|
||||||
unicode-width = "0.1.10"
|
|
||||||
url = "2.2.1"
|
url = "2.2.1"
|
||||||
uuid = { version = "1.2.2", features = ["v4"] }
|
uuid = { version = "1.1.2", features = ["v4"] }
|
||||||
wax = { version = "0.5.0" }
|
which = { version = "4.2.2", optional = true }
|
||||||
which = { version = "4.4.0", optional = true }
|
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
|
||||||
print-positions = "0.6.1"
|
wax = { version = "0.5.0", features = ["diagnostics"] }
|
||||||
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
[target.'cfg(windows)'.dependencies]
|
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
|
||||||
winreg = "0.11.0"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
|
||||||
umask = "2.0.0"
|
umask = "2.0.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
|
version = "2.1.3"
|
||||||
optional = true
|
optional = true
|
||||||
version = "3.0.1"
|
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
features = [
|
version = "0.22.8"
|
||||||
"arg_where",
|
# path = "../../../../polars/polars"
|
||||||
"checked_arithmetic",
|
|
||||||
"concat_str",
|
|
||||||
"cross_join",
|
|
||||||
"csv-file",
|
|
||||||
"cum_agg",
|
|
||||||
"default",
|
|
||||||
"dtype-categorical",
|
|
||||||
"dtype-datetime",
|
|
||||||
"dtype-struct",
|
|
||||||
"dynamic_groupby",
|
|
||||||
"ipc",
|
|
||||||
"is_in",
|
|
||||||
"json",
|
|
||||||
"lazy",
|
|
||||||
"object",
|
|
||||||
"parquet",
|
|
||||||
"random",
|
|
||||||
"rolling_window",
|
|
||||||
"rows",
|
|
||||||
"serde",
|
|
||||||
"serde-lazy",
|
|
||||||
"strings",
|
|
||||||
"strings",
|
|
||||||
"to_dummies",
|
|
||||||
]
|
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.26.1"
|
features = [
|
||||||
|
"default", "to_dummies", "parquet", "json", "serde", "serde-lazy",
|
||||||
|
"object", "checked_arithmetic", "strings", "cum_agg", "is_in",
|
||||||
|
"rolling_window", "strings", "rows", "random",
|
||||||
|
"dtype-datetime", "dtype-struct", "lazy", "cross_join",
|
||||||
|
"dynamic_groupby", "dtype-categorical", "concat_str"
|
||||||
|
]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
version = "0.37.0"
|
||||||
version = "0.44.0"
|
features = [
|
||||||
|
"alloc",
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
"Win32_System_SystemServices",
|
||||||
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dataframe = ["num", "polars", "sqlparser"]
|
|
||||||
plugin = ["nu-parser/plugin"]
|
|
||||||
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
|
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
which-support = ["which"]
|
which-support = ["which"]
|
||||||
|
plugin = ["nu-parser/plugin"]
|
||||||
|
dataframe = ["polars", "num"]
|
||||||
|
database = ["sqlparser", "rusqlite"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.20.0", default-features = false }
|
shadow-rs = { version = "0.16.1", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.76.0" }
|
|
||||||
|
|
||||||
dirs-next = "2.0.0"
|
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
proptest = "1.1.0"
|
dirs-next = "2.0.0"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
rstest = { version = "0.16.0", default-features = false }
|
rstest = "0.15.0"
|
||||||
|
|||||||
@@ -3,18 +3,16 @@ use std::process::Command;
|
|||||||
fn main() -> shadow_rs::SdResult<()> {
|
fn main() -> shadow_rs::SdResult<()> {
|
||||||
// Look up the current Git commit ourselves instead of relying on shadow_rs,
|
// Look up the current Git commit ourselves instead of relying on shadow_rs,
|
||||||
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
||||||
let hash = get_git_hash().unwrap_or_default();
|
let hash = get_git_hash().expect("failed to get latest git commit hash");
|
||||||
println!("cargo:rustc-env=NU_COMMIT_HASH={hash}");
|
println!("cargo:rustc-env=NU_COMMIT_HASH={}", hash);
|
||||||
|
|
||||||
shadow_rs::new()
|
shadow_rs::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_git_hash() -> Option<String> {
|
fn get_git_hash() -> Result<String, std::io::Error> {
|
||||||
Command::new("git")
|
let out = Command::new("git").args(["rev-parse", "HEAD"]).output()?;
|
||||||
.args(["rev-parse", "HEAD"])
|
Ok(String::from_utf8(out.stdout)
|
||||||
.output()
|
.expect("could not convert stdout to string")
|
||||||
.ok()
|
.trim()
|
||||||
.filter(|output| output.status.success())
|
.to_string())
|
||||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
|
||||||
.map(|hash| hash.trim().to_string())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
# Seeds for failure cases proptest has generated in the past. It is
|
|
||||||
# automatically read and these particular cases re-run before any
|
|
||||||
# novel cases are generated.
|
|
||||||
#
|
|
||||||
# It is recommended to check this file in to source control so that
|
|
||||||
# everyone who runs the test benefits from these saved cases.
|
|
||||||
cc 96a80ecd19729fb43a7b7bb2766b37d6083ba73b16abb97075875e3cfcdc763f # shrinks to c = '"'
|
|
||||||
cc 4146602559ea717a02bcef3c6d73cdf613c30d0c3f92c48e26c79b9a1544e027 # shrinks to c = '\\'
|
|
||||||
cc 80532a0ee73df456a719b9e3cce1ae5f3d26009dde819cbaf16f8e0cb6709705 # shrinks to c = ':'
|
|
||||||
cc cdb88505686eea3c74c36f282fd29b2b68bc118ded4ebfc36f9838d174bd7653 # shrinks to c = '`'
|
|
||||||
cc 0f534d55f9771e8810b9c4252a4168abfaec1a35e1b0cac12dbaf726d295a08c # shrinks to c = '\0'
|
|
||||||
cc 5d31bcbab722acd1f4e23ca3a4f95ff309a636b45a73ca8ae9f820d93ff57acc # shrinks to c = '{'
|
|
||||||
cc 5afec063bc96160d681d77f90041b67ef5cfdea4dcbd12d984fd828fbeb4b421 # shrinks to c = '#'
|
|
||||||
cc f919beb3ee5c70e756a15635d65ded7d44f3ae58b5e86b6c09e814d5d8cdd506 # shrinks to c = ';'
|
|
||||||
cc ec00f39b8d45dfd8808947a56af5e50ba5a0ef7c951723b45377815a02e515b1 # shrinks to c = '('
|
|
||||||
cc 25b773cdf4c24179151fa86244c7de4136e05df9e94e6ee77a336ebfd8764444 # shrinks to c = '|'
|
|
||||||
cc 94dc0d54b97d59e1c0f4cb11bdccb3823a1bb908cbc3fd643ee8f067169fad72 # shrinks to c = '0'
|
|
||||||
cc c9d0051fb1e5a8bdc1d4f5a3dceac1b4b465827d1dff4fc3a3755baae6a7bb48 # shrinks to c = '$'
|
|
||||||
cc 14ec40d2eb5bd2663e9b11bb49fb2120852f9ea71678c69d732161412b55a3ec # shrinks to s = ""
|
|
||||||
cc d4afccc51ed9d421bdb7e1723e273dfb6e77c3a449489671a496db234e87c5ed # shrinks to c = '\r'
|
|
||||||
cc 515a56d73eb1b69290ef4c714b629421989879aebd57991bd2c2bf11294353b1 # shrinks to s = "\\\\𐊀{"
|
|
||||||
cc 111566990fffa432acd2dbc845141b0e7870f97125c7621e3ddf142204568246 # shrinks to s = "'\":`"
|
|
||||||
cc 0424c33000d9188be96b3049046eb052770b2158bf5ebb0c98656d7145e8aca9 # shrinks to s = "0"
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits and"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits and")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required(
|
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit and",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Performs bitwise and for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["logic and"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let target: i64 = call.req(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, target, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Apply bits and to two numbers",
|
|
||||||
example: "2 | bits and 2",
|
|
||||||
result: Some(Value::test_int(2)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Apply logical and to a list of numbers",
|
|
||||||
example: "[4 3 2] | bits and 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(2)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, target: i64, head: Span) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => Value::Int {
|
|
||||||
val: val & target,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
use nu_engine::get_full_help;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::Call,
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Bits;
|
|
||||||
|
|
||||||
impl Command for Bits {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits")
|
|
||||||
.category(Category::Bits)
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Various commands for working with bits"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(Value::String {
|
|
||||||
val: get_full_help(
|
|
||||||
&Bits.signature(),
|
|
||||||
&Bits.examples(),
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
self.is_parser_keyword(),
|
|
||||||
),
|
|
||||||
span: call.head,
|
|
||||||
}
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
mod and;
|
|
||||||
mod bits_;
|
|
||||||
mod not;
|
|
||||||
mod or;
|
|
||||||
mod rotate_left;
|
|
||||||
mod rotate_right;
|
|
||||||
mod shift_left;
|
|
||||||
mod shift_right;
|
|
||||||
mod xor;
|
|
||||||
|
|
||||||
use nu_protocol::Spanned;
|
|
||||||
|
|
||||||
pub use and::SubCommand as BitsAnd;
|
|
||||||
pub use bits_::Bits;
|
|
||||||
pub use not::SubCommand as BitsNot;
|
|
||||||
pub use or::SubCommand as BitsOr;
|
|
||||||
pub use rotate_left::SubCommand as BitsRotateLeft;
|
|
||||||
pub use rotate_right::SubCommand as BitsRotateRight;
|
|
||||||
pub use shift_left::SubCommand as BitsShiftLeft;
|
|
||||||
pub use shift_right::SubCommand as BitsShiftRight;
|
|
||||||
pub use xor::SubCommand as BitsXor;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum NumberBytes {
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Four,
|
|
||||||
Eight,
|
|
||||||
Auto,
|
|
||||||
Invalid,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum InputNumType {
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Four,
|
|
||||||
Eight,
|
|
||||||
SignedOne,
|
|
||||||
SignedTwo,
|
|
||||||
SignedFour,
|
|
||||||
SignedEight,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_number_bytes(number_bytes: &Option<Spanned<String>>) -> NumberBytes {
|
|
||||||
match number_bytes.as_ref() {
|
|
||||||
None => NumberBytes::Eight,
|
|
||||||
Some(size) => match size.item.as_str() {
|
|
||||||
"1" => NumberBytes::One,
|
|
||||||
"2" => NumberBytes::Two,
|
|
||||||
"4" => NumberBytes::Four,
|
|
||||||
"8" => NumberBytes::Eight,
|
|
||||||
"auto" => NumberBytes::Auto,
|
|
||||||
_ => NumberBytes::Invalid,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_input_num_type(val: i64, signed: bool, number_size: NumberBytes) -> InputNumType {
|
|
||||||
if signed || val < 0 {
|
|
||||||
match number_size {
|
|
||||||
NumberBytes::One => InputNumType::SignedOne,
|
|
||||||
NumberBytes::Two => InputNumType::SignedTwo,
|
|
||||||
NumberBytes::Four => InputNumType::SignedFour,
|
|
||||||
NumberBytes::Eight => InputNumType::SignedEight,
|
|
||||||
NumberBytes::Auto => {
|
|
||||||
if val <= 0x7F && val >= -(2i64.pow(7)) {
|
|
||||||
InputNumType::SignedOne
|
|
||||||
} else if val <= 0x7FFF && val >= -(2i64.pow(15)) {
|
|
||||||
InputNumType::SignedTwo
|
|
||||||
} else if val <= 0x7FFFFFFF && val >= -(2i64.pow(31)) {
|
|
||||||
InputNumType::SignedFour
|
|
||||||
} else {
|
|
||||||
InputNumType::SignedEight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NumberBytes::Invalid => InputNumType::SignedFour,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match number_size {
|
|
||||||
NumberBytes::One => InputNumType::One,
|
|
||||||
NumberBytes::Two => InputNumType::Two,
|
|
||||||
NumberBytes::Four => InputNumType::Four,
|
|
||||||
NumberBytes::Eight => InputNumType::Eight,
|
|
||||||
NumberBytes::Auto => {
|
|
||||||
if val <= 0xFF {
|
|
||||||
InputNumType::One
|
|
||||||
} else if val <= 0xFFFF {
|
|
||||||
InputNumType::Two
|
|
||||||
} else if val <= 0xFFFFFFFF {
|
|
||||||
InputNumType::Four
|
|
||||||
} else {
|
|
||||||
InputNumType::Eight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NumberBytes::Invalid => InputNumType::Four,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
use super::{get_number_bytes, NumberBytes};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits not"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits not")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.switch(
|
|
||||||
"signed",
|
|
||||||
"always treat input number as a signed number",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"number-bytes",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the size of unsigned number in bytes, it can be 1, 2, 4, 8, auto",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Performs logical negation on each bit"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["negation"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let signed = call.has_flag("signed");
|
|
||||||
let number_bytes: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
||||||
let bytes_len = get_number_bytes(&number_bytes);
|
|
||||||
if let NumberBytes::Invalid = bytes_len {
|
|
||||||
if let Some(val) = number_bytes {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
val.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, head, signed, bytes_len),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Apply logical negation to a list of numbers",
|
|
||||||
example: "[4 3 2] | bits not",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(140737488355323),
|
|
||||||
Value::test_int(140737488355324),
|
|
||||||
Value::test_int(140737488355325),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
|
||||||
example: "[4 3 2] | bits not -n 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(65531),
|
|
||||||
Value::test_int(65532),
|
|
||||||
Value::test_int(65533),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Apply logical negation to a list of numbers, treat input as signed number",
|
|
||||||
example: "[4 3 2] | bits not -s",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(-5),
|
|
||||||
Value::test_int(-4),
|
|
||||||
Value::test_int(-3),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => {
|
|
||||||
if signed || val < 0 {
|
|
||||||
Value::Int { val: !val, span }
|
|
||||||
} else {
|
|
||||||
use NumberBytes::*;
|
|
||||||
let out_val = match number_size {
|
|
||||||
One => !val & 0x00_00_00_00_00_FF,
|
|
||||||
Two => !val & 0x00_00_00_00_FF_FF,
|
|
||||||
Four => !val & 0x00_00_FF_FF_FF_FF,
|
|
||||||
Eight => !val & 0x7F_FF_FF_FF_FF_FF,
|
|
||||||
Auto => {
|
|
||||||
if val <= 0xFF {
|
|
||||||
!val & 0x00_00_00_00_00_FF
|
|
||||||
} else if val <= 0xFF_FF {
|
|
||||||
!val & 0x00_00_00_00_FF_FF
|
|
||||||
} else if val <= 0xFF_FF_FF_FF {
|
|
||||||
!val & 0x00_00_FF_FF_FF_FF
|
|
||||||
} else {
|
|
||||||
!val & 0x7F_FF_FF_FF_FF_FF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This case shouldn't happen here, as it's handled before
|
|
||||||
Invalid => 0,
|
|
||||||
};
|
|
||||||
Value::Int { val: out_val, span }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => match other {
|
|
||||||
// Propagate errors inside the value
|
|
||||||
Value::Error { .. } => other,
|
|
||||||
_ => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"numeric".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits or"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits or")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required(
|
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit or",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Performs bitwise or for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["logic or"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let target: i64 = call.req(engine_state, stack, 0)?;
|
|
||||||
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, target, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Apply bits or to two numbers",
|
|
||||||
example: "2 | bits or 6",
|
|
||||||
result: Some(Value::test_int(6)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Apply logical or to a list of numbers",
|
|
||||||
example: "[8 3 2] | bits or 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(10), Value::test_int(3), Value::test_int(2)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, target: i64, head: Span) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => Value::Int {
|
|
||||||
val: val | target,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use num_traits::int::PrimInt;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits rol"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits rol")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
|
|
||||||
.switch(
|
|
||||||
"signed",
|
|
||||||
"always treat input number as a signed number",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"number-bytes",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Bitwise rotate left for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["rotate left"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let bits: usize = call.req(engine_state, stack, 0)?;
|
|
||||||
let signed = call.has_flag("signed");
|
|
||||||
let number_bytes: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
||||||
let bytes_len = get_number_bytes(&number_bytes);
|
|
||||||
if let NumberBytes::Invalid = bytes_len {
|
|
||||||
if let Some(val) = number_bytes {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
val.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, bits, head, signed, bytes_len),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Rotate left a number with 2 bits",
|
|
||||||
example: "17 | bits rol 2",
|
|
||||||
result: Some(Value::test_int(68)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Rotate left a list of numbers with 2 bits",
|
|
||||||
example: "[5 3 2] | bits rol 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(20), Value::test_int(12), Value::test_int(8)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rotate_left<T: Display + PrimInt>(val: T, bits: u32, span: Span) -> Value
|
|
||||||
where
|
|
||||||
i64: std::convert::TryFrom<T>,
|
|
||||||
{
|
|
||||||
let rotate_result = i64::try_from(val.rotate_left(bits));
|
|
||||||
match rotate_result {
|
|
||||||
Ok(val) => Value::Int { val, span },
|
|
||||||
Err(_) => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Rotate left result beyond the range of 64 bit signed number".to_string(),
|
|
||||||
format!(
|
|
||||||
"{val} of the specified number of bytes rotate left {bits} bits exceed limit"
|
|
||||||
),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => {
|
|
||||||
use InputNumType::*;
|
|
||||||
// let bits = (((bits % 64) + 64) % 64) as u32;
|
|
||||||
let bits = bits as u32;
|
|
||||||
let input_type = get_input_num_type(val, signed, number_size);
|
|
||||||
match input_type {
|
|
||||||
One => get_rotate_left(val as u8, bits, span),
|
|
||||||
Two => get_rotate_left(val as u16, bits, span),
|
|
||||||
Four => get_rotate_left(val as u32, bits, span),
|
|
||||||
Eight => get_rotate_left(val as u64, bits, span),
|
|
||||||
SignedOne => get_rotate_left(val as i8, bits, span),
|
|
||||||
SignedTwo => get_rotate_left(val as i16, bits, span),
|
|
||||||
SignedFour => get_rotate_left(val as i32, bits, span),
|
|
||||||
SignedEight => get_rotate_left(val, bits, span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use num_traits::int::PrimInt;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits ror"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits ror")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
|
|
||||||
.switch(
|
|
||||||
"signed",
|
|
||||||
"always treat input number as a signed number",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"number-bytes",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Bitwise rotate right for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["rotate right"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let bits: usize = call.req(engine_state, stack, 0)?;
|
|
||||||
let signed = call.has_flag("signed");
|
|
||||||
let number_bytes: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
||||||
let bytes_len = get_number_bytes(&number_bytes);
|
|
||||||
if let NumberBytes::Invalid = bytes_len {
|
|
||||||
if let Some(val) = number_bytes {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
val.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, bits, head, signed, bytes_len),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Rotate right a number with 60 bits",
|
|
||||||
example: "17 | bits ror 60",
|
|
||||||
result: Some(Value::test_int(272)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Rotate right a list of numbers of one byte",
|
|
||||||
example: "[15 33 92] | bits ror 2 -n 1",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(195),
|
|
||||||
Value::test_int(72),
|
|
||||||
Value::test_int(23),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rotate_right<T: Display + PrimInt>(val: T, bits: u32, span: Span) -> Value
|
|
||||||
where
|
|
||||||
i64: std::convert::TryFrom<T>,
|
|
||||||
{
|
|
||||||
let rotate_result = i64::try_from(val.rotate_right(bits));
|
|
||||||
match rotate_result {
|
|
||||||
Ok(val) => Value::Int { val, span },
|
|
||||||
Err(_) => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Rotate right result beyond the range of 64 bit signed number".to_string(),
|
|
||||||
format!(
|
|
||||||
"{val} of the specified number of bytes rotate right {bits} bits exceed limit"
|
|
||||||
),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => {
|
|
||||||
use InputNumType::*;
|
|
||||||
// let bits = (((bits % 64) + 64) % 64) as u32;
|
|
||||||
let bits = bits as u32;
|
|
||||||
let input_type = get_input_num_type(val, signed, number_size);
|
|
||||||
match input_type {
|
|
||||||
One => get_rotate_right(val as u8, bits, span),
|
|
||||||
Two => get_rotate_right(val as u16, bits, span),
|
|
||||||
Four => get_rotate_right(val as u32, bits, span),
|
|
||||||
Eight => get_rotate_right(val as u64, bits, span),
|
|
||||||
SignedOne => get_rotate_right(val as i8, bits, span),
|
|
||||||
SignedTwo => get_rotate_right(val as i16, bits, span),
|
|
||||||
SignedFour => get_rotate_right(val as i32, bits, span),
|
|
||||||
SignedEight => get_rotate_right(val, bits, span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use num_traits::CheckedShl;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits shl"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits shl")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to shift left")
|
|
||||||
.switch(
|
|
||||||
"signed",
|
|
||||||
"always treat input number as a signed number",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"number-bytes",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Bitwise shift left for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["shift left"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let bits: usize = call.req(engine_state, stack, 0)?;
|
|
||||||
let signed = call.has_flag("signed");
|
|
||||||
let number_bytes: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
||||||
let bytes_len = get_number_bytes(&number_bytes);
|
|
||||||
if let NumberBytes::Invalid = bytes_len {
|
|
||||||
if let Some(val) = number_bytes {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
val.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, bits, head, signed, bytes_len),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Shift left a number by 7 bits",
|
|
||||||
example: "2 | bits shl 7",
|
|
||||||
result: Some(Value::test_int(256)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Shift left a number with 1 byte by 7 bits",
|
|
||||||
example: "2 | bits shl 7 -n 1",
|
|
||||||
result: Some(Value::test_int(0)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Shift left a signed number by 1 bit",
|
|
||||||
example: "0x7F | bits shl 1 -s",
|
|
||||||
result: Some(Value::test_int(254)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Shift left a list of numbers",
|
|
||||||
example: "[5 3 2] | bits shl 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(20), Value::test_int(12), Value::test_int(8)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_shift_left<T: CheckedShl + Display + Copy>(val: T, bits: u32, span: Span) -> Value
|
|
||||||
where
|
|
||||||
i64: std::convert::TryFrom<T>,
|
|
||||||
{
|
|
||||||
match val.checked_shl(bits) {
|
|
||||||
Some(val) => {
|
|
||||||
let shift_result = i64::try_from(val);
|
|
||||||
match shift_result {
|
|
||||||
Ok(val) => Value::Int { val, span },
|
|
||||||
Err(_) => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Shift left result beyond the range of 64 bit signed number".to_string(),
|
|
||||||
format!(
|
|
||||||
"{val} of the specified number of bytes shift left {bits} bits exceed limit"
|
|
||||||
),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Shift left failed".to_string(),
|
|
||||||
format!("{val} shift left {bits} bits failed, you may shift too many bits"),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => {
|
|
||||||
use InputNumType::*;
|
|
||||||
// let bits = (((bits % 64) + 64) % 64) as u32;
|
|
||||||
let bits = bits as u32;
|
|
||||||
let input_type = get_input_num_type(val, signed, number_size);
|
|
||||||
match input_type {
|
|
||||||
One => get_shift_left(val as u8, bits, span),
|
|
||||||
Two => get_shift_left(val as u16, bits, span),
|
|
||||||
Four => get_shift_left(val as u32, bits, span),
|
|
||||||
Eight => get_shift_left(val as u64, bits, span),
|
|
||||||
SignedOne => get_shift_left(val as i8, bits, span),
|
|
||||||
SignedTwo => get_shift_left(val as i16, bits, span),
|
|
||||||
SignedFour => get_shift_left(val as i32, bits, span),
|
|
||||||
SignedEight => get_shift_left(val, bits, span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use num_traits::CheckedShr;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits shr"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits shr")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("bits", SyntaxShape::Int, "number of bits to shift right")
|
|
||||||
.switch(
|
|
||||||
"signed",
|
|
||||||
"always treat input number as a signed number",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"number-bytes",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Bitwise shift right for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["shift right"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let bits: usize = call.req(engine_state, stack, 0)?;
|
|
||||||
let signed = call.has_flag("signed");
|
|
||||||
let number_bytes: Option<Spanned<String>> =
|
|
||||||
call.get_flag(engine_state, stack, "number-bytes")?;
|
|
||||||
let bytes_len = get_number_bytes(&number_bytes);
|
|
||||||
if let NumberBytes::Invalid = bytes_len {
|
|
||||||
if let Some(val) = number_bytes {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
|
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
val.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, bits, head, signed, bytes_len),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Shift right a number with 2 bits",
|
|
||||||
example: "8 | bits shr 2",
|
|
||||||
result: Some(Value::test_int(2)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Shift right a list of numbers",
|
|
||||||
example: "[15 35 2] | bits shr 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(3), Value::test_int(8), Value::test_int(0)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_shift_right<T: CheckedShr + Display + Copy>(val: T, bits: u32, span: Span) -> Value
|
|
||||||
where
|
|
||||||
i64: std::convert::TryFrom<T>,
|
|
||||||
{
|
|
||||||
match val.checked_shr(bits) {
|
|
||||||
Some(val) => {
|
|
||||||
let shift_result = i64::try_from(val);
|
|
||||||
match shift_result {
|
|
||||||
Ok(val) => Value::Int { val, span },
|
|
||||||
Err(_) => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Shift right result beyond the range of 64 bit signed number".to_string(),
|
|
||||||
format!(
|
|
||||||
"{val} of the specified number of bytes shift right {bits} bits exceed limit"
|
|
||||||
),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Value::Error {
|
|
||||||
error: ShellError::GenericError(
|
|
||||||
"Shift right failed".to_string(),
|
|
||||||
format!("{val} shift right {bits} bits failed, you may shift too many bits"),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => {
|
|
||||||
use InputNumType::*;
|
|
||||||
// let bits = (((bits % 64) + 64) % 64) as u32;
|
|
||||||
let bits = bits as u32;
|
|
||||||
let input_type = get_input_num_type(val, signed, number_size);
|
|
||||||
match input_type {
|
|
||||||
One => get_shift_right(val as u8, bits, span),
|
|
||||||
Two => get_shift_right(val as u16, bits, span),
|
|
||||||
Four => get_shift_right(val as u32, bits, span),
|
|
||||||
Eight => get_shift_right(val as u64, bits, span),
|
|
||||||
SignedOne => get_shift_right(val as i8, bits, span),
|
|
||||||
SignedTwo => get_shift_right(val as i16, bits, span),
|
|
||||||
SignedFour => get_shift_right(val as i32, bits, span),
|
|
||||||
SignedEight => get_shift_right(val, bits, span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::ast::Call;
|
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
|
||||||
use nu_protocol::{
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
impl Command for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"bits xor"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("bits xor")
|
|
||||||
.input_output_types(vec![(Type::Int, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required(
|
|
||||||
"target",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"target integer to perform bit xor",
|
|
||||||
)
|
|
||||||
.category(Category::Bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Performs bitwise xor for integers"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["logic xor"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
let head = call.head;
|
|
||||||
let target: i64 = call.req(engine_state, stack, 0)?;
|
|
||||||
// This doesn't match explicit nulls
|
|
||||||
if matches!(input, PipelineData::Empty) {
|
|
||||||
return Err(ShellError::PipelineEmpty(head));
|
|
||||||
}
|
|
||||||
input.map(
|
|
||||||
move |value| operate(value, target, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Apply bits xor to two numbers",
|
|
||||||
example: "2 | bits xor 2",
|
|
||||||
result: Some(Value::test_int(0)),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Apply logical xor to a list of numbers",
|
|
||||||
example: "[8 3 2] | bits xor 2",
|
|
||||||
result: Some(Value::List {
|
|
||||||
vals: vec![Value::test_int(10), Value::test_int(1), Value::test_int(0)],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(value: Value, target: i64, head: Span) -> Value {
|
|
||||||
match value {
|
|
||||||
Value::Int { val, span } => Value::Int {
|
|
||||||
val: val ^ target,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => value,
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
head,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(SubCommand {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
added_data: Vec<u8>,
|
added_data: Vec<u8>,
|
||||||
index: Option<usize>,
|
index: Option<usize>,
|
||||||
end: bool,
|
end: bool,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +30,6 @@ impl Command for BytesAdd {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes add")
|
Signature::build("bytes add")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("data", SyntaxShape::Binary, "the binary to add")
|
.required("data", SyntaxShape::Binary, "the binary to add")
|
||||||
.named(
|
.named(
|
||||||
"index",
|
"index",
|
||||||
@@ -43,13 +41,13 @@ impl Command for BytesAdd {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, add bytes to the data at the given cell paths",
|
"optionally matches prefix of text by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Add specified bytes to the input"
|
"add specified bytes to the input"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@@ -64,8 +62,12 @@ impl Command for BytesAdd {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
||||||
let end = call.has_flag("end");
|
let end = call.has_flag("end");
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ impl Command for BytesAdd {
|
|||||||
added_data,
|
added_data,
|
||||||
index,
|
index,
|
||||||
end,
|
end,
|
||||||
cell_paths,
|
column_paths,
|
||||||
};
|
};
|
||||||
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@@ -116,27 +118,7 @@ impl Command for BytesAdd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn add(input: &[u8], args: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => add_impl(val, args, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
|
|
||||||
match args.index {
|
match args.index {
|
||||||
None => {
|
None => {
|
||||||
if args.end {
|
if args.end {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@@ -15,12 +15,12 @@ struct Arguments {
|
|||||||
start: isize,
|
start: isize,
|
||||||
end: isize,
|
end: isize,
|
||||||
arg_span: Span,
|
arg_span: Span,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,7 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
Value::List { mut vals, span } => {
|
Value::List { mut vals, span } => {
|
||||||
if vals.len() != 2 {
|
if vals.len() != 2 {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"More than two indices in range".to_string(),
|
"More than two indices given".to_string(),
|
||||||
"value originates from here".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
@@ -40,14 +38,10 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
let end = match end {
|
let end = match end {
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"Only string or list<int> ranges are supported".into(),
|
"could not perform subbytes. Expecting a string or int".to_string(),
|
||||||
format!("input type: {:?}", other.get_type()),
|
other.span().unwrap_or(head),
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -55,14 +49,10 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
let start = match start {
|
let start = match start {
|
||||||
Value::Int { val, .. } => val.to_string(),
|
Value::Int { val, .. } => val.to_string(),
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"Only string or list<int> ranges are supported".into(),
|
"could not perform subbytes. Expecting a string or int".to_string(),
|
||||||
format!("input type: {:?}", other.get_type()),
|
other.span().unwrap_or(head),
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -70,27 +60,21 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::String { val, span } => {
|
Value::String { val, span } => {
|
||||||
let split_result = val.split_once(',');
|
let splitted_result = val.split_once(',');
|
||||||
match split_result {
|
match splitted_result {
|
||||||
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"could not perform subbytes".to_string(),
|
"could not perform subbytes".to_string(),
|
||||||
"with this range".to_string(),
|
|
||||||
head,
|
|
||||||
span,
|
span,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"could not perform subbytes".to_string(),
|
"could not perform subbytes".to_string(),
|
||||||
"with this range".to_string(),
|
other.span().unwrap_or(head),
|
||||||
head,
|
|
||||||
other.expect_span(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -98,26 +82,28 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
let start: isize = if start.is_empty() || start == "_" {
|
let start: isize = if start.is_empty() || start == "_" {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
start.trim().parse().map_err(|_| {
|
match start.trim().parse() {
|
||||||
ShellError::UnsupportedInput(
|
Ok(s) => s,
|
||||||
"could not perform subbytes".to_string(),
|
Err(_) => {
|
||||||
"with this range".to_string(),
|
return Err(ShellError::UnsupportedInput(
|
||||||
head,
|
"could not perform subbytes".to_string(),
|
||||||
span,
|
span,
|
||||||
)
|
))
|
||||||
})?
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let end: isize = if end.is_empty() || end == "_" {
|
let end: isize = if end.is_empty() || end == "_" {
|
||||||
isize::max_value()
|
isize::max_value()
|
||||||
} else {
|
} else {
|
||||||
end.trim().parse().map_err(|_| {
|
match end.trim().parse() {
|
||||||
ShellError::UnsupportedInput(
|
Ok(s) => s,
|
||||||
"could not perform subbytes".to_string(),
|
Err(_) => {
|
||||||
"with this range".to_string(),
|
return Err(ShellError::UnsupportedInput(
|
||||||
head,
|
"could not perform subbytes".to_string(),
|
||||||
span,
|
span,
|
||||||
)
|
))
|
||||||
})?
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok((start, end, span))
|
Ok((start, end, span))
|
||||||
}
|
}
|
||||||
@@ -129,13 +115,11 @@ impl Command for BytesAt {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes at")
|
Signature::build("bytes at")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.required("range", SyntaxShape::Any, "the indexes to get bytes")
|
.required("range", SyntaxShape::Any, "the indexes to get bytes")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, get bytes from data at the given cell paths",
|
"optionally get bytes by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@@ -157,13 +141,17 @@ impl Command for BytesAt {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let range: Value = call.req(engine_state, stack, 0)?;
|
let range: Value = call.req(engine_state, stack, 0)?;
|
||||||
let (start, end, arg_span) = parse_range(range, call.head)?;
|
let (start, end, arg_span) = parse_range(range, call.head)?;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
arg_span,
|
arg_span,
|
||||||
cell_paths,
|
column_paths,
|
||||||
};
|
};
|
||||||
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@@ -240,27 +228,7 @@ impl Command for BytesAt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn at(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => at_impl(val, args, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|
||||||
let len: isize = input.len() as isize;
|
let len: isize = input.len() as isize;
|
||||||
|
|
||||||
let start: isize = if arg.start < 0 {
|
let start: isize = if arg.start < 0 {
|
||||||
@@ -278,7 +246,7 @@ fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
match start.cmp(&end) {
|
match start.cmp(&end) {
|
||||||
Ordering::Equal => Value::Binary { val: vec![], span },
|
Ordering::Equal => Value::Binary { val: vec![], span },
|
||||||
Ordering::Greater => Value::Error {
|
Ordering::Greater => Value::Error {
|
||||||
error: ShellError::TypeMismatch(
|
error: ShellError::UnsupportedInput(
|
||||||
"End must be greater than or equal to Start".to_string(),
|
"End must be greater than or equal to Start".to_string(),
|
||||||
arg.arg_span,
|
arg.arg_span,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
|
|||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Type, Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -24,7 +24,6 @@ impl Command for BytesBuild {
|
|||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("bytes build")
|
Signature::build("bytes build")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Binary)])
|
|
||||||
.rest("rest", SyntaxShape::Any, "list of bytes")
|
.rest("rest", SyntaxShape::Any, "list of bytes")
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@@ -46,18 +45,16 @@ impl Command for BytesBuild {
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for expr in call.positional_iter() {
|
for expr in call.positional_iter() {
|
||||||
let val = eval_expression(engine_state, stack, expr)?;
|
let val = eval_expression(engine_state, stack, expr)?;
|
||||||
match val {
|
match val {
|
||||||
Value::Binary { mut val, .. } => output.append(&mut val),
|
Value::Binary { mut val, .. } => output.append(&mut val),
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::TypeMismatch(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"only binary data arguments are supported".to_string(),
|
"only support expression which yields to binary data".to_string(),
|
||||||
other.expect_span(),
|
other.span().unwrap_or(call.head),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use nu_engine::get_full_help;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
|
Category, IntoPipelineData, PipelineData, Signature, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -14,36 +14,36 @@ impl Command for Bytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes")
|
Signature::build("bytes").category(Category::Bytes)
|
||||||
.category(Category::Bytes)
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Various commands for working with byte data"
|
"Various commands for working with byte data"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(
|
val: get_full_help(&Bytes.signature(), &Bytes.examples(), engine_state, stack),
|
||||||
&Bytes.signature(),
|
|
||||||
&Bytes.examples(),
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
self.is_parser_keyword(),
|
|
||||||
),
|
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::Bytes;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Bytes {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
|
|||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Type, Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -16,7 +16,6 @@ impl Command for BytesCollect {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes collect")
|
Signature::build("bytes collect")
|
||||||
.input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)])
|
|
||||||
.optional(
|
.optional(
|
||||||
"separator",
|
"separator",
|
||||||
SyntaxShape::Binary,
|
SyntaxShape::Binary,
|
||||||
@@ -54,16 +53,14 @@ impl Command for BytesCollect {
|
|||||||
output_binary.append(&mut work_sep)
|
output_binary.append(&mut work_sep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Explicitly propagate errors instead of dropping them.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
other => {
|
other => {
|
||||||
return Err(ShellError::OnlySupportsThisInputType(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"integer".into(),
|
format!(
|
||||||
other.get_type().to_string(),
|
"The element type is {}, this command only works with bytes.",
|
||||||
call.head,
|
other.get_type()
|
||||||
// This line requires the Value::Error match above.
|
),
|
||||||
other.expect_span(),
|
other.span().unwrap_or(call.head),
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +28,11 @@ impl Command for BytesEndsWith {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes ends-with")
|
Signature::build("bytes ends-with")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Bool)])
|
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, check if bytes at the given cell paths end with the pattern",
|
"optionally matches prefix of text by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@@ -54,11 +53,15 @@ impl Command for BytesEndsWith {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
cell_paths,
|
column_paths,
|
||||||
};
|
};
|
||||||
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@@ -68,39 +71,35 @@ impl Command for BytesEndsWith {
|
|||||||
Example {
|
Example {
|
||||||
description: "Checks if binary ends with `0x[AA]`",
|
description: "Checks if binary ends with `0x[AA]`",
|
||||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
|
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
|
||||||
result: Some(Value::test_bool(true)),
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Checks if binary ends with `0x[FF AA AA]`",
|
description: "Checks if binary ends with `0x[FF AA AA]`",
|
||||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
|
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
|
||||||
result: Some(Value::test_bool(true)),
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Checks if binary ends with `0x[11]`",
|
description: "Checks if binary ends with `0x[11]`",
|
||||||
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
|
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
|
||||||
result: Some(Value::test_bool(false)),
|
result: Some(Value::Bool {
|
||||||
|
val: false,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
Value::Bool {
|
||||||
Value::Binary {
|
val: input.ends_with(pattern),
|
||||||
val,
|
span,
|
||||||
span: val_span,
|
|
||||||
} => Value::boolean(val.ends_with(&args.pattern), *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::{Call, CellPath};
|
use nu_protocol::ast::{Call, CellPath};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
end: bool,
|
end: bool,
|
||||||
all: bool,
|
all: bool,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,10 +29,6 @@ impl Command for BytesIndexOf {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes index-of")
|
Signature::build("bytes index-of")
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Binary, Type::Int),
|
|
||||||
(Type::Binary, Type::List(Box::new(Type::Int))),
|
|
||||||
])
|
|
||||||
.required(
|
.required(
|
||||||
"pattern",
|
"pattern",
|
||||||
SyntaxShape::Binary,
|
SyntaxShape::Binary,
|
||||||
@@ -41,7 +37,7 @@ impl Command for BytesIndexOf {
|
|||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, find the indexes at the given cell paths",
|
"optionally returns index of pattern in string by column paths",
|
||||||
)
|
)
|
||||||
.switch("all", "returns all matched index", Some('a'))
|
.switch("all", "returns all matched index", Some('a'))
|
||||||
.switch("end", "search from the end of the binary", Some('e'))
|
.switch("end", "search from the end of the binary", Some('e'))
|
||||||
@@ -53,7 +49,7 @@ impl Command for BytesIndexOf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
vec!["pattern", "match", "find", "search"]
|
vec!["pattern", "match", "find", "search", "index"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@@ -64,13 +60,17 @@ impl Command for BytesIndexOf {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
end: call.has_flag("end"),
|
end: call.has_flag("end"),
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
cell_paths,
|
column_paths,
|
||||||
};
|
};
|
||||||
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
@@ -126,27 +126,7 @@ impl Command for BytesIndexOf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => index_of_impl(val, args, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|
||||||
if arg.all {
|
if arg.all {
|
||||||
search_all_index(input, &arg.pattern, arg.end, span)
|
search_all_index(input, &arg.pattern, arg.end, span)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,14 +1,24 @@
|
|||||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BytesLen;
|
pub struct BytesLen;
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
column_paths: Option<Vec<CellPath>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesArgument for Arguments {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.column_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Command for BytesLen {
|
impl Command for BytesLen {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"bytes length"
|
"bytes length"
|
||||||
@@ -16,12 +26,10 @@ impl Command for BytesLen {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes length")
|
Signature::build("bytes length")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Int)])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, find the length of data at the given cell paths",
|
"optionally find length of binary by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@@ -31,7 +39,7 @@ impl Command for BytesLen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
vec!["size", "count"]
|
vec!["len", "size", "count"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@@ -41,8 +49,13 @@ impl Command for BytesLen {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
|
let arg = Arguments { column_paths };
|
||||||
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,23 +78,10 @@ impl Command for BytesLen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
Value::Int {
|
||||||
Value::Binary {
|
val: input.len() as i64,
|
||||||
val,
|
span,
|
||||||
span: val_span,
|
|
||||||
} => Value::int(val.len() as i64, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ mod replace;
|
|||||||
mod reverse;
|
mod reverse;
|
||||||
mod starts_with;
|
mod starts_with;
|
||||||
|
|
||||||
|
use nu_protocol::ast::CellPath;
|
||||||
|
use nu_protocol::{PipelineData, ShellError, Span, Value};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use add::BytesAdd;
|
pub use add::BytesAdd;
|
||||||
pub use at::BytesAt;
|
pub use at::BytesAt;
|
||||||
pub use build_::BytesBuild;
|
pub use build_::BytesBuild;
|
||||||
@@ -23,3 +28,71 @@ pub use remove::BytesRemove;
|
|||||||
pub use replace::BytesReplace;
|
pub use replace::BytesReplace;
|
||||||
pub use reverse::BytesReverse;
|
pub use reverse::BytesReverse;
|
||||||
pub use starts_with::BytesStartsWith;
|
pub use starts_with::BytesStartsWith;
|
||||||
|
|
||||||
|
trait BytesArgument {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
|
||||||
|
fn operate<C, A>(
|
||||||
|
cmd: C,
|
||||||
|
mut arg: A,
|
||||||
|
input: PipelineData,
|
||||||
|
span: Span,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> Result<PipelineData, ShellError>
|
||||||
|
where
|
||||||
|
A: BytesArgument + Send + Sync + 'static,
|
||||||
|
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
|
||||||
|
{
|
||||||
|
match arg.take_column_paths() {
|
||||||
|
None => input.map(
|
||||||
|
move |v| match v {
|
||||||
|
Value::Binary {
|
||||||
|
val,
|
||||||
|
span: val_span,
|
||||||
|
} => cmd(&val, &arg, val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctrlc,
|
||||||
|
),
|
||||||
|
Some(column_paths) => {
|
||||||
|
let arg = Arc::new(arg);
|
||||||
|
input.map(
|
||||||
|
move |mut v| {
|
||||||
|
for path in &column_paths {
|
||||||
|
let opt = arg.clone();
|
||||||
|
let r = v.update_cell_path(
|
||||||
|
&path.members,
|
||||||
|
Box::new(move |old| {
|
||||||
|
match old {
|
||||||
|
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!(
|
||||||
|
"Input's type is {}. This command only works with bytes.",
|
||||||
|
other.get_type()
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
}}}),
|
||||||
|
);
|
||||||
|
if let Err(error) = r {
|
||||||
|
return Value::Error { error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
},
|
||||||
|
ctrlc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
end: bool,
|
end: bool,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
all: bool,
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,12 +29,11 @@ impl Command for BytesRemove {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes remove")
|
Signature::build("bytes remove")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to find")
|
.required("pattern", SyntaxShape::Binary, "the pattern to find")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, remove bytes from data at the given cell paths",
|
"optionally remove bytes by column paths",
|
||||||
)
|
)
|
||||||
.switch("end", "remove from end of binary", Some('e'))
|
.switch("end", "remove from end of binary", Some('e'))
|
||||||
.switch("all", "remove occurrences of finding binary", Some('a'))
|
.switch("all", "remove occurrences of finding binary", Some('a'))
|
||||||
@@ -43,7 +41,7 @@ impl Command for BytesRemove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Remove bytes"
|
"remove bytes"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@@ -57,11 +55,15 @@ impl Command for BytesRemove {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||||
if pattern_to_remove.item.is_empty() {
|
if pattern_to_remove.item.is_empty() {
|
||||||
return Err(ShellError::TypeMismatch(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"the pattern to remove cannot be empty".to_string(),
|
"the pattern to remove cannot be empty".to_string(),
|
||||||
pattern_to_remove.span,
|
pattern_to_remove.span,
|
||||||
));
|
));
|
||||||
@@ -71,7 +73,7 @@ impl Command for BytesRemove {
|
|||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern: pattern_to_remove,
|
pattern: pattern_to_remove,
|
||||||
end: call.has_flag("end"),
|
end: call.has_flag("end"),
|
||||||
cell_paths,
|
column_paths,
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,27 +135,7 @@ impl Command for BytesRemove {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => remove_impl(val, args, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
let remove_all = arg.all;
|
let remove_all = arg.all;
|
||||||
let input_len = input.len();
|
let input_len = input.len();
|
||||||
@@ -161,7 +143,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
|
|
||||||
// Note:
|
// Note:
|
||||||
// remove_all from start and end will generate the same result.
|
// remove_all from start and end will generate the same result.
|
||||||
// so we'll put `remove_all` relative logic into else clause.
|
// so we'll put `remove_all` relative logic into else clouse.
|
||||||
if arg.end && !remove_all {
|
if arg.end && !remove_all {
|
||||||
let (mut left, mut right) = (
|
let (mut left, mut right) = (
|
||||||
input.len() as isize - arg.pattern.len() as isize,
|
input.len() as isize - arg.pattern.len() as isize,
|
||||||
@@ -172,7 +154,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
left -= 1;
|
left -= 1;
|
||||||
right -= 1;
|
right -= 1;
|
||||||
}
|
}
|
||||||
// append the remaining thing to result, this can be happening when
|
// append the remaining thing to result, this can be happeneed when
|
||||||
// we have something to remove and remove_all is False.
|
// we have something to remove and remove_all is False.
|
||||||
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
||||||
result.append(&mut remain);
|
result.append(&mut remain);
|
||||||
@@ -193,7 +175,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
right += 1;
|
right += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// append the remaining thing to result, this can happened when
|
// append the remaing thing to result, this can happened when
|
||||||
// we have something to remove and remove_all is False.
|
// we have something to remove and remove_all is False.
|
||||||
let mut remain = input[left..].to_vec();
|
let mut remain = input[left..].to_vec();
|
||||||
result.append(&mut remain);
|
result.append(&mut remain);
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
find: Vec<u8>,
|
find: Vec<u8>,
|
||||||
replace: Vec<u8>,
|
replace: Vec<u8>,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
all: bool,
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,13 +29,12 @@ impl Command for BytesReplace {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes replace")
|
Signature::build("bytes replace")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.required("find", SyntaxShape::Binary, "the pattern to find")
|
.required("find", SyntaxShape::Binary, "the pattern to find")
|
||||||
.required("replace", SyntaxShape::Binary, "the replacement pattern")
|
.required("replace", SyntaxShape::Binary, "the replacement pattern")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, replace bytes in data at the given cell paths",
|
"optionally find and replace text by column paths",
|
||||||
)
|
)
|
||||||
.switch("all", "replace all occurrences of find binary", Some('a'))
|
.switch("all", "replace all occurrences of find binary", Some('a'))
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
@@ -57,11 +55,15 @@ impl Command for BytesReplace {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||||
if find.item.is_empty() {
|
if find.item.is_empty() {
|
||||||
return Err(ShellError::TypeMismatch(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"the pattern to find cannot be empty".to_string(),
|
"the pattern to find cannot be empty".to_string(),
|
||||||
find.span,
|
find.span,
|
||||||
));
|
));
|
||||||
@@ -70,7 +72,7 @@ impl Command for BytesReplace {
|
|||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
find: find.item,
|
find: find.item,
|
||||||
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
||||||
cell_paths,
|
column_paths,
|
||||||
all: call.has_flag("all"),
|
all: call.has_flag("all"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,27 +126,7 @@ impl Command for BytesReplace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
|
||||||
Value::Binary {
|
|
||||||
val,
|
|
||||||
span: val_span,
|
|
||||||
} => replace_impl(val, args, *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|
||||||
let mut replaced = vec![];
|
let mut replaced = vec![];
|
||||||
let replace_all = arg.all;
|
let replace_all = arg.all;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
|
struct Arguments {
|
||||||
|
column_paths: Option<Vec<CellPath>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BytesArgument for Arguments {
|
||||||
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
|
self.column_paths.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
||||||
@@ -17,17 +27,16 @@ impl Command for BytesReverse {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes reverse")
|
Signature::build("bytes reverse")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Binary)])
|
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, reverse data at the given cell paths",
|
"optionally matches prefix of text by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Reverse the bytes in the pipeline"
|
"Reverse every bytes in the pipeline"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@@ -41,8 +50,13 @@ impl Command for BytesReverse {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
|
let arg = Arguments { column_paths };
|
||||||
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,30 +82,12 @@ impl Command for BytesReverse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
let mut reversed_input = input.to_vec();
|
||||||
Value::Binary {
|
reversed_input.reverse();
|
||||||
val,
|
Value::Binary {
|
||||||
span: val_span,
|
val: reversed_input,
|
||||||
} => {
|
span,
|
||||||
let mut reversed_input = val.to_vec();
|
|
||||||
reversed_input.reverse();
|
|
||||||
Value::Binary {
|
|
||||||
val: reversed_input,
|
|
||||||
span: *val_span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
use super::{operate, BytesArgument};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::ast::CellPath;
|
use nu_protocol::ast::CellPath;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::Category;
|
use nu_protocol::Category;
|
||||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
pattern: Vec<u8>,
|
pattern: Vec<u8>,
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
column_paths: Option<Vec<CellPath>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
impl BytesArgument for Arguments {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||||
self.cell_paths.take()
|
self.column_paths.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +28,11 @@ impl Command for BytesStartsWith {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("bytes starts-with")
|
Signature::build("bytes starts-with")
|
||||||
.input_output_types(vec![(Type::Binary, Type::Bool)])
|
|
||||||
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
.required("pattern", SyntaxShape::Binary, "the pattern to match")
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, check if bytes at the given cell paths start with the pattern",
|
"optionally matches prefix of text by column paths",
|
||||||
)
|
)
|
||||||
.category(Category::Bytes)
|
.category(Category::Bytes)
|
||||||
}
|
}
|
||||||
@@ -54,11 +53,15 @@ impl Command for BytesStartsWith {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let column_paths = if column_paths.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(column_paths)
|
||||||
|
};
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
pattern,
|
pattern,
|
||||||
cell_paths,
|
column_paths,
|
||||||
};
|
};
|
||||||
operate(
|
operate(
|
||||||
starts_with,
|
starts_with,
|
||||||
@@ -74,39 +77,35 @@ impl Command for BytesStartsWith {
|
|||||||
Example {
|
Example {
|
||||||
description: "Checks if binary starts with `0x[1F FF AA]`",
|
description: "Checks if binary starts with `0x[1F FF AA]`",
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
|
||||||
result: Some(Value::test_bool(true)),
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Checks if binary starts with `0x[1F]`",
|
description: "Checks if binary starts with `0x[1F]`",
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
|
||||||
result: Some(Value::test_bool(true)),
|
result: Some(Value::Bool {
|
||||||
|
val: true,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Checks if binary starts with `0x[1F]`",
|
description: "Checks if binary starts with `0x[1F]`",
|
||||||
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
|
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
|
||||||
result: Some(Value::test_bool(false)),
|
result: Some(Value::Bool {
|
||||||
|
val: false,
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
||||||
match val {
|
Value::Bool {
|
||||||
Value::Binary {
|
val: input.starts_with(pattern),
|
||||||
val,
|
span,
|
||||||
span: val_span,
|
|
||||||
} => Value::boolean(val.starts_with(&args.pattern), *val_span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => val.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"binary".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
/// ```text
|
/// ```text
|
||||||
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
|
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Eq, Debug, Ord, PartialOrd)]
|
#[derive(Eq, Debug)]
|
||||||
pub enum HashableValue {
|
pub enum HashableValue {
|
||||||
Bool {
|
Bool {
|
||||||
val: bool,
|
val: bool,
|
||||||
@@ -53,7 +53,7 @@ impl Default for HashableValue {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
HashableValue::Bool {
|
HashableValue::Bool {
|
||||||
val: false,
|
val: false,
|
||||||
span: Span::unknown(),
|
span: Span { start: 0, end: 0 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,14 +78,13 @@ impl HashableValue {
|
|||||||
Value::String { val, span } => Ok(HashableValue::String { val, span }),
|
Value::String { val, span } => Ok(HashableValue::String { val, span }),
|
||||||
Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }),
|
Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }),
|
||||||
|
|
||||||
// Explicitly propagate errors instead of dropping them.
|
_ => {
|
||||||
Value::Error { error } => Err(error),
|
let input_span = value.span().unwrap_or(span);
|
||||||
_ => Err(ShellError::UnsupportedInput(
|
Err(ShellError::UnsupportedInput(
|
||||||
"input value is not hashable".into(),
|
format!("input value {value:?} is not hashable"),
|
||||||
format!("input type: {:?}", value.get_type()),
|
input_span,
|
||||||
span,
|
))
|
||||||
value.expect_span(),
|
}
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +214,7 @@ mod test {
|
|||||||
];
|
];
|
||||||
for (val, expect_hashable_val) in values.into_iter() {
|
for (val, expect_hashable_val) in values.into_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
HashableValue::from_value(val, Span::unknown()).unwrap(),
|
HashableValue::from_value(val, Span { start: 0, end: 0 }).unwrap(),
|
||||||
expect_hashable_val
|
expect_hashable_val
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -229,7 +228,7 @@ mod test {
|
|||||||
vals: vec![Value::Bool { val: true, span }],
|
vals: vec![Value::Bool { val: true, span }],
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Closure {
|
Value::Block {
|
||||||
val: 0,
|
val: 0,
|
||||||
captures: HashMap::new(),
|
captures: HashMap::new(),
|
||||||
span,
|
span,
|
||||||
@@ -246,7 +245,7 @@ mod test {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
for v in values {
|
for v in values {
|
||||||
assert!(HashableValue::from_value(v, Span::unknown()).is_err())
|
assert!(HashableValue::from_value(v, Span { start: 0, end: 0 }).is_err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +266,7 @@ mod test {
|
|||||||
for val in values.into_iter() {
|
for val in values.into_iter() {
|
||||||
let expected_val = val.clone();
|
let expected_val = val.clone();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
HashableValue::from_value(val, Span::unknown())
|
HashableValue::from_value(val, Span { start: 0, end: 0 })
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_value(),
|
.into_value(),
|
||||||
expected_val
|
expected_val
|
||||||
@@ -280,11 +279,14 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
HashableValue::Bool {
|
HashableValue::Bool {
|
||||||
val: true,
|
val: true,
|
||||||
span: Span::new(0, 1)
|
span: Span { start: 0, end: 1 }
|
||||||
},
|
},
|
||||||
HashableValue::Bool {
|
HashableValue::Bool {
|
||||||
val: true,
|
val: true,
|
||||||
span: Span::new(90, 1000)
|
span: Span {
|
||||||
|
start: 90,
|
||||||
|
end: 1000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -297,7 +299,7 @@ mod test {
|
|||||||
assert!(set.contains(&HashableValue::Bool { val: true, span }));
|
assert!(set.contains(&HashableValue::Bool { val: true, span }));
|
||||||
|
|
||||||
// hashable value doesn't care about span.
|
// hashable value doesn't care about span.
|
||||||
let diff_span = Span::new(1, 2);
|
let diff_span = Span { start: 1, end: 2 };
|
||||||
set.insert(HashableValue::Bool {
|
set.insert(HashableValue::Bool {
|
||||||
val: true,
|
val: true,
|
||||||
span: diff_span,
|
span: diff_span,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use super::hashable_value::HashableValue;
|
use super::hashable_value::HashableValue;
|
||||||
use itertools::Itertools;
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||||
Type, Value,
|
Value,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@@ -25,7 +24,6 @@ impl Command for Histogram {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("histogram")
|
Signature::build("histogram")
|
||||||
.input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Table(vec![])),])
|
|
||||||
.optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list")
|
.optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list")
|
||||||
.optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output")
|
.optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output")
|
||||||
.named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t'))
|
.named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t'))
|
||||||
@@ -38,49 +36,24 @@ impl Command for Histogram {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Compute a histogram of file types",
|
description: "Get a histogram for the types of files",
|
||||||
example: "ls | histogram type",
|
example: "ls | histogram type",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Compute a histogram for the types of files, with frequency column named freq",
|
"Get a histogram for the types of files, with frequency column named freq",
|
||||||
example: "ls | histogram type freq",
|
example: "ls | histogram type freq",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Compute a histogram for a list of numbers",
|
description: "Get a histogram for a list of numbers",
|
||||||
example: "[1 2 1] | histogram",
|
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram",
|
||||||
result: Some(Value::List {
|
result: None,
|
||||||
vals: vec![Value::Record {
|
|
||||||
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(1),
|
|
||||||
Value::test_int(2),
|
|
||||||
Value::test_float(0.6666666666666666),
|
|
||||||
Value::test_string("66.67%"),
|
|
||||||
Value::test_string("******************************************************************"),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
},
|
|
||||||
Value::Record {
|
|
||||||
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
|
|
||||||
vals: vec![
|
|
||||||
Value::test_int(2),
|
|
||||||
Value::test_int(1),
|
|
||||||
Value::test_float(0.3333333333333333),
|
|
||||||
Value::test_string("33.33%"),
|
|
||||||
Value::test_string("*********************************"),
|
|
||||||
],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}],
|
|
||||||
span: Span::test_data(),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value",
|
description: "Get a histogram for a list of numbers, and percentage is based on the maximum value",
|
||||||
example: "[1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
|
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
|
||||||
result: None,
|
result: None,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -98,11 +71,12 @@ impl Command for Histogram {
|
|||||||
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
|
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
|
||||||
let frequency_column_name = match frequency_name_arg {
|
let frequency_column_name = match frequency_name_arg {
|
||||||
Some(inner) => {
|
Some(inner) => {
|
||||||
|
let span = inner.span;
|
||||||
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
|
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
|
||||||
return Err(ShellError::TypeMismatch(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"frequency-column-name can't be 'value', 'count' or 'percentage'"
|
"frequency-column-name can't be 'value', 'count' or 'percentage'"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
inner.span,
|
span,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
inner.item
|
inner.item
|
||||||
@@ -118,7 +92,7 @@ impl Command for Histogram {
|
|||||||
"normalize" => PercentageCalcMethod::Normalize,
|
"normalize" => PercentageCalcMethod::Normalize,
|
||||||
"relative" => PercentageCalcMethod::Relative,
|
"relative" => PercentageCalcMethod::Relative,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::TypeMismatch(
|
return Err(ShellError::UnsupportedInput(
|
||||||
"calc method can only be 'normalize' or 'relative'".to_string(),
|
"calc method can only be 'normalize' or 'relative'".to_string(),
|
||||||
inner.span,
|
inner.span,
|
||||||
))
|
))
|
||||||
@@ -129,15 +103,16 @@ impl Command for Histogram {
|
|||||||
let span = call.head;
|
let span = call.head;
|
||||||
let data_as_value = input.into_value(span);
|
let data_as_value = input.into_value(span);
|
||||||
// `input` is not a list, here we can return an error.
|
// `input` is not a list, here we can return an error.
|
||||||
run_histogram(
|
match data_as_value.as_list() {
|
||||||
data_as_value.as_list()?.to_vec(),
|
Ok(list_value) => run_histogram(
|
||||||
column_name,
|
list_value.to_vec(),
|
||||||
frequency_column_name,
|
column_name,
|
||||||
calc_method,
|
frequency_column_name,
|
||||||
span,
|
calc_method,
|
||||||
// Note that as_list() filters out Value::Error here.
|
span,
|
||||||
data_as_value.expect_span(),
|
),
|
||||||
)
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +122,6 @@ fn run_histogram(
|
|||||||
freq_column: String,
|
freq_column: String,
|
||||||
calc_method: PercentageCalcMethod,
|
calc_method: PercentageCalcMethod,
|
||||||
head_span: Span,
|
head_span: Span,
|
||||||
list_span: Span,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut inputs = vec![];
|
let mut inputs = vec![];
|
||||||
// convert from inputs to hashable values.
|
// convert from inputs to hashable values.
|
||||||
@@ -156,24 +130,14 @@ fn run_histogram(
|
|||||||
// some invalid input scenario needs to handle:
|
// some invalid input scenario needs to handle:
|
||||||
// Expect input is a list of hashable value, if one value is not hashable, throw out error.
|
// Expect input is a list of hashable value, if one value is not hashable, throw out error.
|
||||||
for v in values {
|
for v in values {
|
||||||
match v {
|
let current_span = v.span().unwrap_or(head_span);
|
||||||
// Propagate existing errors.
|
inputs.push(HashableValue::from_value(v, head_span).map_err(|_| {
|
||||||
Value::Error { error } => return Err(error),
|
ShellError::UnsupportedInput(
|
||||||
_ => {
|
"--column-name is not provided, can only support a list of simple value."
|
||||||
let t = v.get_type();
|
.to_string(),
|
||||||
let span = v.expect_span();
|
current_span,
|
||||||
inputs.push(HashableValue::from_value(v, head_span).map_err(|_| {
|
)
|
||||||
ShellError::UnsupportedInput(
|
})?);
|
||||||
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
|
|
||||||
format!(
|
|
||||||
"input type: {t:?}"
|
|
||||||
),
|
|
||||||
head_span,
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
})?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(ref col) => {
|
Some(ref col) => {
|
||||||
@@ -195,17 +159,14 @@ fn run_histogram(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Propagate existing errors.
|
|
||||||
Value::Error { error } => return Err(error),
|
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if inputs.is_empty() {
|
if inputs.is_empty() {
|
||||||
return Err(ShellError::CantFindColumn(
|
return Err(ShellError::UnsupportedInput(
|
||||||
col_name.clone(),
|
format!("expect input is table, and inputs doesn't contain any value which has {col_name} column"),
|
||||||
head_span,
|
head_span,
|
||||||
list_span,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,42 +213,34 @@ fn histogram_impl(
|
|||||||
freq_column.to_string(),
|
freq_column.to_string(),
|
||||||
];
|
];
|
||||||
const MAX_FREQ_COUNT: f64 = 100.0;
|
const MAX_FREQ_COUNT: f64 = 100.0;
|
||||||
for (val, count) in counter.into_iter().sorted() {
|
for (val, count) in counter.into_iter() {
|
||||||
let quantile = match calc_method {
|
let quantile = match calc_method {
|
||||||
PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64,
|
PercentageCalcMethod::Normalize => (count as f64 / total_cnt as f64),
|
||||||
PercentageCalcMethod::Relative => count as f64 / max_cnt as f64,
|
PercentageCalcMethod::Relative => (count as f64 / max_cnt as f64),
|
||||||
};
|
};
|
||||||
|
|
||||||
let percentage = format!("{:.2}%", quantile * 100_f64);
|
let percentage = format!("{:.2}%", quantile * 100_f64);
|
||||||
let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize);
|
let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize);
|
||||||
|
|
||||||
result.push((
|
result.push(Value::Record {
|
||||||
count, // attach count first for easily sorting.
|
cols: result_cols.clone(),
|
||||||
Value::Record {
|
vals: vec![
|
||||||
cols: result_cols.clone(),
|
val.into_value(),
|
||||||
vals: vec![
|
Value::Int { val: count, span },
|
||||||
val.into_value(),
|
Value::Float {
|
||||||
Value::Int { val: count, span },
|
val: quantile,
|
||||||
Value::Float {
|
span,
|
||||||
val: quantile,
|
},
|
||||||
span,
|
Value::String {
|
||||||
},
|
val: percentage,
|
||||||
Value::String {
|
span,
|
||||||
val: percentage,
|
},
|
||||||
span,
|
Value::String { val: freq, span },
|
||||||
},
|
],
|
||||||
Value::String { val: freq, span },
|
span,
|
||||||
],
|
});
|
||||||
span,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
result.sort_by(|a, b| b.0.cmp(&a.0));
|
Value::List { vals: result, span }.into_pipeline_data()
|
||||||
Value::List {
|
|
||||||
vals: result.into_iter().map(|x| x.1).collect(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.into_pipeline_data()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
use crate::input_handler::{operate, CmdArgument};
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_protocol::{
|
|
||||||
ast::{Call, CellPath},
|
|
||||||
engine::{Command, EngineState, Stack},
|
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
|
||||||
};
|
|
||||||
use print_positions::print_positions;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Fill;
|
|
||||||
|
|
||||||
struct Arguments {
|
|
||||||
width: usize,
|
|
||||||
alignment: FillAlignment,
|
|
||||||
character: String,
|
|
||||||
cell_paths: Option<Vec<CellPath>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmdArgument for Arguments {
|
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
|
||||||
self.cell_paths.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum FillAlignment {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Middle,
|
|
||||||
MiddleRight,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for Fill {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"fill"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Fill and Align"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
|
||||||
Signature::build("fill")
|
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Int, Type::String),
|
|
||||||
(Type::Float, Type::String),
|
|
||||||
(Type::String, Type::String),
|
|
||||||
(Type::Filesize, Type::String),
|
|
||||||
])
|
|
||||||
.vectorizes_over_list(true)
|
|
||||||
.named(
|
|
||||||
"width",
|
|
||||||
SyntaxShape::Int,
|
|
||||||
"The width of the output. Defaults to 1",
|
|
||||||
Some('w'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"alignment",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"The alignment of the output. Defaults to Left (Left(l), Right(r), Center(c/m), MiddleRight(cr/mr))",
|
|
||||||
Some('a'),
|
|
||||||
)
|
|
||||||
.named(
|
|
||||||
"character",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"The character to fill with. Defaults to ' ' (space)",
|
|
||||||
Some('c'),
|
|
||||||
)
|
|
||||||
.category(Category::Conversions)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["display", "render", "format", "pad", "align"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Fill a string on the left side to a width of 15 with the character '─'",
|
|
||||||
example: "'nushell' | fill -a l -c '─' -w 15",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "nushell────────".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Fill a string on the right side to a width of 15 with the character '─'",
|
|
||||||
example: "'nushell' | fill -a r -c '─' -w 15",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "────────nushell".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Fill a string on both sides to a width of 15 with the character '─'",
|
|
||||||
example: "'nushell' | fill -a m -c '─' -w 15",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "────nushell────".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Fill a number on the left side to a width of 5 with the character '0'",
|
|
||||||
example: "1 | fill --alignment right --character 0 --width 5",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "00001".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Fill a number on both sides to a width of 5 with the character '0'",
|
|
||||||
example: "1.1 | fill --alignment center --character 0 --width 5",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "01.10".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Fill a filesize on the left side to a width of 5 with the character '0'",
|
|
||||||
example: "1kib | fill --alignment middle --character 0 --width 10",
|
|
||||||
result: Some(Value::String {
|
|
||||||
val: "0001024000".into(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
fill(engine_state, stack, call, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill(
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
||||||
let width_arg: Option<usize> = call.get_flag(engine_state, stack, "width")?;
|
|
||||||
let alignment_arg: Option<String> = call.get_flag(engine_state, stack, "alignment")?;
|
|
||||||
let character_arg: Option<String> = call.get_flag(engine_state, stack, "character")?;
|
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
|
||||||
|
|
||||||
let alignment = if let Some(arg) = alignment_arg {
|
|
||||||
match arg.to_lowercase().as_str() {
|
|
||||||
"l" | "left" => FillAlignment::Left,
|
|
||||||
"r" | "right" => FillAlignment::Right,
|
|
||||||
"c" | "center" | "m" | "middle" => FillAlignment::Middle,
|
|
||||||
"cr" | "centerright" | "mr" | "middleright" => FillAlignment::MiddleRight,
|
|
||||||
_ => FillAlignment::Left,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
FillAlignment::Left
|
|
||||||
};
|
|
||||||
|
|
||||||
let width = if let Some(arg) = width_arg { arg } else { 1 };
|
|
||||||
|
|
||||||
let character = if let Some(arg) = character_arg {
|
|
||||||
arg
|
|
||||||
} else {
|
|
||||||
" ".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg = Arguments {
|
|
||||||
width,
|
|
||||||
alignment,
|
|
||||||
character,
|
|
||||||
cell_paths,
|
|
||||||
};
|
|
||||||
|
|
||||||
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|
||||||
match input {
|
|
||||||
Value::Int { val, .. } => fill_int(*val, args, span),
|
|
||||||
Value::Filesize { val, .. } => fill_int(*val, args, span),
|
|
||||||
Value::Float { val, .. } => fill_float(*val, args, span),
|
|
||||||
Value::String { val, .. } => fill_string(val, args, span),
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => input.clone(),
|
|
||||||
other => Value::Error {
|
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"int, filesize, float, string".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_float(num: f64, args: &Arguments, span: Span) -> Value {
|
|
||||||
let s = num.to_string();
|
|
||||||
let out_str = pad(&s, args.width, &args.character, args.alignment, false);
|
|
||||||
|
|
||||||
Value::String { val: out_str, span }
|
|
||||||
}
|
|
||||||
fn fill_int(num: i64, args: &Arguments, span: Span) -> Value {
|
|
||||||
let s = num.to_string();
|
|
||||||
let out_str = pad(&s, args.width, &args.character, args.alignment, false);
|
|
||||||
|
|
||||||
Value::String { val: out_str, span }
|
|
||||||
}
|
|
||||||
fn fill_string(s: &str, args: &Arguments, span: Span) -> Value {
|
|
||||||
let out_str = pad(s, args.width, &args.character, args.alignment, false);
|
|
||||||
|
|
||||||
Value::String { val: out_str, span }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad(s: &str, width: usize, pad_char: &str, alignment: FillAlignment, truncate: bool) -> String {
|
|
||||||
// Attribution: Most of this function was taken from https://github.com/ogham/rust-pad and tweaked. Thank you!
|
|
||||||
// Use width instead of len for graphical display
|
|
||||||
|
|
||||||
let cols = print_positions(s).count();
|
|
||||||
|
|
||||||
if cols >= width {
|
|
||||||
if truncate {
|
|
||||||
return s[..width].to_string();
|
|
||||||
} else {
|
|
||||||
return s.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = width - cols;
|
|
||||||
|
|
||||||
let (left_pad, right_pad) = match alignment {
|
|
||||||
FillAlignment::Left => (0, diff),
|
|
||||||
FillAlignment::Right => (diff, 0),
|
|
||||||
FillAlignment::Middle => (diff / 2, diff - diff / 2),
|
|
||||||
FillAlignment::MiddleRight => (diff - diff / 2, diff / 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut new_str = String::new();
|
|
||||||
for _ in 0..left_pad {
|
|
||||||
new_str.push_str(pad_char)
|
|
||||||
}
|
|
||||||
new_str.push_str(s);
|
|
||||||
for _ in 0..right_pad {
|
|
||||||
new_str.push_str(pad_char)
|
|
||||||
}
|
|
||||||
new_str
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_examples() {
|
|
||||||
use crate::test_examples;
|
|
||||||
|
|
||||||
test_examples(Fill {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -19,9 +18,7 @@ impl Command for Fmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("fmt")
|
Signature::build("fmt").category(Category::Conversions)
|
||||||
.input_output_types(vec![(Type::Number, Type::Record(vec![]))])
|
|
||||||
.category(Category::Conversions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@@ -44,14 +41,38 @@ impl Command for Fmt {
|
|||||||
"upperhex".into(),
|
"upperhex".into(),
|
||||||
],
|
],
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::test_string("0b101010"),
|
Value::String {
|
||||||
Value::test_string("42"),
|
val: "0b101010".to_string(),
|
||||||
Value::test_string("42"),
|
span: Span::test_data(),
|
||||||
Value::test_string("4.2e1"),
|
},
|
||||||
Value::test_string("0x2a"),
|
Value::String {
|
||||||
Value::test_string("0o52"),
|
val: "42".to_string(),
|
||||||
Value::test_string("4.2E1"),
|
span: Span::test_data(),
|
||||||
Value::test_string("0x2A"),
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "42".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "4.2e1".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "0x2a".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "0o52".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "4.2E1".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
|
Value::String {
|
||||||
|
val: "0x2A".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
@@ -64,7 +85,7 @@ impl Command for Fmt {
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
fmt(engine_state, stack, call, input)
|
fmt(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,25 +95,39 @@ fn fmt(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let head = call.head;
|
||||||
let args = CellPathOnlyArgs::from(cell_paths);
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
|
input.map(
|
||||||
|
move |v| {
|
||||||
|
if column_paths.is_empty() {
|
||||||
|
action(&v, head)
|
||||||
|
} else {
|
||||||
|
let mut ret = v;
|
||||||
|
for path in &column_paths {
|
||||||
|
let r =
|
||||||
|
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||||
|
if let Err(error) = r {
|
||||||
|
return Value::Error { error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
pub fn action(input: &Value, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Int { val, .. } => fmt_it(*val, span),
|
Value::Int { val, .. } => fmt_it(*val, span),
|
||||||
Value::Filesize { val, .. } => fmt_it(*val, span),
|
Value::Filesize { val, .. } => fmt_it(*val, span),
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
_ => Value::Error {
|
||||||
Value::Error { .. } => input.clone(),
|
error: ShellError::UnsupportedInput(
|
||||||
other => Value::Error {
|
format!("unsupported input type: {:?}", input.get_type()),
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer or filesize".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
span,
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -103,31 +138,31 @@ fn fmt_it(num: i64, span: Span) -> Value {
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
cols.push("binary".into());
|
cols.push("binary".into());
|
||||||
vals.push(Value::string(format!("{num:#b}"), span));
|
vals.push(Value::string(format!("{:#b}", num), span));
|
||||||
|
|
||||||
cols.push("debug".into());
|
cols.push("debug".into());
|
||||||
vals.push(Value::string(format!("{num:#?}"), span));
|
vals.push(Value::string(format!("{:#?}", num), span));
|
||||||
|
|
||||||
cols.push("display".into());
|
cols.push("display".into());
|
||||||
vals.push(Value::string(format!("{num}"), span));
|
vals.push(Value::string(format!("{}", num), span));
|
||||||
|
|
||||||
cols.push("lowerexp".into());
|
cols.push("lowerexp".into());
|
||||||
vals.push(Value::string(format!("{num:#e}"), span));
|
vals.push(Value::string(format!("{:#e}", num), span));
|
||||||
|
|
||||||
cols.push("lowerhex".into());
|
cols.push("lowerhex".into());
|
||||||
vals.push(Value::string(format!("{num:#x}"), span));
|
vals.push(Value::string(format!("{:#x}", num), span));
|
||||||
|
|
||||||
cols.push("octal".into());
|
cols.push("octal".into());
|
||||||
vals.push(Value::string(format!("{num:#o}"), span));
|
vals.push(Value::string(format!("{:#o}", num), span));
|
||||||
|
|
||||||
// cols.push("pointer".into());
|
// cols.push("pointer".into());
|
||||||
// vals.push(Value::string(format!("{:#p}", &num), span));
|
// vals.push(Value::string(format!("{:#p}", &num), span));
|
||||||
|
|
||||||
cols.push("upperexp".into());
|
cols.push("upperexp".into());
|
||||||
vals.push(Value::string(format!("{num:#E}"), span));
|
vals.push(Value::string(format!("{:#E}", num), span));
|
||||||
|
|
||||||
cols.push("upperhex".into());
|
cols.push("upperhex".into());
|
||||||
vals.push(Value::string(format!("{num:#X}"), span));
|
vals.push(Value::string(format!("{:#X}", num), span));
|
||||||
|
|
||||||
Value::Record { cols, vals, span }
|
Value::Record { cols, vals, span }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
Type, Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -17,20 +16,10 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into binary")
|
Signature::build("into binary")
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Binary, Type::Binary),
|
|
||||||
(Type::Int, Type::Binary),
|
|
||||||
(Type::Number, Type::Binary),
|
|
||||||
(Type::String, Type::Binary),
|
|
||||||
(Type::Bool, Type::Binary),
|
|
||||||
(Type::Filesize, Type::Binary),
|
|
||||||
(Type::Date, Type::Binary),
|
|
||||||
])
|
|
||||||
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, convert data at the given cell paths",
|
"column paths to convert to binary (for table input)",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@@ -40,7 +29,7 @@ impl Command for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
vec!["convert", "bytes"]
|
vec!["convert", "binary", "bytes", "bin"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@@ -49,7 +38,7 @@ impl Command for SubCommand {
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_binary(engine_state, stack, call, input)
|
into_binary(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,9 +98,9 @@ fn into_binary(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
||||||
@@ -131,10 +120,27 @@ fn into_binary(
|
|||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => input.map(
|
||||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
move |v| {
|
||||||
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
|
if column_paths.is_empty() {
|
||||||
}
|
action(&v, head)
|
||||||
|
} else {
|
||||||
|
let mut ret = v;
|
||||||
|
for path in &column_paths {
|
||||||
|
let r = ret.update_cell_path(
|
||||||
|
&path.members,
|
||||||
|
Box::new(move |old| action(old, head)),
|
||||||
|
);
|
||||||
|
if let Err(error) = r {
|
||||||
|
return Value::Error { error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +160,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
pub fn action(input: &Value, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Binary { .. } => input.clone(),
|
Value::Binary { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::Binary {
|
Value::Int { val, .. } => Value::Binary {
|
||||||
@@ -174,27 +180,16 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Bool { val, .. } => Value::Binary {
|
Value::Bool { val, .. } => Value::Binary {
|
||||||
val: int_to_endian(i64::from(*val)),
|
val: int_to_endian(if *val { 1i64 } else { 0 }),
|
||||||
span,
|
|
||||||
},
|
|
||||||
Value::Duration { val, .. } => Value::Binary {
|
|
||||||
val: int_to_endian(*val),
|
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Date { val, .. } => Value::Binary {
|
Value::Date { val, .. } => Value::Binary {
|
||||||
val: val.format("%c").to_string().as_bytes().to_vec(),
|
val: val.format("%c").to_string().as_bytes().to_vec(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
|
||||||
Value::Error { .. } => input.clone(),
|
_ => Value::Error {
|
||||||
other => Value::Error {
|
error: ShellError::UnsupportedInput("'into binary' for unsupported type".into(), span),
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"integer, float, filesize, string, date, duration, binary or bool".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -16,17 +15,10 @@ impl Command for SubCommand {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("into bool")
|
Signature::build("into bool")
|
||||||
.input_output_types(vec![
|
|
||||||
(Type::Int, Type::Bool),
|
|
||||||
(Type::Number, Type::Bool),
|
|
||||||
(Type::String, Type::Bool),
|
|
||||||
(Type::Bool, Type::Bool),
|
|
||||||
(Type::List(Box::new(Type::Any)), Type::Table(vec![])),
|
|
||||||
])
|
|
||||||
.rest(
|
.rest(
|
||||||
"rest",
|
"rest",
|
||||||
SyntaxShape::CellPath,
|
SyntaxShape::CellPath,
|
||||||
"for a data structure input, convert data at the given cell paths",
|
"column paths to convert to boolean (for table input)",
|
||||||
)
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
@@ -45,7 +37,7 @@ impl Command for SubCommand {
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_bool(engine_state, stack, call, input)
|
into_bool(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +46,7 @@ impl Command for SubCommand {
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Convert value to boolean in table",
|
description: "Convert value to boolean in table",
|
||||||
example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
|
example: "echo [[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
|
||||||
result: Some(Value::List {
|
result: Some(Value::List {
|
||||||
vals: vec![
|
vals: vec![
|
||||||
Value::Record {
|
Value::Record {
|
||||||
@@ -96,11 +88,6 @@ impl Command for SubCommand {
|
|||||||
example: "1 | into bool",
|
example: "1 | into bool",
|
||||||
result: Some(Value::boolean(true, span)),
|
result: Some(Value::boolean(true, span)),
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
description: "convert decimal to boolean",
|
|
||||||
example: "0.3 | into bool",
|
|
||||||
result: Some(Value::boolean(true, span)),
|
|
||||||
},
|
|
||||||
Example {
|
Example {
|
||||||
description: "convert decimal string to boolean",
|
description: "convert decimal string to boolean",
|
||||||
example: "'0.0' | into bool",
|
example: "'0.0' | into bool",
|
||||||
@@ -121,9 +108,28 @@ fn into_bool(
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let head = call.head;
|
||||||
let args = CellPathOnlyArgs::from(cell_paths);
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
|
||||||
|
input.map(
|
||||||
|
move |v| {
|
||||||
|
if column_paths.is_empty() {
|
||||||
|
action(&v, head)
|
||||||
|
} else {
|
||||||
|
let mut ret = v;
|
||||||
|
for path in &column_paths {
|
||||||
|
let r =
|
||||||
|
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||||
|
if let Err(error) = r {
|
||||||
|
return Value::Error { error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
||||||
@@ -148,7 +154,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn action(input: &Value, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Bool { .. } => input.clone(),
|
Value::Bool { .. } => input.clone(),
|
||||||
Value::Int { val, .. } => Value::Bool {
|
Value::Int { val, .. } => Value::Bool {
|
||||||
@@ -163,15 +169,10 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
Ok(val) => Value::Bool { val, span },
|
Ok(val) => Value::Bool { val, span },
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
},
|
},
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
_ => Value::Error {
|
||||||
Value::Error { .. } => input.clone(),
|
error: ShellError::UnsupportedInput(
|
||||||
other => Value::Error {
|
"'into bool' does not support this input".into(),
|
||||||
error: ShellError::OnlySupportsThisInputType(
|
|
||||||
"bool, integer, float or string".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
span,
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user