Compare commits

..

2 Commits

Author SHA1 Message Date
0120e4040d Bump to 0.77.1 development version (#8453)
# Description

Either to be used in an emergency point release or to indicate
development builds in the `version` command
2023-03-17 18:10:56 +13:00
716c144e24 Revert "Throw out error if external command in subexpression is failed to run (#8204)" (#8475)
This reverts commit dec0a2517f.

It breaks programs like `fzf`

# Description

Fixes: #8472 
Fixes:  #8313
Reopen: #7690 

# User-Facing Changes

_(List of all changes that impact the user experience here. This helps
us keep track of breaking changes.)_

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2023-03-17 17:42:26 +13:00
537 changed files with 16339 additions and 25844 deletions

View File

@ -1,5 +0,0 @@
#!/usr/bin/env nu
use ../toolkit.nu fmt
fmt --check --verbose

View File

@ -1,6 +0,0 @@
#!/usr/bin/env nu
use ../toolkit.nu [fmt, clippy]
fmt --check --verbose
clippy --verbose

View File

@ -1,6 +1,5 @@
name: Bug Report
description: Create a report to help us improve
labels: ["needs-triage"]
body:
- type: textarea
id: description

View File

@ -1,6 +1,6 @@
name: Feature Request
description: "When you want a new feature for something that doesn't already exist"
labels: ["needs-triage", "enhancement"]
labels: "enhancement"
body:
- type: textarea
id: problem

View File

@ -1,11 +0,0 @@
---
name: standard library bug or feature report
about: Used to submit issues related to the nu standard library
title: ''
labels: ['needs-triage', 'std-library']
assignees: ''
---
**Describe the bug or feature**
A clear and concise description of what the bug is.

View File

@ -1,24 +1,23 @@
# 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 goes here. **Provide examples and/or screenshots** if your changes affect the user experience.
-->
_(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_
_(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. -->
_(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands:
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library
> **Note**
> from `nushell` you can also use the `toolkit` as follows
@ -26,7 +25,7 @@ Make sure you've run and fixed any issues with these commands:
> use toolkit.nu # or use an `env_change` hook to activate it automatically
> toolkit check pr
> ```
-->
# After Submitting
<!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
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.

View File

@ -40,13 +40,13 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: cargo fmt
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
nu-tests:
env:
@ -77,14 +77,14 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
std-lib-and-python-virtualenv:
python-virtualenv:
env:
NU_LOG_LEVEL: DEBUG
NUSHELL_CARGO_TARGET: ci
strategy:
fail-fast: true
@ -101,22 +101,10 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: Install Nushell
# prior to [*standard library: bring the tests into the main CI*](#8525)
# there was a `--profile ci` here in the `cargo install`, as well as
# `NUSHELL_CARGO_TARGET: ci` in the prelude above.
#
# this caused a "stackoverflow" error in the CI on windows,
# see [this failing job](https://github.com/nushell/nushell/actions/runs/4512034615/jobs/7944945590)
#
# the CI profile has been removed in 00b820de9021227d1910a9ea388297ee7aee308e
# as part of #8525.
run: cargo install --path . --locked --no-default-features
- name: Standard library tests
run: nu -c 'use std; std run-tests --path crates/nu-std'
run: cargo install --locked --path=. --profile ci --no-default-features
- name: Setup Python
uses: actions/setup-python@v4
@ -160,16 +148,17 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: Clippy
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
- name: Tests
run: cargo test --profile ci --package nu_plugin_*
nu-coverage:
needs: nu-tests
env:
NUSHELL_CARGO_TARGET: ci
@ -188,7 +177,7 @@ jobs:
- uses: actions/checkout@v3
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov

View File

@ -50,17 +50,17 @@ let flags = $env.TARGET_RUSTFLAGS
let dist = $'($env.GITHUB_WORKSPACE)/output'
let version = (open Cargo.toml | get package.version)
print $'Debugging info:'
$'Debugging info:'
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
# $env
let USE_UBUNTU = 'ubuntu-20.04'
print $'(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 }
print $'Start building ($bin)...'; hr-line
$'Start building ($bin)...'; hr-line
# ----------------------------------------------------------------------------
# Build for Ubuntu and macOS
@ -70,28 +70,23 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
sudo apt update
sudo apt-get install libxcb-composite0-dev -y
}
match $target {
'aarch64-unknown-linux-gnu' => {
sudo apt-get install gcc-aarch64-linux-gnu -y
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
cargo-build-nu $flags
}
'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
}
'armv7-unknown-linux-gnueabihf' => {
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
cargo-build-nu $flags
}
_ => {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
cargo-build-nu $flags
}
if $target == 'aarch64-unknown-linux-gnu' {
sudo apt-get install gcc-aarch64-linux-gnu -y
let-env CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
cargo-build-nu $flags
} else if $target == 'armv7-unknown-linux-gnueabihf' {
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
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 {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
cargo-build-nu $flags
}
}
@ -112,34 +107,31 @@ if $os in ['windows-latest'] {
let suffix = if $os == 'windows-latest' { '.exe' }
# nu, nu_plugin_* were all included
let executable = $'target/($target)/release/($bin)*($suffix)'
print $'Current executable file: ($executable)'
$'Current executable file: ($executable)'
cd $src; mkdir $dist;
rm -rf $'target/($target)/release/*.d' $'target/($target)/release/nu_pretty_hex*'
print $'(char nl)All executable files:'; hr-line
# We have to use `print` here to make sure the command output is displayed
print (ls -f $executable); sleep 1sec
$'(char nl)All executable files:'; hr-line
ls -f $executable
print $'(char nl)Copying release files...'; hr-line
$'(char nl)Copying release files...'; hr-line
cp -v README.release.txt $'($dist)/README.txt'
[LICENSE $executable] | each {|it| cp -rv $it $dist } | flatten
# Sleep a few seconds to make sure the cp process finished successfully
sleep 3sec
print $'(char nl)Check binary release version detail:'; hr-line
$'(char nl)Check binary release version detail:'; hr-line
let ver = if $os == 'windows-latest' {
(do -i { ./output/nu.exe -c 'version' }) | str join
} else {
(do -i { ./output/nu -c 'version' }) | str join
}
if ($ver | str trim | is-empty) {
print $'(ansi r)Incompatible nu binary...(ansi reset)'
} else { print $ver }
$'(ansi r)Incompatible nu binary...(ansi reset)'
} else { $ver }
# ----------------------------------------------------------------------------
# Create a release archive and send it to output for the following steps
# ----------------------------------------------------------------------------
cd $dist; print $'(char nl)Creating release archive...'; hr-line
cd $dist; $'(char nl)Creating release archive...'; hr-line
if $os in [$USE_UBUNTU, 'macos-latest'] {
let files = (ls | get name)
@ -149,7 +141,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
mkdir $dest
$files | each {|it| mv $it $dest } | ignore
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
tar -czf $archive $dest
print $'archive: ---> ($archive)'; ls $archive
@ -160,7 +152,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
let releaseStem = $'($bin)-($version)-($target)'
print $'(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://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
@ -168,7 +160,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
if (get-env _EXTRA_) == 'msi' {
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
print $'(char nl)Start creating Windows msi package...'
$'(char nl)Start creating Windows msi package...'
cd $src; hr-line
# Wix need the binaries be stored in target/release/
cp -r $'($dist)/*' target/release/
@ -179,7 +171,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
} else {
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
let archive = $'($dist)/($releaseStem).zip'
7z a $archive *
print $'archive: ---> ($archive)';

View File

@ -70,12 +70,12 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.4.4
uses: actions-rust-lang/setup-rust-toolchain@v1.4.3
- name: Setup Nushell
uses: hustcer/setup-nu@v3
with:
version: 0.78.0
version: 0.72.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@ -22,9 +22,6 @@ debian/nu/
# VSCode's IDE items
.vscode/*
# JetBrains' Fleet IDE
.fleet/*
# Visual Studio Extension SourceGear Rust items
VSWorkspaceSettings.json
unstable_cargo_features.txt

View File

@ -59,12 +59,7 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
- Run Clippy on Nushell:
```shell
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err
```
or via the `toolkit.nu` command:
```shell
use toolkit.nu clippy
clippy
cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
```
- Run all tests:
@ -73,17 +68,6 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
cargo test --workspace
```
along with dataframe tests
```shell
cargo test --workspace --features=dataframe
```
or via the `toolkit.nu` command:
```shell
use toolkit.nu test
test
```
- Run all tests for a specific command
```shell
@ -95,30 +79,12 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
```shell
cargo fmt --all -- --check
```
or via the `toolkit.nu` command:
```shell
use toolkit.nu fmt
fmt --check
```
- Format the code in the project
```shell
cargo fmt --all
```
or via the `toolkit.nu` command:
```shell
use toolkit.nu fmt
fmt
```
- Set up `git` hooks to check formatting and run `clippy` before committing and pushing:
```shell
use toolkit.nu setup-git-hooks
setup-git-hooks
```
_Unfortunately, this hook isn't available on Windows._
### Debugging Tips
@ -133,90 +99,3 @@ The most comprehensive test suite we have is the `nu-test-support` crate. For te
cargo run --release -- --log-level trace --log-target file
open $"($nu.temp-path)/nu-($nu.pid).log"
```
## Git etiquette
As nushell thrives on its broad base of volunteer contributors and maintainers with different backgrounds we have a few guidelines for how we best utilize git and GitHub for our contributions. We strive to balance three goals with those recommendations:
1. The **volunteer maintainers and contributors** can easily follow the changes you propose, gauge the impact, and come to help you or make a decision.
2. **You as a contributor** can focus most of your time on improving the quality of the nushell project and contributing your expertise to the code or documentation.
3. Making sure we can trace back *why* decisions were made in the past.
This includes discarded approaches. Also we want to quickly identify regressions and fix when something broke.
### How we merge PRs
In general the maintainers **squash** all changes of your PR into a single commit when merging.
This keeps a clean enough linear history, while not forcing you to conform to a too strict style while iterating in your PR or fixing small problems. As an added benefit the commits on the `main` branch are tied to the discussion that happened in the PR through their `#1234` issue number.
> **Note**
> **Pro advice:** In some circumstances, we can agree on rebase-merging a particularly large but connected PR as a series of atomic commits onto the `main` branch to ensure we can more easily revert or bisect particular aspects.
### A good PR makes a change!
As a result of this PR-centric strategy and the general goal that the reviewers should easily understand your change, the **PR title and description matters** a great deal!
Make sure your description is **concise** but contains all relevant information and context.
This means demonstrating what changes, ideally through nushell code or output **examples**.
Furthermore links to technical documentation or instructions for folks that want to play with your change make the review process much easier.
> **Note**
> Try to follow the suggestions in our PR message template to make sure we can quickly focus on the technical merits and impact on the users.
#### A PR should limit itself to a single functional change or related set of same changes.
Mixing different changes in the same PR will make the review process much harder. A PR might get stuck on one aspect while we would actually like to land another change. Furthermore, if we are forced to revert a change, mixing and matching different aspects makes fixing bugs or regressions much harder.
Thus, please try to **separate out unrelated changes**!
**Don't** mix unrelated refactors with a potentially contested change.
Stylistic fixes and housekeeping can be bundled up into singular PRs.
#### Guidelines for the PR title
The PR title should be concise but contain everything for a contributor to know if they should help out in the review of this particular change.
**DON'T**
- `Update file/in/some/deeply/nested/path.rs`
- Why are you making this change?
- `Fix 2134`
- What has to be fixed?
- Hard to follow when not online on GitHub.
- ``Ignore `~` expansion``
- In what context should this change take effect?
- `[feature] refactor the whole parser and also make nushell indentation-sensitive, upgrade to using Cpython. Let me know what you think!`
- Be concise
- Maybe break up into smaller commits or PRs if the title already appears too long?
**DO**
- Mention the nushell feature or command that is affected.
- ``Fix URL parsing in `http get` (issue #1234)``
- You can mention the issue number if other context is there.
- In general, mention all related issues in the description to crosslink (e.g. `Fixes #1234`, `Closes #6789`)
- For internal changes mention the area or symbols affected if it helps to clarify
- ``Factor out `quote_string()` from parser to reuse in `explore` ``
### Review process / Merge conflicts
> **Note**
> Keep in mind that the maintainers are volunteers that need to allocate their attention to several different areas and active PRs. We will try to get back to you as soon as possible.
You can help us to make the review process a smooth experience:
- Testing:
- We generally review in detail after all the tests pass. Let us know if there is a problem you want to discuss to fix a test failure or forces us to accept a breaking change.
- If you fix a bug, it is highly recommended that you add a test that reproduces the original issue/panic in a minimal form.
- In general, added tests help us to understand which assumptions go into a particular addition/change.
- Try to also test corner cases where those assumptions might break. This can be more valuable than simply adding many similar tests.
- Commit history inside a PR during code review:
- Good **atomic commits** can help follow larger changes, but we are not pedantic.
- We don't shame fixup commits while you try to figure out a problem. They can help others see what you tried and what didn't work. (see our [squash policy](#how-we-merge-prs))
- During active review constant **force pushing** just to amend changes can be confusing!
- GitHub's UI presents reviewers with less options to compare diffs
- fetched branches for experimentation become invalid!
- the notification a maintainer receives has a low signal-to-noise ratio
- Git pros *can* use their judgement to rebase/squash to clean up the history *if it aids the understanding* of a larger change during review
- Merge conflicts:
- In general you should take care of resolving merge conflicts.
- Use your judgement whether to `git merge main` or to `git rebase main`
- Choose what simplifies having confidence in the conflict resolution and the review. **Merge commits in your branch are OK** in the squash model.
- Feel free to notify your reviewers or affected PR authors if your change might cause larger conflicts with another change.
- During the rollup of multiple PRs, we may choose to resolve merge conflicts and CI failures ourselves. (Allow maintainers to push to your branch to enable us to do this quickly.)

1346
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.80.0"
version = "0.77.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -37,50 +37,47 @@ members = [
"crates/nu_plugin_query",
"crates/nu_plugin_custom_values",
"crates/nu_plugin_formats",
"crates/nu-std",
"crates/nu-utils",
]
[dependencies]
chrono = { version = "0.4.23", features = ["serde"] }
crossterm = "0.26"
crossterm = "0.24.0"
ctrlc = "3.2.1"
log = "0.4"
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
nu-cli = { path = "./crates/nu-cli", version = "0.80.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.80.0" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.80.0" }
nu-command = { path = "./crates/nu-command", version = "0.80.0" }
nu-engine = { path = "./crates/nu-engine", version = "0.80.0" }
nu-json = { path = "./crates/nu-json", version = "0.80.0" }
nu-parser = { path = "./crates/nu-parser", version = "0.80.0" }
nu-path = { path = "./crates/nu-path", version = "0.80.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.80.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.80.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.80.0" }
nu-system = { path = "./crates/nu-system", version = "0.80.0" }
nu-table = { path = "./crates/nu-table", version = "0.80.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.80.0" }
nu-std = { path = "./crates/nu-std", version = "0.80.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.80.0" }
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
nu-cli = { path = "./crates/nu-cli", version = "0.77.1" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.77.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.77.1" }
nu-command = { path = "./crates/nu-command", version = "0.77.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.77.1" }
nu-json = { path = "./crates/nu-json", version = "0.77.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.77.1" }
nu-path = { path = "./crates/nu-path", version = "0.77.1" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.77.1" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.77.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.77.1" }
nu-system = { path = "./crates/nu-system", version = "0.77.1" }
nu-table = { path = "./crates/nu-table", version = "0.77.1" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.77.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.77.1" }
nu-ansi-term = "0.47.0"
reedline = { version = "0.19.1", features = ["bashisms", "sqlite"]}
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
rayon = "1.7.0"
is_executable = "1.0.1"
simplelog = "0.12.0"
time = "0.3.12"
serde_json = "1.0"
[target.'cfg(not(target_os = "windows"))'.dependencies]
# Our dependencies don't use OpenSSL on Windows
openssl = { version = "0.10.48", features = ["vendored"], optional = true }
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
signal-hook = { version = "0.3.14", default-features = false }
[target.'cfg(windows)'.build-dependencies]
winresource = "0.1"
winres = "0.1"
[target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.26", default-features = false, features = [
@ -92,14 +89,14 @@ nix = { version = "0.26", default-features = false, features = [
atty = "0.2"
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.80.0" }
tempfile = "3.5.0"
nu-test-support = { path = "./crates/nu-test-support", version = "0.77.1" }
tempfile = "3.4.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.17.0", default-features = false }
rstest = { version = "0.16.0", default-features = false }
itertools = "0.10.3"
[features]
@ -160,7 +157,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main"}
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Criterion benchmarking setup

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Copyright (c) 2019 - 2022 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -175,8 +175,7 @@ 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 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 while the [showcase repo](https://github.com/nushell/showcase) *shows* off informative blog posts that have been written about Nushell along with videos that highlight technical
topics that have been presented.
The [awesome-nu repo](https://github.com/nushell/awesome-nu#plugins) lists a variety of nu-plugins.
## Goals
@ -222,7 +221,6 @@ Please submit an issue or PR to be added to this list.
- [oh-my-posh](https://ohmyposh.dev)
- [Couchbase Shell](https://couchbase.sh)
- [virtualenv](https://github.com/pypa/virtualenv)
- [atuin](https://github.com/ellie/atuin)
## Contributing

View File

@ -22,7 +22,7 @@ fn parser_benchmarks(c: &mut Criterion) {
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),
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
BatchSize::SmallInput,
)
});
@ -31,7 +31,7 @@ fn parser_benchmarks(c: &mut Criterion) {
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),
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
BatchSize::SmallInput,
)
});

View File

@ -1,30 +1,36 @@
@echo off
echo -------------------------------------------------------------------
echo Building nushell (nu.exe) with dataframes and all the plugins
echo -------------------------------------------------------------------
echo.
@echo -------------------------------------------------------------------
@echo Building nushell (nu.exe) with dataframes and all the plugins
@echo -------------------------------------------------------------------
@echo.
echo Building nushell.exe
cargo build --features=dataframe
echo.
@echo.
call :build crates\nu_plugin_example nu_plugin_example.exe
call :build ..\..\crates\nu_plugin_gstat nu_plugin_gstat.exe
call :build ..\..\crates\nu_plugin_inc nu_plugin_inc.exe
call :build ..\..\crates\nu_plugin_query nu_plugin_query.exe
call :build ..\..\crates\nu_plugin_custom_values nu_plugin_custom_values.exe
@cd crates\nu_plugin_example
echo Building nu_plugin_example.exe
cargo build
@echo.
cd ..\..
exit /b 0
@cd ..\..\crates\nu_plugin_gstat
echo Building nu_plugin_gstat.exe
cargo build
@echo.
:build
setlocal
set "location=%~1"
set "target=%~2"
@cd ..\..\crates\nu_plugin_inc
echo Building nu_plugin_inc.exe
cargo build
@echo.
cd "%location%"
echo Building %target%
cargo build
echo.
endlocal
exit /b 0
@cd ..\..\crates\nu_plugin_query
echo Building nu_plugin_query.exe
cargo build
@echo.
@cd ..\..\crates\nu_plugin_custom_values
echo Building nu_plugin_custom_values.exe
cargo build
@echo.
@cd ..\..

View File

@ -20,6 +20,5 @@ for plugin in $plugins {
'----------------------------'
cd $'crates/($plugin)'
cargo build
cd ../../
ignore
}

View File

@ -1,6 +1,6 @@
#[cfg(windows)]
fn main() {
let mut res = winresource::WindowsResource::new();
let mut res = winres::WindowsResource::new();
res.set("ProductName", "Nushell");
res.set("FileDescription", "Nushell");
res.set("LegalCopyright", "Copyright (C) 2022");

View File

@ -5,40 +5,39 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.80.0"
version = "0.77.1"
[lib]
bench = false
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.80.0" }
rstest = { version = "0.17.0", default-features = false }
nu-test-support = { path = "../nu-test-support", version = "0.77.1" }
nu-command = { path = "../nu-command", version = "0.77.1" }
rstest = { version = "0.16.0", default-features = false }
[dependencies]
nu-command = { path = "../nu-command", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-path = { path = "../nu-path", version = "0.80.0" }
nu-parser = { path = "../nu-parser", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.80.0" }
nu-color-config = { path = "../nu-color-config", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.77.1" }
nu-path = { path = "../nu-path", version = "0.77.1" }
nu-parser = { path = "../nu-parser", version = "0.77.1" }
nu-protocol = { path = "../nu-protocol", version = "0.77.1" }
nu-utils = { path = "../nu-utils", version = "0.77.1" }
nu-color-config = { path = "../nu-color-config", version = "0.77.1" }
nu-ansi-term = "0.47.0"
reedline = { version = "0.19.1", features = ["bashisms", "sqlite"]}
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
atty = "0.2.14"
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
crossterm = "0.26"
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.7.0", features = ["fancy-no-backtrace"] }
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
percent-encoding = "2"
sysinfo = "0.28.2"
thiserror = "1.0.31"
unicode-segmentation = "1.10.0"
[features]
plugin = []

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Copyright (c) 2019 - 2022 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,9 +1,9 @@
use crate::util::report_error;
use log::info;
use miette::Result;
use nu_engine::{convert_env_values, eval_block};
use nu_parser::parse;
use nu_protocol::engine::Stack;
use nu_protocol::report_error;
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
PipelineData, Spanned, Value,
@ -34,9 +34,9 @@ pub fn evaluate_commands(
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err);
let (output, err) = parse(&mut working_set, None, commands.item.as_bytes(), false, &[]);
if let Some(err) = err {
report_error(&working_set, &err);
std::process::exit(1);
}

View File

@ -1,138 +0,0 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::IntoPipelineData;
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)]
pub struct Commandline;
impl Command for Commandline {
fn name(&self) -> &str {
"commandline"
}
fn signature(&self) -> Signature {
Signature::build("commandline")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.switch(
"cursor",
"Set or get the current cursor position",
Some('c'),
)
.switch(
"append",
"appends the string to the end of the buffer",
Some('a'),
)
.switch(
"insert",
"inserts the string into the buffer at the cursor position",
Some('i'),
)
.switch(
"replace",
"replaces the current contents of the buffer (default)",
Some('r'),
)
.optional(
"cmd",
SyntaxShape::String,
"the string to perform the operation with",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"View or modify the current command line input buffer."
}
fn search_terms(&self) -> Vec<&str> {
vec!["repl", "interactive"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
let mut buffer = engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex");
let mut cursor_pos = engine_state
.repl_cursor_pos
.lock()
.expect("repl cursor pos mutex");
if call.has_flag("cursor") {
let cmd_str = cmd.as_string()?;
match cmd_str.parse::<i64>() {
Ok(n) => {
*cursor_pos = if n <= 0 {
0usize
} else {
buffer
.grapheme_indices(true)
.map(|(i, _c)| i)
.nth(n as usize)
.unwrap_or(buffer.len())
}
}
Err(_) => {
return Err(ShellError::CantConvert {
to_type: "int".to_string(),
from_type: "string".to_string(),
span: cmd.span()?,
help: Some(format!(
r#"string "{cmd_str}" does not represent a valid integer"#
)),
})
}
}
} else if call.has_flag("append") {
buffer.push_str(&cmd.as_string()?);
} else if call.has_flag("insert") {
let cmd_str = cmd.as_string()?;
buffer.insert_str(*cursor_pos, &cmd_str);
*cursor_pos += cmd_str.len();
} else {
*buffer = cmd.as_string()?;
*cursor_pos = buffer.len();
}
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
} else {
let buffer = engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex");
if call.has_flag("cursor") {
let cursor_pos = engine_state
.repl_cursor_pos
.lock()
.expect("repl cursor pos mutex");
let char_pos = buffer
.grapheme_indices(true)
.chain(std::iter::once((buffer.len(), "")))
.position(|(i, _c)| i == *cursor_pos)
.expect("Cursor position isn't on a grapheme boundary");
Ok(Value::String {
val: char_pos.to_string(),
span: call.head,
}
.into_pipeline_data())
} else {
Ok(Value::String {
val: buffer.to_string(),
span: call.head,
}
.into_pipeline_data())
}
}
}
}

View File

@ -1,33 +0,0 @@
use nu_protocol::engine::{EngineState, StateWorkingSet};
use crate::commands::*;
pub fn add_cli_context(mut engine_state: EngineState) -> EngineState {
let delta = {
let mut working_set = StateWorkingSet::new(&engine_state);
macro_rules! bind_command {
( $( $command:expr ),* $(,)? ) => {
$( working_set.add_decl(Box::new($command)); )*
};
}
bind_command! {
Commandline,
History,
HistorySession,
Keybindings,
KeybindingsDefault,
KeybindingsList,
KeybindingsListen,
};
working_set.render()
};
if let Err(err) = engine_state.merge_delta(delta) {
eprintln!("Error creating default context: {err:?}");
}
engine_state
}

View File

@ -88,7 +88,7 @@ impl CommandCompletion {
let filter_predicate = |command: &[u8]| match_algorithm.matches_u8(command, partial);
let mut results = working_set
let results = working_set
.find_commands_by_predicate(filter_predicate)
.into_iter()
.map(move |x| Suggestion {
@ -97,8 +97,20 @@ impl CommandCompletion {
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
})
.collect::<Vec<_>>();
});
let results_aliases = working_set
.find_aliases_by_predicate(filter_predicate)
.into_iter()
.map(move |x| Suggestion {
value: String::from_utf8_lossy(&x).to_string(),
description: None,
extra: None,
span: reedline::Span::new(span.start - offset, span.end - offset),
append_whitespace: true,
});
let mut results = results.chain(results_aliases).collect::<Vec<_>>();
let partial = working_set.get_span_contents(span);
let partial = String::from_utf8_lossy(partial).to_string();
@ -157,7 +169,7 @@ impl Completer for CommandCompletion {
.take_while(|x| {
matches!(
x.1,
FlatShape::InternalCall(_)
FlatShape::InternalCall
| FlatShape::External
| FlatShape::ExternalArg
| FlatShape::Literal
@ -185,7 +197,7 @@ impl Completer for CommandCompletion {
let config = working_set.get_config();
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall)
|| ((span.end - span.start) == 0)
|| is_passthrough_command(working_set.delta.get_file_contents())
{
@ -299,7 +311,7 @@ mod command_completions_tests {
let delta = {
let mut working_set = StateWorkingSet::new(&engine_state);
let _ = working_set.add_file("child.nu".into(), input);
working_set.add_file("child.nu".into(), input);
working_set.render()
};

View File

@ -113,13 +113,14 @@ impl NuCompleter {
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
let mut working_set = StateWorkingSet::new(&self.engine_state);
let offset = working_set.next_span_start();
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
let initial_line = line.to_string();
let mut line = line.to_string();
line.insert(pos, 'a');
let alias_total_offset: usize = alias_offset.iter().sum();
new_line.insert(alias_total_offset + pos, b'a');
let pos = offset + pos;
let config = self.engine_state.get_config();
let output = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
for pipeline in output.pipelines.into_iter() {
for pipeline_element in pipeline.elements {
@ -130,6 +131,7 @@ impl NuCompleter {
| 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() {
@ -152,17 +154,24 @@ impl NuCompleter {
}
// Complete based on the last span
if pos >= flat.0.start && pos < flat.0.end {
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
most_left_variable(flat_idx, &working_set, flattened.clone());
// Create a new span
let new_span = Span::new(flat.0.start, flat.0.end - 1);
let new_span = if flat_idx == 0 {
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
} else {
Span::new(
flat.0.start - span_offset,
flat.0.end - 1 - span_offset,
)
};
// Parses the prefix. Completion should look up to the cursor position, not after.
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
let index = pos - flat.0.start;
let index = pos - (flat.0.start - span_offset);
prefix.drain(index..);
// Variables completion
@ -382,6 +391,85 @@ impl ReedlineCompleter for NuCompleter {
}
}
type MatchedAlias = Vec<(Vec<u8>, Vec<u8>)>;
// Handler the completion when giving lines contains at least one alias. (e.g: `g checkout`)
// that `g` is an alias of `git`
fn try_find_alias(line: &[u8], working_set: &StateWorkingSet) -> (Vec<u8>, Vec<usize>) {
// An vector represents the offsets of alias
// e.g: the offset is 2 for the alias `g` of `git`
let mut alias_offset = vec![];
let mut output = vec![];
if let Some(matched_alias) = search_alias(line, working_set) {
let mut lens = matched_alias.len();
for (input_vec, line_vec) in matched_alias {
alias_offset.push(line_vec.len() - input_vec.len());
output.extend(line_vec);
if lens > 1 {
output.push(b' ');
lens -= 1;
}
}
if !line.is_empty() {
let last = line.last().expect("input is empty");
if last == &b' ' {
output.push(b' ');
}
}
} else {
output = line.to_vec();
}
(output, alias_offset)
}
fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAlias> {
let mut vec_names = vec![];
let mut vec_alias = vec![];
let mut pos = 0;
let mut is_alias = false;
for (index, character) in input.iter().enumerate() {
if *character == b' ' {
let range = &input[pos..index];
vec_names.push(range.to_owned());
pos = index + 1;
}
}
// Push the rest to names vector.
if pos < input.len() {
vec_names.push(input[pos..].to_owned());
}
for name in &vec_names {
if let Some(alias_id) = working_set.find_alias(&name[..]) {
let alias_span = working_set.get_alias(alias_id);
let mut span_vec = vec![];
is_alias = true;
for alias in alias_span {
let name = working_set.get_span_contents(*alias);
if !name.is_empty() {
span_vec.push(name);
}
}
// Join span of vector together for complex alias, e.g: `f` is an alias for `git remote -v`
let full_aliases = span_vec.join(&[b' '][..]);
vec_alias.push(full_aliases);
} else {
vec_alias.push(name.to_owned());
}
}
if is_alias {
// Zip names and alias vectors, the original inputs and its aliases mapping.
// e.g:(['g'], ['g','i','t'])
let output = vec_names.into_iter().zip(vec_alias).collect();
Some(output)
} else {
None
}
}
// reads the most left variable returning it's name (e.g: $myvar)
// and the depth (a.b.c)
fn most_left_variable(
@ -402,7 +490,7 @@ fn most_left_variable(
let result = working_set.get_span_contents(item.0).to_vec();
match item.1 {
FlatShape::Variable(_) => {
FlatShape::Variable => {
variables_found.push(result);
found_var = true;

View File

@ -6,7 +6,7 @@ use nu_protocol::{
PipelineData, Span, Type, Value,
};
use reedline::Suggestion;
use std::{collections::HashMap, sync::Arc};
use std::sync::Arc;
use super::completer::map_value_completions;
@ -66,7 +66,7 @@ impl Completer for CustomCompletion {
],
redirect_stdout: true,
redirect_stderr: true,
parser_info: HashMap::new(),
parser_info: vec![],
},
PipelineData::empty(),
);

View File

@ -1,5 +1,5 @@
use crate::completions::{Completer, CompletionOptions};
use nu_engine::{column::get_columns, eval_variable};
use nu_engine::eval_variable;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Span, Value,
@ -179,7 +179,11 @@ impl Completer for VariableCompletion {
let mut removed_overlays = vec![];
// Working set scope vars
for scope_frame in working_set.delta.scope.iter().rev() {
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
for overlay_frame in scope_frame
.active_overlays(&mut removed_overlays)
.iter()
.rev()
{
for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
@ -200,7 +204,12 @@ impl Completer for VariableCompletion {
// Permanent state vars
// for scope in &self.engine_state.scope {
for overlay_frame in self.engine_state.active_overlays(&removed_overlays).rev() {
for overlay_frame in self
.engine_state
.active_overlays(&removed_overlays)
.iter()
.rev()
{
for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
@ -267,19 +276,7 @@ fn nested_suggestions(
output
}
Value::List { vals, span: _ } => {
for column_name in get_columns(vals.as_slice()) {
output.push(Suggestion {
value: column_name,
description: None,
extra: None,
span: current_span,
append_whitespace: false,
});
}
output
}
_ => output,
}
}
@ -307,38 +304,6 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
span: Span::unknown(),
};
}
Value::LazyRecord { val, span: _ } => {
for col in val.column_names() {
if col.as_bytes().to_vec() == next_sublevel {
return recursive_value(
val.get_column_value(col).unwrap_or_default(),
sublevels.into_iter().skip(1).collect(),
);
}
}
// Current sublevel value not found
return Value::Nothing {
span: Span::unknown(),
};
}
Value::List { vals, span } => {
for col in get_columns(vals.as_slice()) {
if col.as_bytes().to_vec() == next_sublevel {
return recursive_value(
Value::List { vals, span }
.get_data_by_key(&col)
.unwrap_or_default(),
sublevels.into_iter().skip(1).collect(),
);
}
}
// Current sublevel value not found
return Value::Nothing {
span: Span::unknown(),
};
}
_ => return val,
}
}

View File

@ -1,11 +1,12 @@
use crate::util::eval_source;
use crate::util::{eval_source, report_error};
#[cfg(feature = "plugin")]
use nu_parser::ParseError;
#[cfg(feature = "plugin")]
use nu_path::canonicalize_with;
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
use nu_protocol::report_error;
use nu_protocol::{HistoryFileFormat, PipelineData};
#[cfg(feature = "plugin")]
use nu_protocol::{ParseError, Spanned};
use nu_protocol::Spanned;
use nu_protocol::{HistoryFileFormat, PipelineData};
#[cfg(feature = "plugin")]
use nu_utils::utils::perf;
use std::path::PathBuf;

View File

@ -1,11 +1,10 @@
use crate::util::eval_source;
use crate::util::{eval_source, report_error};
use log::info;
use log::trace;
use miette::{IntoDiagnostic, Result};
use nu_engine::{convert_env_values, current_dir};
use nu_parser::parse;
use nu_path::canonicalize_with;
use nu_protocol::report_error;
use nu_protocol::{
ast::Call,
engine::{EngineState, Stack, StateWorkingSet},
@ -93,14 +92,10 @@ pub fn evaluate_file(
"FILE_PWD".to_string(),
Value::string(parent.to_string_lossy(), Span::unknown()),
);
stack.add_env_var(
"CURRENT_FILE".to_string(),
Value::string(file_path.to_string_lossy(), Span::unknown()),
);
let mut working_set = StateWorkingSet::new(engine_state);
trace!("parsing file: {}", file_path_str);
let _ = parse(&mut working_set, Some(file_path_str), &file, false);
let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]);
if working_set.find_decl(b"main", &Type::Any).is_some() {
let args = format!("main {}", args.join(" "));

View File

@ -1,7 +1,6 @@
mod commands;
mod completions;
mod config_files;
mod eval_cmds;
mod eval_file;
mod menus;
mod nu_highlight;
@ -14,19 +13,18 @@ mod syntax_highlight;
mod util;
mod validation;
pub use commands::add_cli_context;
pub use commands::evaluate_commands;
pub use completions::{FileCompletion, NuCompleter};
pub use config_files::eval_config_contents;
pub use eval_cmds::evaluate_commands;
pub use eval_file::evaluate_file;
pub use menus::{DescriptionMenu, NuHelpCompleter};
pub use nu_command::util::get_init_cwd;
pub use nu_highlight::NuHighlight;
pub use print::Print;
pub use prompt::NushellPrompt;
pub use repl::evaluate_repl;
pub use repl::{eval_env_change_hook, eval_hook};
pub use syntax_highlight::NuHighlighter;
pub use util::{eval_source, gather_parent_env_vars};
pub use util::{eval_source, gather_parent_env_vars, get_init_cwd, report_error, report_error_new};
pub use validation::NuValidator;
#[cfg(feature = "plugin")]

View File

@ -57,9 +57,7 @@ impl NuHelpCompleter {
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(sig, |v| {
v.into_string_parsable(", ", &self.0.config)
}))
long_desc.push_str(&get_flags_section(sig))
}
if !sig.required_positional.is_empty()
@ -71,18 +69,10 @@ impl NuHelpCompleter {
let _ = write!(long_desc, " {}: {}\r\n", positional.name, positional.desc);
}
for positional in &sig.optional_positional {
let opt_suffix = if let Some(value) = &positional.default_value {
format!(
" (optional, default: {})",
&value.into_string_parsable(", ", &self.0.config),
)
} else {
(" (optional)").to_string()
};
let _ = write!(
long_desc,
" (optional) {}: {}{}\r\n",
positional.name, positional.desc, opt_suffix
" (optional) {}: {}\r\n",
positional.name, positional.desc
);
}

View File

@ -106,13 +106,11 @@ impl Prompt for NushellPrompt {
prompt_string.replace('\n', "\r\n").into()
} else {
let default = DefaultPrompt::default();
let prompt = default
default
.render_prompt_left()
.to_string()
.replace('\n', "\r\n")
+ " ";
prompt.into()
.into()
}
}

View File

@ -1,7 +1,7 @@
use crate::util::report_error;
use crate::NushellPrompt;
use log::trace;
use nu_engine::eval_subexpression;
use nu_protocol::report_error;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Config, PipelineData, Value,

View File

@ -97,11 +97,12 @@ pub(crate) fn add_menus(
{
let (block, _) = {
let mut working_set = StateWorkingSet::new(&engine_state);
let output = parse(
let (output, _) = parse(
&mut working_set,
Some(name), // format!("entry #{}", entry_num)
definition.as_bytes(),
true,
&[],
);
(output, working_set.render())
@ -625,12 +626,9 @@ fn add_parsed_keybinding(
"shift" => KeyModifiers::SHIFT,
"alt" => KeyModifiers::ALT,
"none" => KeyModifiers::NONE,
"shift_alt" | "alt_shift" => KeyModifiers::SHIFT | KeyModifiers::ALT,
"control_shift" | "shift_control" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
"control_alt" | "alt_control" => KeyModifiers::CONTROL | KeyModifiers::ALT,
"control_alt_shift" | "control_shift_alt" => {
KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT
}
"control | shift" => KeyModifiers::CONTROL | KeyModifiers::SHIFT,
"control | alt" => KeyModifiers::CONTROL | KeyModifiers::ALT,
"control | alt | shift" => KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT,
_ => {
return Err(ShellError::UnsupportedConfigValue(
"CONTROL, SHIFT, ALT or NONE".to_string(),

View File

@ -2,22 +2,21 @@ use crate::{
completions::NuCompleter,
prompt_update,
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
util::eval_source,
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
NuHighlighter, NuValidator, NushellPrompt,
};
use crossterm::cursor::SetCursorStyle;
use crossterm::cursor::CursorShape;
use log::{trace, warn};
use miette::{IntoDiagnostic, Result};
use nu_color_config::StyleComputer;
use nu_command::hook::eval_hook;
use nu_command::util::get_guaranteed_cwd;
use nu_engine::convert_env_values;
use nu_parser::{lex, trim_quotes_str};
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{
ast::PathMember,
config::NuCursorShape,
engine::{EngineState, Stack, StateWorkingSet},
report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
Value,
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
Spanned, Type, Value, VarId,
};
use nu_utils::utils::perf;
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
@ -43,10 +42,8 @@ pub fn evaluate_repl(
stack: &mut Stack,
nushell_path: &str,
prerun_command: Option<Spanned<String>>,
load_std_lib: Option<Spanned<String>>,
entire_start_time: Instant,
) -> Result<()> {
use nu_command::hook;
use reedline::{FileBackedHistory, Reedline, Signal};
let use_color = engine_state.get_config().use_ansi_coloring;
@ -106,20 +103,6 @@ pub fn evaluate_repl(
);
let config = engine_state.get_config();
if config.bracketed_paste {
// try to enable bracketed paste
// It doesn't work on windows system: https://github.com/crossterm-rs/crossterm/issues/737
#[cfg(not(target_os = "windows"))]
let _ = line_editor.enable_bracketed_paste();
}
// Setup history_isolation aka "history per session"
let history_isolation = config.history_isolation;
let history_session_id = if history_isolation {
Reedline::create_history_session_id()
} else {
None
};
start_time = std::time::Instant::now();
let history_path = crate::config_files::get_history_path(
@ -139,9 +122,7 @@ pub fn evaluate_repl(
SqliteBackedHistory::with_file(history_path.to_path_buf()).into_diagnostic()?,
),
};
line_editor = line_editor
.with_history_session_id(history_session_id)
.with_history(history);
line_editor = line_editor.with_history(history);
};
perf(
"setup history",
@ -154,8 +135,19 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
let sys = sysinfo::System::new();
let show_banner = config.show_banner;
let use_ansi = config.use_ansi_coloring;
if show_banner {
let banner = get_banner(engine_state, stack);
if use_ansi {
println!("{banner}");
} else {
println!("{}", nu_utils::strip_ansi_string_likely(banner));
}
}
perf(
"get sysinfo",
"get sysinfo/show banner",
start_time,
file!(),
line!(),
@ -175,19 +167,6 @@ pub fn evaluate_repl(
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
}
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
if load_std_lib.is_none() && engine_state.get_config().show_banner {
eval_source(
engine_state,
stack,
r#"use std banner; banner"#.as_bytes(),
"show_banner",
PipelineData::empty(),
false,
);
}
loop {
let loop_start_time = std::time::Instant::now();
@ -421,7 +400,7 @@ pub fn evaluate_repl(
// fire the "env_change" hook
let config = engine_state.get_config();
if let Err(error) =
hook::eval_env_change_hook(config.hooks.env_change.clone(), engine_state, stack)
eval_env_change_hook(config.hooks.env_change.clone(), engine_state, stack)
{
report_error_new(engine_state, &error)
}
@ -448,6 +427,16 @@ pub fn evaluate_repl(
entry_num += 1;
if entry_num == 1 {
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
if show_banner {
println!(
"Startup Time: {}",
format_duration(engine_state.get_startup_time())
);
}
}
start_time = std::time::Instant::now();
let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration;
@ -470,35 +459,20 @@ pub fn evaluate_repl(
.into_diagnostic()?; // todo: don't stop repl if error here?
}
// Right before we start running the code the user gave us, fire the `pre_execution`
// hook
if let Some(hook) = config.hooks.pre_execution.clone() {
// Set the REPL buffer to the current command for the "pre_execution" hook
let mut repl_buffer = engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex");
*repl_buffer = s.to_string();
drop(repl_buffer);
engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex")
.replace(line_editor.current_buffer_contents().to_string());
// Right before we start running the code the user gave us,
// fire the "pre_execution" hook
if let Some(hook) = config.hooks.pre_execution.clone() {
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err);
}
}
let mut repl_cursor = engine_state
.repl_cursor_pos
.lock()
.expect("repl cursor pos mutex");
*repl_cursor = line_editor.current_insertion_point();
drop(repl_cursor);
let mut repl_buffer = engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex");
*repl_buffer = line_editor.current_buffer_contents().to_string();
drop(repl_buffer);
if shell_integration {
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
}
@ -654,23 +628,23 @@ pub fn evaluate_repl(
run_ansi_sequence(RESET_APPLICATION_MODE)?;
}
let mut repl_buffer = engine_state
.repl_buffer_state
let mut ops = engine_state
.repl_operation_queue
.lock()
.expect("repl buffer state mutex");
let mut repl_cursor_pos = engine_state
.repl_cursor_pos
.lock()
.expect("repl cursor pos mutex");
line_editor.run_edit_commands(&[
EditCommand::Clear,
EditCommand::InsertString(repl_buffer.to_string()),
EditCommand::MoveToPosition(*repl_cursor_pos),
]);
*repl_buffer = "".to_string();
drop(repl_buffer);
*repl_cursor_pos = 0;
drop(repl_cursor_pos);
.expect("repl op queue mutex");
while let Some(op) = ops.pop_front() {
match op {
ReplOperation::Append(s) => line_editor.run_edit_commands(&[
EditCommand::MoveToEnd,
EditCommand::InsertString(s),
]),
ReplOperation::Insert(s) => {
line_editor.run_edit_commands(&[EditCommand::InsertString(s)])
}
ReplOperation::Replace(s) => line_editor
.run_edit_commands(&[EditCommand::Clear, EditCommand::InsertString(s)]),
}
}
}
Ok(Signal::CtrlC) => {
// `Reedline` clears the line content. New prompt is shown
@ -722,17 +696,112 @@ pub fn evaluate_repl(
Ok(())
}
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle {
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> CursorShape {
match shape {
NuCursorShape::Block => SetCursorStyle::SteadyBlock,
NuCursorShape::UnderScore => SetCursorStyle::SteadyUnderScore,
NuCursorShape::Line => SetCursorStyle::SteadyBar,
NuCursorShape::BlinkBlock => SetCursorStyle::BlinkingBlock,
NuCursorShape::BlinkUnderScore => SetCursorStyle::BlinkingUnderScore,
NuCursorShape::BlinkLine => SetCursorStyle::BlinkingBar,
NuCursorShape::Block => CursorShape::Block,
NuCursorShape::UnderScore => CursorShape::UnderScore,
NuCursorShape::Line => CursorShape::Line,
}
}
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
let age = match eval_string_with_input(
engine_state,
stack,
None,
"(date now) - ('2019-05-10 09:59:12-0700' | into datetime)",
) {
Ok(Value::Duration { val, .. }) => format_duration(val),
_ => "".to_string(),
};
let banner = format!(
r#"{} __ ,
{} .--()°'.' {}Welcome to {}Nushell{},
{}'|, . ,' {}based on the {}nu{} language,
{} !_-(_\ {}where all data is structured!
Please join our {}Discord{} community at {}https://discord.gg/NtAbbGn{}
Our {}GitHub{} repository is at {}https://github.com/nushell/nushell{}
Our {}Documentation{} is located at {}https://nushell.sh{}
{}Tweet{} us at {}@nu_shell{}
Learn how to remove this at: {}https://nushell.sh/book/configuration.html#remove-welcome-message{}
It's been this long since {}Nushell{}'s first commit:
{}{}
"#,
"\x1b[32m", //start line 1 green
"\x1b[32m", //start line 2
"\x1b[0m", //before welcome
"\x1b[32m", //before nushell
"\x1b[0m", //after nushell
"\x1b[32m", //start line 3
"\x1b[0m", //before based
"\x1b[32m", //before nu
"\x1b[0m", //after nu
"\x1b[32m", //start line 4
"\x1b[0m", //before where
"\x1b[35m", //before Discord purple
"\x1b[0m", //after Discord
"\x1b[35m", //before Discord URL
"\x1b[0m", //after Discord URL
"\x1b[1;32m", //before GitHub green_bold
"\x1b[0m", //after GitHub
"\x1b[1;32m", //before GitHub URL
"\x1b[0m", //after GitHub URL
"\x1b[32m", //before Documentation
"\x1b[0m", //after Documentation
"\x1b[32m", //before Documentation URL
"\x1b[0m", //after Documentation URL
"\x1b[36m", //before Tweet blue
"\x1b[0m", //after Tweet
"\x1b[1;36m", //before @nu_shell cyan_bold
"\x1b[0m", //after @nu_shell
"\x1b[32m", //before Welcome Message
"\x1b[0m", //after Welcome Message
"\x1b[32m", //before Nushell
"\x1b[0m", //after Nushell
age,
"\x1b[0m", //after banner disable
);
banner
}
// Taken from Nana's simple_eval
/// Evaluate a block of Nu code, optionally with input.
/// For example, source="$in * 2" will multiply the value in input by 2.
pub fn eval_string_with_input(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<Value>,
source: &str,
) -> Result<Value, ShellError> {
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let (output, _) = parse(&mut working_set, None, source.as_bytes(), false, &[]);
(output, working_set.render())
};
engine_state.merge_delta(delta)?;
let input_as_pipeline_data = match input {
Some(input) => PipelineData::Value(input, None),
None => PipelineData::empty(),
};
eval_block(
engine_state,
stack,
&block,
input_as_pipeline_data,
false,
true,
)
.map(|x| x.into_value(Span::unknown()))
}
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
let exit_code = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
@ -741,6 +810,341 @@ pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) ->
format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
}
pub fn eval_env_change_hook(
env_change_hook: Option<Value>,
engine_state: &mut EngineState,
stack: &mut Stack,
) -> Result<(), ShellError> {
if let Some(hook) = env_change_hook {
match hook {
Value::Record {
cols: env_names,
vals: hook_values,
..
} => {
for (env_name, hook_value) in env_names.iter().zip(hook_values.iter()) {
let before = engine_state
.previous_env_vars
.get(env_name)
.cloned()
.unwrap_or_default();
let after = stack
.get_env_var(engine_state, env_name)
.unwrap_or_default();
if before != after {
eval_hook(
engine_state,
stack,
None,
vec![("$before".into(), before), ("$after".into(), after.clone())],
hook_value,
)?;
engine_state
.previous_env_vars
.insert(env_name.to_string(), after);
}
}
}
x => {
return Err(ShellError::TypeMismatch {
err_message: "record for the 'env_change' hook".to_string(),
span: x.span()?,
});
}
}
}
Ok(())
}
pub fn eval_hook(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
value: &Value,
) -> Result<PipelineData, ShellError> {
let value_span = value.span()?;
// Hooks can optionally be a record in this form:
// {
// condition: {|before, after| ... } # block that evaluates to true/false
// code: # block or a string
// }
// The condition block will be run to check whether the main hook (in `code`) should be run.
// If it returns true (the default if a condition block is not specified), the hook should be run.
let condition_path = PathMember::String {
val: "condition".to_string(),
span: value_span,
};
let mut output = PipelineData::empty();
let code_path = PathMember::String {
val: "code".to_string(),
span: value_span,
};
match value {
Value::List { vals, .. } => {
for val in vals {
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
}
}
Value::Record { .. } => {
let do_run_hook = if let Ok(condition) =
value
.clone()
.follow_cell_path(&[condition_path], false, false)
{
match condition {
Value::Block {
val: block_id,
span: block_span,
..
}
| Value::Closure {
val: block_id,
span: block_span,
..
} => {
match run_hook_block(
engine_state,
stack,
block_id,
None,
arguments.clone(),
block_span,
) {
Ok(pipeline_data) => {
if let PipelineData::Value(Value::Bool { val, .. }, ..) =
pipeline_data
{
val
} else {
return Err(ShellError::UnsupportedConfigValue(
"boolean output".to_string(),
"other PipelineData variant".to_string(),
block_span,
));
}
}
Err(err) => {
return Err(err);
}
}
}
other => {
return Err(ShellError::UnsupportedConfigValue(
"block".to_string(),
format!("{}", other.get_type()),
other.span()?,
));
}
}
} else {
// always run the hook
true
};
if do_run_hook {
match value.clone().follow_cell_path(&[code_path], false, false)? {
Value::String {
val,
span: source_span,
} => {
let (block, delta, vars) = {
let mut working_set = StateWorkingSet::new(engine_state);
let mut vars: Vec<(VarId, Value)> = vec![];
for (name, val) in arguments {
let var_id = working_set.add_variable(
name.as_bytes().to_vec(),
val.span()?,
Type::Any,
false,
);
vars.push((var_id, val));
}
let (output, err) =
parse(&mut working_set, Some("hook"), val.as_bytes(), false, &[]);
if let Some(err) = err {
report_error(&working_set, &err);
return Err(ShellError::UnsupportedConfigValue(
"valid source code".into(),
"source code with syntax errors".into(),
source_span,
));
}
(output, working_set.render(), vars)
};
engine_state.merge_delta(delta)?;
let input = PipelineData::empty();
let var_ids: Vec<VarId> = vars
.into_iter()
.map(|(var_id, val)| {
stack.add_var(var_id, val);
var_id
})
.collect();
match eval_block(engine_state, stack, &block, input, false, false) {
Ok(pipeline_data) => {
output = pipeline_data;
}
Err(err) => {
report_error_new(engine_state, &err);
}
}
for var_id in var_ids.iter() {
stack.vars.remove(var_id);
}
}
Value::Block {
val: block_id,
span: block_span,
..
} => {
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
other => {
return Err(ShellError::UnsupportedConfigValue(
"block or string".to_string(),
format!("{}", other.get_type()),
other.span()?,
));
}
}
}
}
Value::Block {
val: block_id,
span: block_span,
..
} => {
output = run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?;
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
output = run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?;
}
other => {
return Err(ShellError::UnsupportedConfigValue(
"block, record, or list of records".into(),
format!("{}", other.get_type()),
other.span()?,
));
}
}
let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?;
Ok(output)
}
fn run_hook_block(
engine_state: &EngineState,
stack: &mut Stack,
block_id: BlockId,
optional_input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
span: Span,
) -> Result<PipelineData, ShellError> {
let block = engine_state.get_block(block_id);
let input = optional_input.unwrap_or_else(PipelineData::empty);
let mut callee_stack = stack.gather_captures(&block.captures);
for (idx, PositionalArg { var_id, .. }) in
block.signature.required_positional.iter().enumerate()
{
if let Some(var_id) = var_id {
if let Some(arg) = arguments.get(idx) {
callee_stack.add_var(*var_id, arg.1.clone())
} else {
return Err(ShellError::IncompatibleParametersSingle {
msg: "This hook block has too many parameters".into(),
span,
});
}
}
}
let pipeline_data =
eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?;
if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
return Err(*error);
}
// If all went fine, preserve the environment of the called block
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(pipeline_data)
}
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
ShellError::GenericError(

View File

@ -18,7 +18,10 @@ impl Highlighter for NuHighlighter {
trace!("highlighting: {}", line);
let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false);
let block = {
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
block
};
let (shapes, global_span_offset) = {
let shapes = flatten_block(&working_set, &block);
(shapes, self.engine_state.next_span_start())
@ -76,28 +79,29 @@ impl Highlighter for NuHighlighter {
}};
}
let mut add_colored_token = |shape: &FlatShape, text: String| {
output.push((get_shape_color(shape.to_string(), &self.config), text));
};
macro_rules! add_colored_token {
($shape:expr, $text:expr) => {
output.push((get_shape_color($shape.to_string(), &self.config), $text))
};
}
match shape.1 {
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
FlatShape::Binary => add_colored_token(&shape.1, next_token),
FlatShape::Bool => add_colored_token(&shape.1, next_token),
FlatShape::Int => add_colored_token(&shape.1, next_token),
FlatShape::Float => add_colored_token(&shape.1, next_token),
FlatShape::Range => add_colored_token(&shape.1, next_token),
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
FlatShape::External => add_colored_token(&shape.1, next_token),
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
FlatShape::Literal => add_colored_token(&shape.1, next_token),
FlatShape::Operator => add_colored_token(&shape.1, next_token),
FlatShape::Signature => add_colored_token(&shape.1, next_token),
FlatShape::String => add_colored_token(&shape.1, next_token),
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
FlatShape::Binary => add_colored_token!(shape.1, next_token),
FlatShape::Bool => add_colored_token!(shape.1, next_token),
FlatShape::Int => add_colored_token!(shape.1, next_token),
FlatShape::Float => add_colored_token!(shape.1, next_token),
FlatShape::Range => add_colored_token!(shape.1, next_token),
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
FlatShape::External => add_colored_token!(shape.1, next_token),
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
FlatShape::Literal => add_colored_token!(shape.1, next_token),
FlatShape::Operator => add_colored_token!(shape.1, next_token),
FlatShape::Signature => add_colored_token!(shape.1, next_token),
FlatShape::String => add_colored_token!(shape.1, next_token),
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
FlatShape::List => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
@ -111,23 +115,17 @@ impl Highlighter for NuHighlighter {
FlatShape::Block => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Closure => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
FlatShape::Directory => add_colored_token(&shape.1, next_token),
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
add_colored_token(&shape.1, next_token)
}
FlatShape::Flag => add_colored_token(&shape.1, next_token),
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
FlatShape::And => add_colored_token(&shape.1, next_token),
FlatShape::Or => add_colored_token(&shape.1, next_token),
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
FlatShape::Directory => add_colored_token!(shape.1, next_token),
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
FlatShape::Variable => add_colored_token!(shape.1, next_token),
FlatShape::Flag => add_colored_token!(shape.1, next_token),
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
FlatShape::And => add_colored_token!(shape.1, next_token),
FlatShape::Or => add_colored_token!(shape.1, next_token),
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
}
last_seen_span = shape.0.end;
}
@ -310,8 +308,6 @@ fn find_matching_block_end_in_expr(
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
Expr::Signature(_) => None,
Expr::MatchPattern(_) => None,
Expr::MatchBlock(_) => None,
Expr::Nothing => None,
Expr::Garbage => None,

View File

@ -1,16 +1,16 @@
use nu_command::hook::eval_hook;
use crate::repl::eval_hook;
use nu_engine::{eval_block, eval_block_with_early_return};
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
use nu_protocol::engine::StateWorkingSet;
use nu_protocol::CliError;
use nu_protocol::{
engine::{EngineState, Stack},
print_if_stream, PipelineData, ShellError, Span, Value,
};
use nu_protocol::{report_error, report_error_new};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
use nu_utils::utils::perf;
use std::path::Path;
use std::path::{Path, PathBuf};
// This will collect environment variables from std::env and adds them to a stack.
//
@ -113,8 +113,7 @@ fn gather_env_vars(
span,
}) = parts.get(0)
{
let mut working_set = StateWorkingSet::new(engine_state);
let bytes = working_set.get_span_contents(*span);
let bytes = engine_state.get_span_contents(span);
if bytes.len() < 2 {
report_capture_error(
@ -126,12 +125,9 @@ fn gather_env_vars(
continue;
}
let (bytes, err) = unescape_unquote_string(bytes, *span);
if let Some(err) = err {
working_set.error(err);
}
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
if working_set.parse_errors.first().is_some() {
if parse_error.is_some() {
report_capture_error(
engine_state,
&String::from_utf8_lossy(contents),
@ -157,8 +153,7 @@ fn gather_env_vars(
span,
}) = parts.get(2)
{
let mut working_set = StateWorkingSet::new(engine_state);
let bytes = working_set.get_span_contents(*span);
let bytes = engine_state.get_span_contents(span);
if bytes.len() < 2 {
report_capture_error(
@ -170,12 +165,9 @@ fn gather_env_vars(
continue;
}
let (bytes, err) = unescape_unquote_string(bytes, *span);
if let Some(err) = err {
working_set.error(err);
}
let (bytes, parse_error) = unescape_unquote_string(bytes, *span);
if working_set.parse_errors.first().is_some() {
if parse_error.is_some() {
report_capture_error(
engine_state,
&String::from_utf8_lossy(contents),
@ -217,15 +209,16 @@ pub fn eval_source(
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(
let (output, err) = parse(
&mut working_set,
Some(fname), // format!("entry #{}", entry_num)
source,
false,
&[],
);
if let Some(err) = working_set.parse_errors.first() {
if let Some(err) = err {
set_last_exit_code(stack, 1);
report_error(&working_set, err);
report_error(&working_set, &err);
return false;
}
@ -317,6 +310,43 @@ fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
);
}
pub fn report_error(
working_set: &StateWorkingSet,
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
) {
eprintln!("Error: {:?}", CliError(error, working_set));
// reset vt processing, aka ansi because illbehaved externals can break it
#[cfg(windows)]
{
let _ = nu_utils::enable_vt_processing();
}
}
pub fn report_error_new(
engine_state: &EngineState,
error: &(dyn miette::Diagnostic + Send + Sync + 'static),
) {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, error);
}
pub fn get_init_cwd() -> PathBuf {
std::env::current_dir().unwrap_or_else(|_| {
std::env::var("PWD")
.map(Into::into)
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
})
}
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);
get_init_cwd()
})
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -1,8 +1,5 @@
use nu_parser::parse;
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
ParseError,
};
use nu_parser::{parse, ParseError};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use reedline::{ValidationResult, Validator};
use std::sync::Arc;
@ -13,12 +10,9 @@ pub struct NuValidator {
impl Validator for NuValidator {
fn validate(&self, line: &str) -> ValidationResult {
let mut working_set = StateWorkingSet::new(&self.engine_state);
parse(&mut working_set, None, line.as_bytes(), false);
let (_, err) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
if matches!(
working_set.parse_errors.first(),
Some(ParseError::UnexpectedEof(..))
) {
if matches!(err, Some(ParseError::UnexpectedEof(..))) {
ValidationResult::Incomplete
} else {
ValidationResult::Complete

View File

@ -524,12 +524,10 @@ fn variables_completions() {
// Test completions for $nu
let suggestions = completer.complete("$nu.", 4);
assert_eq!(14, suggestions.len());
assert_eq!(12, suggestions.len());
let expected: Vec<String> = vec![
"config-path".into(),
"current-exe".into(),
"default-config-dir".into(),
"env-path".into(),
"history-path".into(),
"home-path".into(),
@ -556,100 +554,6 @@ fn variables_completions() {
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.os-info
let suggestions = completer.complete("$nu.os-info.", 12);
assert_eq!(4, suggestions.len());
let expected: Vec<String> = vec![
"arch".into(),
"family".into(),
"kernel_version".into(),
"name".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope
let suggestions = completer.complete("$nu.scope.", 10);
assert_eq!(5, suggestions.len());
let expected: Vec<String> = vec![
"aliases".into(),
"commands".into(),
"engine_state".into(),
"modules".into(),
"vars".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.commands
let suggestions = completer.complete("$nu.scope.commands.", 19);
assert_eq!(15, suggestions.len());
let expected: Vec<String> = vec![
"category".into(),
"creates_scope".into(),
"examples".into(),
"extra_usage".into(),
"is_builtin".into(),
"is_custom".into(),
"is_extern".into(),
"is_keyword".into(),
"is_plugin".into(),
"is_sub".into(),
"module_name".into(),
"name".into(),
"search_terms".into(),
"signatures".into(),
"usage".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.commands.signatures
let suggestions = completer.complete("$nu.scope.commands.signatures.", 30);
assert_eq!(17, suggestions.len());
let expected: Vec<String> = vec![
"any".into(),
"binary".into(),
"bool".into(),
"datetime".into(),
"duration".into(),
"filesize".into(),
"int".into(),
"list<any>".into(),
"list<binary>".into(),
"list<number>".into(),
"list<string>".into(),
"nothing".into(),
"number".into(),
"range".into(),
"record".into(),
"string".into(),
"table".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.engine_state
let suggestions = completer.complete("$nu.scope.engine_state.", 23);
assert_eq!(6, suggestions.len());
let expected: Vec<String> = vec![
"num_blocks".into(),
"num_decls".into(),
"num_env_vars".into(),
"num_modules".into(),
"num_vars".into(),
"source_bytes".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.scope.vars
let suggestions = completer.complete("$nu.scope.vars.", 15);
assert_eq!(3, suggestions.len());
let expected: Vec<String> = vec!["name".into(), "type".into(), "value".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var
let suggestions = completer.complete("$actor.", 7);
@ -759,8 +663,8 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
let (dir, _, mut engine_state, mut stack) = new_engine();
let (_, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state);
let block = parse(&mut working_set, None, block.as_bytes(), false);
assert!(working_set.parse_errors.is_empty());
let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]);
assert!(err.is_none());
(block, working_set.render())
};

View File

@ -146,9 +146,9 @@ pub fn merge_input(
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let block = parse(&mut working_set, None, input, false);
let (block, err) = parse(&mut working_set, None, input, false, &[]);
assert!(working_set.parse_errors.is_empty());
assert!(err.is_none());
(block, working_set.render())
};

View File

@ -6,17 +6,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021"
license = "MIT"
name = "nu-cmd-lang"
version = "0.80.0"
version = "0.77.1"
[lib]
bench = false
[dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-parser = { path = "../nu-parser", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.80.0" }
nu-color-config = { path = "../nu-color-config", version = "0.77.1" }
nu-engine = { path = "../nu-engine", version = "0.77.1" }
nu-parser = { path = "../nu-parser", version = "0.77.1" }
nu-protocol = { path = "../nu-protocol", version = "0.77.1" }
nu-utils = { path = "../nu-utils", version = "0.77.1" }
nu-ansi-term = "0.47.0"
@ -29,4 +29,4 @@ shadow-rs = { version = "0.21.0", default-features = false }
shadow-rs = { version = "0.21.0", default-features = false }
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.80.0" }
nu-test-support = { path="../nu-test-support", version = "0.77.1" }

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Copyright (c) 2019 - 2022 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -20,6 +20,15 @@ impl Command for Break {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,

View File

@ -0,0 +1,85 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::ReplOperation;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::IntoPipelineData;
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct Commandline;
impl Command for Commandline {
fn name(&self) -> &str {
"commandline"
}
fn signature(&self) -> Signature {
Signature::build("commandline")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.switch(
"append",
"appends the string to the end of the buffer",
Some('a'),
)
.switch(
"insert",
"inserts the string into the buffer at the cursor position",
Some('i'),
)
.switch(
"replace",
"replaces the current contents of the buffer (default)",
Some('r'),
)
.optional(
"cmd",
SyntaxShape::String,
"the string to perform the operation with",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"View or modify the current command line input buffer."
}
fn search_terms(&self) -> Vec<&str> {
vec!["repl", "interactive"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
let mut ops = engine_state
.repl_operation_queue
.lock()
.expect("repl op queue mutex");
ops.push_back(if call.has_flag("append") {
ReplOperation::Append(cmd.as_string()?)
} else if call.has_flag("insert") {
ReplOperation::Insert(cmd.as_string()?)
} else {
ReplOperation::Replace(cmd.as_string()?)
});
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
} else if let Some(ref cmd) = *engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex")
{
Ok(Value::String {
val: cmd.clone(),
span: call.head,
}
.into_pipeline_data())
} else {
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
}
}
}

View File

@ -21,7 +21,7 @@ impl Command for Const {
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by constant value",
)
.category(Category::Core)

View File

@ -20,6 +20,15 @@ impl Command for Continue {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,

View File

@ -22,11 +22,7 @@ impl Command for Do {
fn signature(&self) -> Signature {
Signature::build("do")
.required(
"closure",
SyntaxShape::OneOf(vec![SyntaxShape::Closure(None), SyntaxShape::Any]),
"the closure to run",
)
.required("closure", SyntaxShape::Any, "the closure to run")
.input_output_types(vec![(Type::Any, Type::Any)])
.switch(
"ignore-errors",

View File

@ -16,11 +16,7 @@ impl Command for ErrorMake {
fn signature(&self) -> Signature {
Signature::build("error make")
.input_output_types(vec![(Type::Nothing, Type::Error)])
.required(
"error_struct",
SyntaxShape::Record(vec![]),
"the error to create",
)
.required("error_struct", SyntaxShape::Record, "the error to create")
.switch(
"unspanned",
"remove the origin label from the error",
@ -48,66 +44,43 @@ impl Command for ErrorMake {
let arg: Value = call.req(engine_state, stack, 0)?;
let unspanned = call.has_flag("unspanned");
let throw_error = if unspanned { None } else { Some(span) };
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
if unspanned {
Err(make_error(&arg, None).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
} else {
Err(make_error(&arg, Some(span)).unwrap_or_else(|| {
ShellError::GenericError(
"Creating error value not supported.".into(),
"unsupported error format".into(),
Some(span),
None,
Vec::new(),
)
}))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create a simple custom error",
example: r#"error make {msg: "my custom error message"}"#,
result: Some(Value::Error {
error: Box::new(ShellError::GenericError(
"my custom error message".to_string(),
"".to_string(),
None,
None,
Vec::new(),
)),
}),
},
Example {
description: "Create a more complex custom error",
example: r#"error make {
msg: "my custom error message"
label: {
text: "my custom label text" # not mandatory unless $.label exists
start: 123 # not mandatory unless $.label.end is set
end: 456 # not mandatory unless $.label.start is set
}
}"#,
result: Some(Value::Error {
error: Box::new(ShellError::GenericError(
"my custom error message".to_string(),
"my custom label text".to_string(),
Some(Span::new(123, 456)),
None,
Vec::new(),
)),
}),
},
Example {
description:
"Create a custom error for a custom command that shows the span of the argument",
description: "Create a custom error for a custom command",
example: r#"def foo [x] {
let span = (metadata $x).span;
error make {
msg: "this is fishy"
label: {
text: "fish right here"
start: $span.start
end: $span.end
}
}
error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } }
}"#,
result: None,
},
Example {
description: "Create a simple custom error for a custom command",
example: r#"def foo [x] {
error make {msg: "this is fishy"}
}"#,
result: None,
},
@ -116,7 +89,7 @@ impl Command for ErrorMake {
}
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
if let Value::Record { span, .. } = &value {
if let Value::Record { .. } = &value {
let msg = value.get_data_by_key("msg");
let label = value.get_data_by_key("label");
@ -126,11 +99,6 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
let label_end = label.get_data_by_key("end");
let label_text = label.get_data_by_key("text");
let label_span = match label.span() {
Ok(lspan) => Some(lspan),
Err(_) => None,
};
match (label_start, label_end, label_text) {
(
Some(Value::Int { val: start, .. }),
@ -138,25 +106,13 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
Some(Value::String {
val: label_text, ..
}),
) => {
if start > end {
Some(ShellError::GenericError(
"invalid error format.".into(),
"`$.label.start` should be smaller than `$.label.end`".into(),
label_span,
Some(format!("{} > {}", start, end)),
Vec::new(),
))
} else {
Some(ShellError::GenericError(
message,
label_text,
Some(Span::new(start as usize, end as usize)),
None,
Vec::new(),
))
}
}
) => Some(ShellError::GenericError(
message,
label_text,
Some(Span::new(start as usize, end as usize)),
None,
Vec::new(),
)),
(
None,
None,
@ -170,27 +126,6 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
None,
Vec::new(),
)),
(_, _, None) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.text`".into(),
label_span,
None,
Vec::new(),
)),
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.end`".into(),
label_span,
Some("required because `$.label.start` is set".to_string()),
Vec::new(),
)),
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.label.start`".into(),
label_span,
Some("required because `$.label.end` is set".to_string()),
Vec::new(),
)),
_ => None,
}
}
@ -201,13 +136,6 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
None,
Vec::new(),
)),
(None, _) => Some(ShellError::GenericError(
"Unable to parse error format.".into(),
"missing required member `$.msg`".into(),
Some(*span),
None,
Vec::new(),
)),
_ => None,
}
} else {

View File

@ -1,75 +0,0 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct ExportModule;
impl Command for ExportModule {
fn name(&self) -> &str {
"export module"
}
fn usage(&self) -> &str {
"Export a custom module from a module."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export module")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("module", SyntaxShape::String, "module name or module path")
.optional(
"block",
SyntaxShape::Block,
"body of the module if 'module' parameter is not a path",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Define a custom command in a submodule of a module and call it",
example: r#"module spam {
export module eggs {
export def foo [] { "foo" }
}
}
use spam eggs
eggs foo"#,
result: Some(Value::test_string("foo")),
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(ExportModule {})
}
}

View File

@ -19,7 +19,6 @@ impl Command for Extern {
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters")
.optional("body", SyntaxShape::Block, "wrapper function block")
.category(Category::Core)
}

View File

@ -120,11 +120,9 @@ impl Command for For {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.drain_with_exit_code()?;
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
return Ok(PipelineData::new_external_stream_with_only_exit_code(
exit_code,
));
break;
}
}
}
@ -164,11 +162,9 @@ impl Command for For {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.drain_with_exit_code()?;
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
return Ok(PipelineData::new_external_stream_with_only_exit_code(
exit_code,
));
break;
}
}
}

View File

@ -7,6 +7,7 @@ use nu_protocol::{
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Cow;
#[derive(Clone)]
pub struct HelpAliases;
@ -111,22 +112,22 @@ pub fn help_aliases(
name.push_str(&r.item);
}
let Some(alias) = engine_state.find_decl(name.as_bytes(), &[]) else {
let alias_id = if let Some(id) = engine_state.find_alias(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::AliasNotFound(span(
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
)));
};
let Some(alias) = engine_state.get_decl(alias).as_alias() else {
return Err(ShellError::AliasNotFound(span(
&rest.iter().map(|r| r.span).collect::<Vec<Span>>(),
)));
};
let alias_expansion = engine_state
.get_alias(alias_id)
.iter()
.map(|span| String::from_utf8_lossy(engine_state.get_span_contents(span)))
.collect::<Vec<Cow<str>>>()
.join(" ");
let alias_expansion =
String::from_utf8_lossy(engine_state.get_span_contents(&alias.wrapped_call.span));
let usage = alias.usage();
let extra_usage = alias.extra_usage();
let alias_usage = engine_state.build_alias_usage(alias_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
@ -135,12 +136,14 @@ pub fn help_aliases(
let mut long_desc = String::new();
long_desc.push_str(usage);
long_desc.push_str("\n\n");
if !extra_usage.is_empty() {
long_desc.push_str(extra_usage);
if let Some((usage, extra_usage)) = alias_usage {
long_desc.push_str(&usage);
long_desc.push_str("\n\n");
if !extra_usage.is_empty() {
long_desc.push_str(&extra_usage);
long_desc.push_str("\n\n");
}
}
long_desc.push_str(&format!("{G}Alias{RESET}: {C}{name}{RESET}"));
@ -162,7 +165,7 @@ pub fn help_aliases(
fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope_data = ScopeData::new(engine_state, stack);
scope_data.populate_all();
scope_data.populate_aliases();
scope_data.collect_aliases(span)
}

View File

@ -1,152 +0,0 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{get_full_help, scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct HelpExterns;
impl Command for HelpExterns {
fn name(&self) -> &str {
"help externs"
}
fn usage(&self) -> &str {
"Show help on nushell externs."
}
fn signature(&self) -> Signature {
Signature::build("help externs")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of extern to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in extern names and usage",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all externs",
example: "help externs",
result: None,
},
Example {
description: "show help for single extern",
example: "help externs smth",
result: None,
},
Example {
description: "search for string in extern names and usages",
example: "help externs --find smth",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_externs(engine_state, stack, call)
}
}
pub fn help_externs(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_externs(engine_state, stack, head);
let found_cmds_vec =
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_externs(engine_state, stack, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let output = engine_state
.get_signatures_with_examples(false)
.iter()
.filter(|(signature, _, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _, is_parser_keyword)| {
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
})
.collect::<Vec<String>>();
if !output.is_empty() {
Ok(Value::String {
val: output.join("======================\n\n"),
span: call.head,
}
.into_pipeline_data())
} else {
Err(ShellError::CommandNotFound(span(&[
rest[0].span,
rest[rest.len() - 1].span,
])))
}
}
}
fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope = ScopeData::new(engine_state, stack);
scope.populate_all();
scope.collect_externs(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpExterns;
use crate::test_examples;
test_examples(HelpExterns {})
}
}

View File

@ -4,8 +4,8 @@ use nu_engine::{scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
span, AliasId, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData,
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -117,7 +117,9 @@ pub fn help_modules(
name.push_str(&r.item);
}
let Some(module_id) = engine_state.find_module(name.as_bytes(), &[]) else {
let module_id = if let Some(id) = engine_state.find_module(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::ModuleNotFoundAtRuntime {
mod_name: name,
span: span(&rest.iter().map(|r| r.span).collect::<Vec<Span>>()),
@ -150,16 +152,9 @@ pub fn help_modules(
long_desc.push_str("\n\n");
if !module.decls.is_empty() || module.main.is_some() {
let commands: Vec<(Vec<u8>, DeclId)> = engine_state
.get_decls_sorted(false)
.filter(|(_, id)| !engine_state.get_decl(*id).is_alias())
.collect();
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
let mut module_commands: Vec<(Vec<u8>, DeclId)> = module
.decls()
.into_iter()
.filter(|(_, id)| !engine_state.get_decl(*id).is_alias())
.collect();
let mut module_commands = module.decls();
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
let commands_str = module_commands
@ -186,18 +181,15 @@ pub fn help_modules(
long_desc.push_str("\n\n");
}
if !module.decls.is_empty() {
let aliases: Vec<(Vec<u8>, DeclId)> = engine_state
.get_decls_sorted(false)
.filter(|(_, id)| engine_state.get_decl(*id).is_alias())
.collect();
if !module.aliases.is_empty() {
let aliases: Vec<(Vec<u8>, AliasId)> = engine_state.get_aliases_sorted(false).collect();
let mut module_aliases: Vec<(Vec<u8>, DeclId)> = module
.decls()
.into_iter()
.filter(|(_, id)| engine_state.get_decl(*id).is_alias())
let mut module_aliases: Vec<(&[u8], AliasId)> = module
.aliases
.iter()
.map(|(name, id)| (name.as_ref(), *id))
.collect();
module_aliases.sort_by(|a, b| a.0.cmp(&b.0));
module_aliases.sort_by(|a, b| a.0.cmp(b.0));
let aliases_str = module_aliases
.iter()
@ -206,7 +198,7 @@ pub fn help_modules(
if let Some((used_name_bytes, _)) =
aliases.iter().find(|(_, alias_id)| id == alias_id)
{
if engine_state.find_decl(name.as_bytes(), &[]).is_some() {
if engine_state.find_alias(name.as_bytes(), &[]).is_some() {
format!("{CB}{name}{RESET}")
} else {
let alias_name = String::from_utf8_lossy(used_name_bytes);

View File

@ -20,7 +20,7 @@ impl Command for If {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("if")
.input_output_types(vec![(Type::Any, Type::Any)])
.required("cond", SyntaxShape::MathExpression, "condition to check")
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"then_block",
SyntaxShape::Block,
@ -40,6 +40,15 @@ impl Command for If {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,

View File

@ -22,7 +22,7 @@ impl Command for Let {
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)

View File

@ -25,6 +25,15 @@ impl Command for Loop {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
@ -58,11 +67,9 @@ impl Command for Loop {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.drain_with_exit_code()?;
let exit_code = pipeline.print(engine_state, stack, false, false)?;
if exit_code != 0 {
return Ok(PipelineData::new_external_stream_with_only_exit_code(
exit_code,
));
break;
}
}
}

View File

@ -1,124 +0,0 @@
use nu_engine::{eval_block, eval_expression_with_input, CallExt};
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Matcher, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Match;
impl Command for Match {
fn name(&self) -> &str {
"match"
}
fn usage(&self) -> &str {
"Conditionally run a block on a matched value."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("match")
.input_output_types(vec![(Type::Any, Type::Any)])
.required("value", SyntaxShape::Any, "value to check")
.required(
"match_block",
SyntaxShape::MatchBlock,
"block to run if check succeeds",
)
.category(Category::Core)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
let block = call.positional_nth(1);
if let Some(Expression {
expr: Expr::MatchBlock(matches),
..
}) = block
{
for match_ in matches {
let mut match_variables = vec![];
if match_.0.match_value(&value, &mut match_variables) {
// This case does match, go ahead and return the evaluated expression
for match_variable in match_variables {
stack.add_var(match_variable.0, match_variable.1);
}
if let Some(block_id) = match_.1.as_block() {
let block = engine_state.get_block(block_id);
return eval_block(
engine_state,
stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
} else {
return eval_expression_with_input(
engine_state,
stack,
&match_.1,
input,
call.redirect_stdout,
call.redirect_stderr,
)
.map(|x| x.0);
}
}
}
}
Ok(PipelineData::Empty)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Match on a value in range",
example: "match 3 { 1..10 => 'yes!' }",
result: Some(Value::test_string("yes!")),
},
Example {
description: "Match on a field in a record",
example: "match {a: 100} { {a: $my_value} => { $my_value } }",
result: Some(Value::test_int(100)),
},
Example {
description: "Match with a catch-all",
example: "match 3 { 1 => { 'yes!' }, _ => { 'no!' } }",
result: Some(Value::test_string("no!")),
},
Example {
description: "Match against a list",
example: "match [1, 2, 3] { [$a, $b, $c] => { $a + $b + $c }, _ => 0 }",
result: Some(Value::test_int(6)),
},
Example {
description: "Match against pipeline input",
example: "{a: {b: 3}} | match $in {{a: { $b }} => ($b + 10) }",
result: Some(Value::test_int(13)),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Match {})
}
}

View File

@ -1,6 +1,6 @@
mod alias;
mod break_;
mod collect;
mod commandline;
mod const_;
mod continue_;
mod def;
@ -14,14 +14,12 @@ mod export_alias;
mod export_def;
mod export_def_env;
mod export_extern;
mod export_module;
mod export_use;
mod extern_;
mod for_;
pub mod help;
pub mod help_aliases;
pub mod help_commands;
pub mod help_externs;
pub mod help_modules;
mod help_operators;
mod hide;
@ -30,7 +28,6 @@ mod if_;
mod ignore;
mod let_;
mod loop_;
mod match_;
mod module;
mod mut_;
pub(crate) mod overlay;
@ -42,7 +39,7 @@ mod while_;
pub use alias::Alias;
pub use break_::Break;
pub use collect::Collect;
pub use commandline::Commandline;
pub use const_::Const;
pub use continue_::Continue;
pub use def::Def;
@ -56,14 +53,12 @@ pub use export_alias::ExportAlias;
pub use export_def::ExportDef;
pub use export_def_env::ExportDefEnv;
pub use export_extern::ExportExtern;
pub use export_module::ExportModule;
pub use export_use::ExportUse;
pub use extern_::Extern;
pub use for_::For;
pub use help::Help;
pub use help_aliases::HelpAliases;
pub use help_commands::HelpCommands;
pub use help_externs::HelpExterns;
pub use help_modules::HelpModules;
pub use help_operators::HelpOperators;
pub use hide::Hide;
@ -72,7 +67,6 @@ pub use if_::If;
pub use ignore::Ignore;
pub use let_::Let;
pub use loop_::Loop;
pub use match_::Match;
pub use module::Module;
pub use mut_::Mut;
pub use overlay::*;

View File

@ -19,13 +19,8 @@ impl Command for Module {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("module")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("module", SyntaxShape::String, "module name or module path")
.optional(
"block",
SyntaxShape::Block,
"body of the module if 'module' parameter is not a module path",
)
.required("module_name", SyntaxShape::String, "module name")
.required("block", SyntaxShape::Block, "body of the module")
.category(Category::Core)
}

View File

@ -22,7 +22,7 @@ impl Command for Mut {
.required("var_name", SyntaxShape::VarWithOptType, "variable name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by value",
)
.category(Category::Core)

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env, CallExt};
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
use nu_parser::trim_quotes_str;
use nu_protocol::ast::{Call, Expr};
use nu_protocol::engine::{Command, EngineState, Stack};
@ -66,24 +66,23 @@ impl Command for OverlayUse {
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
if let Expr::Overlay(module_id) = overlay_expr.expr {
module_id
} else {
return Err(ShellError::NushellFailedSpanned {
msg: "Not an overlay".to_string(),
label: "requires an overlay (path or a string)".to_string(),
span: overlay_expr.span,
});
}
let maybe_origin_module_id = if let Some(overlay_expr) = call.parser_info_nth(0) {
if let Expr::Overlay(module_id) = overlay_expr.expr {
module_id
} else {
return Err(ShellError::NushellFailedSpanned {
msg: "Missing positional".to_string(),
label: "missing required overlay".to_string(),
span: call.head,
msg: "Not an overlay".to_string(),
label: "requires an overlay (path or a string)".to_string(),
span: overlay_expr.span,
});
};
}
} else {
return Err(ShellError::NushellFailedSpanned {
msg: "Missing positional".to_string(),
label: "missing required overlay".to_string(),
span: call.head,
});
};
let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
name
@ -114,12 +113,7 @@ impl Command for OverlayUse {
// Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block {
let maybe_path = find_in_dirs_env(
&name_arg.item,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
)?;
let maybe_path = find_in_dirs_env(&name_arg.item, engine_state, caller_stack)?;
let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack.gather_captures(&block.captures);
@ -134,11 +128,6 @@ impl Command for OverlayUse {
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
}
if let Some(file_path) = &maybe_path {
let file_path = Value::string(file_path.to_string_lossy(), call.head);
callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path);
}
let _ = eval_block(
engine_state,
&mut callee_stack,

View File

@ -63,7 +63,7 @@ impl Command for Register {
result: None,
},
Example {
description: "Register `nu_plugin_query` plugin from `nu -c` (writes/updates $nu.plugin-path)",
description: "Register `nu_plugin_query` plugin from `nu -c`(plugin will be available in that nu session only)",
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
result: None,
},

View File

@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Closure, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Type, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
@ -26,16 +26,22 @@ impl Command for Try {
"catch_block",
SyntaxShape::Keyword(
b"catch".to_vec(),
Box::new(SyntaxShape::OneOf(vec![
SyntaxShape::Closure(None),
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
])),
Box::new(SyntaxShape::Closure(Some(vec![SyntaxShape::Any]))),
),
"block to run if try block fails",
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
@ -53,13 +59,17 @@ impl Command for Try {
match result {
Err(error) => {
let error = intercept_block_control(error)?;
let err_record = err_to_record(error, call.head);
handle_catch(err_record, catch_block, engine_state, stack)
let err_value = Value::Error {
error: Box::new(error),
};
handle_catch(err_value, catch_block, engine_state, stack)
}
Ok(PipelineData::Value(Value::Error { error }, ..)) => {
let error = intercept_block_control(*error)?;
let err_record = err_to_record(error, call.head);
handle_catch(err_record, catch_block, engine_state, stack)
let err_value = Value::Error {
error: Box::new(error),
};
handle_catch(err_value, catch_block, engine_state, stack)
}
// external command may fail to run
Ok(pipeline) => {
@ -135,19 +145,6 @@ fn intercept_block_control(error: ShellError) -> Result<ShellError, ShellError>
}
}
/// Convert from `error` to [`Value::Record`] so the error information can be easily accessed in catch.
fn err_to_record(error: ShellError, head: Span) -> Value {
let cols = vec!["msg".to_string(), "debug".to_string(), "raw".to_string()];
let vals = vec![
Value::string(error.to_string(), head),
Value::string(format!("{error:?}"), head),
Value::Error {
error: Box::new(error),
},
];
Value::record(cols, vals, head)
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env};
use nu_engine::{eval_block, find_in_dirs_env, redirect_env};
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
@ -14,14 +14,14 @@ impl Command for Use {
}
fn usage(&self) -> &str {
"Use definitions from a module, making them available in your shell."
"Use definitions from a module."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("module", SyntaxShape::String, "Module or module file")
.rest(
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
@ -30,10 +30,7 @@ impl Command for Use {
}
fn extra_usage(&self) -> &str {
r#"See `help std` for the standard library module.
See `help modules` to list all available modules.
This command is a parser keyword. For details, check:
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
@ -48,10 +45,13 @@ This command is a parser keyword. For details, check:
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let Some(Expression {
expr: Expr::ImportPattern(import_pattern),
let import_pattern = if let Some(Expression {
expr: Expr::ImportPattern(pat),
..
}) = call.get_parser_info("import_pattern") else {
}) = call.parser_info_nth(0)
{
pat
} else {
return Err(ShellError::GenericError(
"Unexpected import".into(),
"import pattern not supported".into(),
@ -72,16 +72,13 @@ This command is a parser keyword. For details, check:
let module_arg_str = String::from_utf8_lossy(
engine_state.get_span_contents(&import_pattern.head.span),
);
let maybe_file_path = find_in_dirs_env(
&module_arg_str,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
)?;
let maybe_parent = maybe_file_path
.as_ref()
.and_then(|path| path.parent().map(|p| p.to_path_buf()));
let maybe_parent = if let Some(path) =
find_in_dirs_env(&module_arg_str, engine_state, caller_stack)?
{
path.parent().map(|p| p.to_path_buf()).or(None)
} else {
None
};
let mut callee_stack = caller_stack.gather_captures(&block.captures);
@ -91,11 +88,6 @@ This command is a parser keyword. For details, check:
callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
}
if let Some(file_path) = maybe_file_path {
let file_path = Value::string(file_path.to_string_lossy(), call.head);
callee_stack.add_env_var("CURRENT_FILE".to_string(), file_path);
}
// Run the block (discard the result)
let _ = eval_block(
engine_state,
@ -137,26 +129,6 @@ This command is a parser keyword. For details, check:
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::test_string("BAZ")),
},
Example {
description: "Use a plain module name to import its definitions qualified by the module name",
example: r#"module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam; (spam foo) + (spam bar)"#,
result: Some(Value::test_string("foobar")),
},
Example {
description: "Specify * to use all definitions in a module",
example: r#"module spam { export def foo [] { "foo" }; export def bar [] { "bar" } }; use spam *; (foo) + (bar)"#,
result: Some(Value::test_string("foobar")),
},
Example {
description: "To use commands with spaces, like subcommands, surround them with quotes",
example: r#"module spam { export def 'foo bar' [] { "baz" } }; use spam 'foo bar'; foo bar"#,
result: Some(Value::test_string("baz")),
},
Example {
description: "To use multiple definitions from a module, wrap them in a list",
example: r#"module spam { export def foo [] { "foo" }; export def 'foo bar' [] { "baz" } }; use spam ['foo', 'foo bar']; (foo) + (foo bar)"#,
result: Some(Value::test_string("foobaz")),
},
]
}
}

View File

@ -21,7 +21,7 @@ impl Command for While {
Signature::build("while")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("cond", SyntaxShape::MathExpression, "condition to check")
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"block",
SyntaxShape::Block,
@ -30,6 +30,15 @@ impl Command for While {
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
@ -68,13 +77,10 @@ impl Command for While {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.drain_with_exit_code()?;
let exit_code =
pipeline.print(engine_state, stack, false, false)?;
if exit_code != 0 {
return Ok(
PipelineData::new_external_stream_with_only_exit_code(
exit_code,
),
);
break;
}
}
}

View File

@ -18,7 +18,7 @@ pub fn create_default_context() -> EngineState {
bind_command! {
Alias,
Break,
Collect,
Commandline,
Const,
Continue,
Def,
@ -33,14 +33,12 @@ pub fn create_default_context() -> EngineState {
ExportDefEnv,
ExportExtern,
ExportUse,
ExportModule,
Extern,
For,
Help,
HelpAliases,
HelpCommands,
HelpModules,
HelpExterns,
HelpOperators,
Hide,
HideEnv,
@ -53,7 +51,6 @@ pub fn create_default_context() -> EngineState {
OverlayHide,
Let,
Loop,
Match,
Module,
Mut,
Return,

View File

@ -126,9 +126,9 @@ fn eval_pipeline_without_terminal_expression(
pub fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) {
let mut working_set = StateWorkingSet::new(engine_state);
let output = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false);
let (output, err) = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]);
if let Some(err) = working_set.parse_errors.first() {
if let Some(err) = err {
panic!("test parse error in `{contents}`: {err:?}")
}

View File

@ -13,12 +13,11 @@ mod test_examples {
check_example_evaluates_to_expected_output,
check_example_input_and_output_types_match_command_signature,
};
use crate::{
Break, Collect, Def, Describe, Echo, ExportCommand, ExportDef, If, Let, Module, Mut, Use,
};
use crate::{Break, Describe, Mut};
use crate::{Echo, If, Let};
use nu_protocol::{
engine::{Command, EngineState, StateWorkingSet},
Type, Value,
Type,
};
use std::collections::HashSet;
@ -56,28 +55,17 @@ mod test_examples {
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
let mut engine_state = Box::new(EngineState::new());
let cwd = std::env::current_dir()
.expect("Could not get current working directory.")
.to_string_lossy()
.to_string();
engine_state.add_env_var("PWD".to_string(), Value::test_string(cwd));
let delta = {
// Base functions that are needed for testing
// Try to keep this working set small to keep tests running as fast as possible
let mut working_set = StateWorkingSet::new(&engine_state);
working_set.add_decl(Box::new(Break));
working_set.add_decl(Box::new(Collect));
working_set.add_decl(Box::new(Def));
working_set.add_decl(Box::new(Describe));
working_set.add_decl(Box::new(Echo));
working_set.add_decl(Box::new(ExportCommand));
working_set.add_decl(Box::new(ExportDef));
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(Module));
working_set.add_decl(Box::new(Mut));
working_set.add_decl(Box::new(Use));
// Adding the command that is being tested to the working set
working_set.add_decl(cmd);

View File

@ -5,19 +5,21 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.80.0"
version = "0.77.1"
[lib]
bench = false
[dependencies]
serde = { version="1.0.123", features=["derive"] }
# used only for text_style Alignments
tabled = { version = "0.10.0", features = ["color"], default-features = false }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.77.1" }
nu-ansi-term = "0.47.0"
nu-utils = { path = "../nu-utils", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-json = { path="../nu-json", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.77.1" }
nu-engine = { path = "../nu-engine", version = "0.77.1" }
nu-json = { path="../nu-json", version = "0.77.1" }
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.80.0" }
nu-test-support = { path="../nu-test-support", version = "0.77.1" }

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Copyright (c) 2019 - 2022 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -20,26 +20,22 @@ impl From<Style> for NuStyle {
}
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();
if s.is_blink {
attrs.push('l');
};
if s.is_bold {
attrs.push('b');
};
if s.is_dimmed {
attrs.push('d');
};
if s.is_reverse {
attrs.push('r');
};
if s.is_strikethrough {
attrs.push('s');
};
if s.is_underline {
attrs.push('u');
};
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

View File

@ -9,7 +9,6 @@ pub fn default_shape_color(shape: String) -> Style {
"shape_binary" => Style::new().fg(Color::Purple).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_bool" => Style::new().fg(Color::LightCyan),
"shape_closure" => Style::new().fg(Color::Green).bold(),
"shape_custom" => Style::new().fg(Color::Green),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_directory" => Style::new().fg(Color::Cyan),
@ -22,10 +21,8 @@ pub fn default_shape_color(shape: String) -> Style {
"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_keyword" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_literal" => Style::new().fg(Color::Blue),
"shape_match_pattern" => Style::new().fg(Color::Green),
"shape_nothing" => Style::new().fg(Color::LightCyan),
"shape_operator" => Style::new().fg(Color::Yellow),
"shape_or" => Style::new().fg(Color::Purple).bold(),
@ -38,7 +35,6 @@ pub fn default_shape_color(shape: String) -> Style {
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_variable" => Style::new().fg(Color::Purple),
"shape_vardecl" => Style::new().fg(Color::Purple),
_ => Style::default(),
}
}

View File

@ -1,4 +1,3 @@
use crate::text_style::Alignment;
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
use nu_ansi_term::{Color, Style};
use nu_engine::eval_block;
@ -6,6 +5,7 @@ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
CliError, IntoPipelineData, Value,
};
use tabled::alignment::AlignmentHorizontal;
use std::{
collections::HashMap,
@ -21,6 +21,13 @@ pub enum ComputableStyle {
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>;
//
@ -111,28 +118,34 @@ impl<'a> StyleComputer<'a> {
// Used only by the `table` command.
pub fn style_primitive(&self, value: &Value) -> TextStyle {
use Alignment::*;
let s = self.compute(&value.get_type().get_non_specified_string(), value);
match *value {
Value::Bool { .. } => TextStyle::with_style(Left, s),
Value::Int { .. } => TextStyle::with_style(Right, s),
Value::Filesize { .. } => TextStyle::with_style(Right, s),
Value::Duration { .. } => TextStyle::with_style(Right, s),
Value::Date { .. } => TextStyle::with_style(Left, s),
Value::Range { .. } => TextStyle::with_style(Left, s),
Value::Float { .. } => TextStyle::with_style(Right, s),
Value::String { .. } => TextStyle::with_style(Left, s),
Value::Nothing { .. } => TextStyle::with_style(Left, s),
Value::Binary { .. } => TextStyle::with_style(Left, s),
Value::CellPath { .. } => TextStyle::with_style(Left, s),
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(Left, s)
TextStyle::with_style(AlignmentHorizontal::Left, s)
}
Value::Closure { .. }
| Value::CustomValue { .. }
| Value::Error { .. }
| Value::LazyRecord { .. }
| Value::MatchPattern { .. } => TextStyle::basic_left(),
_ => TextStyle::basic_left(),
}
}
@ -140,12 +153,6 @@ impl<'a> StyleComputer<'a> {
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
let config = engine_state.get_config();
macro_rules! initial {
($a:expr, $b:expr) => {
($a.to_string(), ComputableStyle::Static($b))
};
}
// Create the hashmap
let mut map: StyleMapping = HashMap::from([
initial!("separator", Color::White.normal()),

View File

@ -1,11 +1,7 @@
use nu_ansi_term::{Color, Style};
use std::fmt::Display;
#[derive(Debug, Clone, Copy)]
pub enum Alignment {
Center,
Left,
Right,
}
pub type Alignment = tabled::alignment::AlignmentHorizontal;
#[derive(Debug, Clone, Copy)]
pub struct TextStyle {
@ -244,3 +240,23 @@ impl Default for TextStyle {
Self::new()
}
}
impl tabled::papergrid::Color for TextStyle {
fn fmt_prefix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(color) = &self.color_style {
color.prefix().fmt(f)?;
}
Ok(())
}
fn fmt_suffix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(color) = &self.color_style {
if !color.is_plain() {
f.write_str("\u{1b}[0m")?;
}
}
Ok(())
}
}

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.80.0"
version = "0.77.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,42 +13,40 @@ version = "0.80.0"
bench = false
[dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.80.0" }
nu-color-config = { path = "../nu-color-config", version = "0.80.0" }
nu-engine = { path = "../nu-engine", version = "0.80.0" }
nu-explore = { path = "../nu-explore", version = "0.80.0" }
nu-glob = { path = "../nu-glob", version = "0.80.0" }
nu-json = { path = "../nu-json", version = "0.80.0" }
nu-parser = { path = "../nu-parser", version = "0.80.0" }
nu-path = { path = "../nu-path", version = "0.80.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.80.0" }
nu-protocol = { path = "../nu-protocol", version = "0.80.0" }
nu-system = { path = "../nu-system", version = "0.80.0" }
nu-table = { path = "../nu-table", version = "0.80.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.80.0" }
nu-utils = { path = "../nu-utils", version = "0.80.0" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.77.1" }
nu-color-config = { path = "../nu-color-config", version = "0.77.1" }
nu-engine = { path = "../nu-engine", version = "0.77.1" }
nu-explore = { path = "../nu-explore", version = "0.77.1" }
nu-glob = { path = "../nu-glob", version = "0.77.1" }
nu-json = { path = "../nu-json", version = "0.77.1" }
nu-parser = { path = "../nu-parser", version = "0.77.1" }
nu-path = { path = "../nu-path", version = "0.77.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.77.1" }
nu-protocol = { path = "../nu-protocol", version = "0.77.1" }
nu-system = { path = "../nu-system", version = "0.77.1" }
nu-table = { path = "../nu-table", version = "0.77.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.77.1" }
nu-utils = { path = "../nu-utils", version = "0.77.1" }
num-format = { version = "0.4.3" }
nu-ansi-term = "0.47.0"
# Potential dependencies for extras
Inflector = "0.11"
alphanumeric-sort = "1.5.0"
alphanumeric-sort = "1.4.4"
atty = "0.2.14"
base64 = "0.21.0"
byteorder = "1.4.3"
bytesize = "1.2.0"
calamine = "0.19.1"
chrono = { version = "0.4.23", features = [
"std",
"unstable-locales",
], default-features = false }
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
chrono-humanize = "0.2.1"
chrono-tz = "0.8.1"
crossterm = "0.26"
crossterm = "0.24.0"
csv = "1.2.0"
dialoguer = { default-features = false, features = ["fuzzy-select"], version = "0.10.3" }
dialoguer = { default-features = false, version = "0.10.3" }
digest = { default-features = false, version = "0.10.0" }
dtparse = "1.4.0"
dtparse = "1.2.0"
encoding_rs = "0.8.30"
fancy-regex = "0.11.0"
filesize = "0.2.0"
@ -60,43 +58,37 @@ indicatif = "0.17.2"
is-root = "0.1.2"
itertools = "0.10.0"
log = "0.4.14"
lscolors = { version = "0.14", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" }
miette = { version = "5.7.0", features = ["fancy-no-backtrace"] }
mime = "0.3.16"
mime_guess = "2.0.4"
notify = "4.0.17"
num = { version = "0.4.0", optional = true }
num-traits = "0.2.14"
once_cell = "1.17"
open = "4.0.2"
open = "3.2.0"
pathdiff = "0.2.1"
powierza-coefficient = "1.0.2"
quick-xml = "0.28"
quick-xml = "0.27"
rand = "0.8"
rayon = "1.7.0"
regex = "1.7.1"
ureq = { version = "2.6.2", default-features = false, features = [
"json",
"charset",
"native-tls",
"gzip",
] }
ureq = { version = "2.6.2", default-features = false, features = ["json", "charset", "native-tls", "gzip"] }
native-tls = "0.2.11"
roxmltree = "0.18.0"
rust-embed = "6.6.0"
same-file = "1.0.6"
serde = { version = "1.0.123", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7.0"
serde_yaml = "0.9.4"
sha2 = "0.10.0"
# Disable default features b/c the default features build Git (very slow to compile)
percent-encoding = "2.2.0"
reedline = { version = "0.17.0", features = ["bashisms", "sqlite"] }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.32.0", features = ["serde"], optional = true }
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
sysinfo = "0.28.2"
tabled = "0.12.0"
tabled = "0.10.0"
terminal_size = "0.2.1"
thiserror = "1.0.31"
titlecase = "2.0.0"
@ -108,14 +100,13 @@ uuid = { version = "1.3.0", features = ["v4"] }
wax = { version = "0.5.0" }
which = { version = "4.4.0", optional = true }
print-positions = "0.6.1"
os_pipe = "1.1.3"
[target.'cfg(windows)'.dependencies]
winreg = "0.50.0"
winreg = "0.11.0"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
umask = "2.1.0"
umask = "2.0.0"
users = "0.11.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
@ -128,7 +119,7 @@ features = [
"checked_arithmetic",
"concat_str",
"cross_join",
"csv",
"csv-file",
"cum_agg",
"default",
"dtype-categorical",
@ -151,27 +142,26 @@ features = [
"to_dummies",
]
optional = true
version = "0.29.0"
version = "0.27.2"
[target.'cfg(windows)'.dependencies.windows]
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
version = "0.48.0"
version = "0.44.0"
[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?
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
trash-support = ["trash"]
which-support = ["which"]
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.80.0" }
mockito = "1.0.0"
nu-test-support = { path = "../nu-test-support", version = "0.77.1" }
mockito = "0.32.3"
dirs-next = "2.0.0"
hamcrest2 = "0.3.0"
proptest = "1.1.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
rstest = { version = "0.17.0", default-features = false }
rstest = { version = "0.16.0", default-features = false }

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 - 2023 The Nushell Project Developers
Copyright (c) 2019 - 2022 The Nushell Project Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,23 @@
# 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"

View File

@ -90,7 +90,7 @@ impl Command for SubCommand {
Example {
description:
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
example: "[4 3 2] | bits not -n '2'",
example: "[4 3 2] | bits not -n 2",
result: Some(Value::List {
vals: vec![
Value::test_int(65531),

View File

@ -85,7 +85,7 @@ impl Command for SubCommand {
},
Example {
description: "Rotate right a list of numbers of one byte",
example: "[15 33 92] | bits ror 2 -n '1'",
example: "[15 33 92] | bits ror 2 -n 1",
result: Some(Value::List {
vals: vec![
Value::test_int(195),

View File

@ -85,7 +85,7 @@ impl Command for SubCommand {
},
Example {
description: "Shift left a number with 1 byte by 7 bits",
example: "2 | bits shl 7 -n '1'",
example: "2 | bits shl 7 -n 1",
result: Some(Value::test_int(0)),
},
Example {

View File

@ -1,19 +1,20 @@
use crate::{
input_handler::{operate, CmdArgument},
util,
};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, Range, ShellError, Signature, Span, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
use std::cmp::Ordering;
#[derive(Clone)]
pub struct BytesAt;
struct Arguments {
indexes: Subbytes,
start: isize,
end: isize,
arg_span: Span,
cell_paths: Option<Vec<CellPath>>,
}
@ -23,14 +24,103 @@ impl CmdArgument for Arguments {
}
}
impl From<(isize, isize)> for Subbytes {
fn from(input: (isize, isize)) -> Self {
Self(input.0, input.1)
}
}
/// ensure given `range` is valid, and returns [start, end, val_span] pair.
fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellError> {
let (start, end, span) = match range {
Value::List { mut vals, span } => {
if vals.len() != 2 {
return Err(ShellError::UnsupportedInput(
"More than two indices in range".to_string(),
"value originates from here".to_string(),
head,
span,
));
} else {
let end = vals.pop().expect("Already check has size 2");
let end = match end {
Value::Int { val, .. } => val.to_string(),
Value::String { val, .. } => val,
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(*error),
other => {
return Err(ShellError::UnsupportedInput(
"Only string or list<int> ranges are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
))
}
};
let start = vals.pop().expect("Already check has size 1");
let start = match start {
Value::Int { val, .. } => val.to_string(),
Value::String { val, .. } => val,
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(*error),
other => {
return Err(ShellError::UnsupportedInput(
"Only string or list<int> ranges are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
))
}
};
(start, end, span)
}
}
Value::String { val, span } => {
let split_result = val.split_once(',');
match split_result {
Some((start, end)) => (start.to_string(), end.to_string(), span),
None => {
return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span,
))
}
}
}
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(*error),
other => {
return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
other.expect_span(),
))
}
};
#[derive(Clone, Copy)]
struct Subbytes(isize, isize);
let start: isize = if start.is_empty() || start == "_" {
0
} else {
start.trim().parse().map_err(|_| {
ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span,
)
})?
};
let end: isize = if end.is_empty() || end == "_" {
isize::max_value()
} else {
end.trim().parse().map_err(|_| {
ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span,
)
})?
};
Ok((start, end, span))
}
impl Command for BytesAt {
fn name(&self) -> &str {
@ -41,7 +131,7 @@ impl Command for BytesAt {
Signature::build("bytes at")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.vectorizes_over_list(true)
.required("range", SyntaxShape::Range, "the range to get bytes")
.required("range", SyntaxShape::Any, "the indexes to get bytes")
.rest(
"rest",
SyntaxShape::CellPath,
@ -51,7 +141,7 @@ impl Command for BytesAt {
}
fn usage(&self) -> &str {
"Get bytes defined by a range"
"Get bytes defined by a range. Note that the start is included but the end is excluded, and that the first byte is index 0."
}
fn search_terms(&self) -> Vec<&str> {
@ -65,45 +155,48 @@ impl Command for BytesAt {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let range: Range = call.req(engine_state, stack, 0)?;
let indexes = match util::process_range(&range) {
Ok(idxs) => idxs.into(),
Err(processing_error) => {
return Err(processing_error("could not perform subbytes", call.head));
}
};
let range: Value = call.req(engine_state, stack, 0)?;
let (start, end, arg_span) = parse_range(range, call.head)?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
indexes,
let arg = Arguments {
start,
end,
arg_span,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Get a subbytes `0x[10 01]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..<4",
example: " 0x[33 44 55 10 01 13] | bytes at [3 4]",
result: Some(Value::Binary {
val: vec![0x10],
span: Span::test_data(),
}),
},
Example {
description: "Get a subbytes `0x[10 01 13]` from the bytes `0x[33 44 55 10 01 13]`",
example: " 0x[33 44 55 10 01 13] | bytes at 3..6",
description: "Alternatively, you can use the form",
example: " 0x[33 44 55 10 01 13] | bytes at '3,4'",
result: Some(Value::Binary {
val: vec![0x10, 0x01, 0x13],
val: vec![0x10],
span: Span::test_data(),
}),
},
Example {
description: "Drop the last `n` characters from the string",
example: " 0x[33 44 55 10 01 13] | bytes at ',-3'",
result: Some(Value::Binary {
val: vec![0x33, 0x44, 0x55],
span: Span::test_data(),
}),
},
Example {
description: "Get the remaining characters from a starting index",
example: " 0x[33 44 55 10 01 13] | bytes at 3..",
example: " 0x[33 44 55 10 01 13] | bytes at '3,'",
result: Some(Value::Binary {
val: vec![0x10, 0x01, 0x13],
span: Span::test_data(),
@ -111,7 +204,7 @@ impl Command for BytesAt {
},
Example {
description: "Get the characters from the beginning until ending index",
example: " 0x[33 44 55 10 01 13] | bytes at ..<4",
example: " 0x[33 44 55 10 01 13] | bytes at ',4'",
result: Some(Value::Binary {
val: vec![0x33, 0x44, 0x55, 0x10],
span: Span::test_data(),
@ -120,7 +213,7 @@ impl Command for BytesAt {
Example {
description:
"Or the characters from the beginning until ending index inside a table",
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at 1.. ColB ColC"#,
example: r#" [[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes at "1," ColB ColC"#,
result: Some(Value::List {
vals: vec![Value::Record {
cols: vec!["ColA".to_string(), "ColB".to_string(), "ColC".to_string()],
@ -147,66 +240,73 @@ impl Command for BytesAt {
}
}
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let range = &args.indexes;
match input {
Value::Binary { val, .. } => {
use std::cmp::{self, Ordering};
let len = val.len() as isize;
let start = if range.0 < 0 { range.0 + len } else { range.0 };
let end = if range.1 < 0 {
cmp::max(range.1 + len, 0)
} else {
range.1
};
if start < len && end >= 0 {
match start.cmp(&end) {
Ordering::Equal => Value::Binary {
val: vec![],
span: head,
},
Ordering::Greater => Value::Error {
error: Box::new(ShellError::TypeMismatch {
err_message: "End must be greater than or equal to Start".to_string(),
span: head,
}),
},
Ordering::Less => Value::Binary {
val: {
if end == isize::max_value() {
val.iter().skip(start as usize).copied().collect()
} else {
val.iter()
.skip(start as usize)
.take((end - start) as usize)
.copied()
.collect()
}
},
span: head,
},
}
} else {
Value::Binary {
val: vec![],
span: head,
}
}
}
Value::Error { .. } => input.clone(),
fn at(val: &Value, args: &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: Box::new(ShellError::UnsupportedInput(
"Only binary values are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
// This line requires the Value::Error match above.
other.expect_span(),
)),
error: Box::new(ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.expect_span(),
}),
},
}
}
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let len: isize = input.len() as isize;
let start: isize = if arg.start < 0 {
arg.start + len
} else {
arg.start
};
let end: isize = if arg.end < 0 {
std::cmp::max(len + arg.end, 0)
} else {
arg.end
};
if start < len && end >= 0 {
match start.cmp(&end) {
Ordering::Equal => Value::Binary { val: vec![], span },
Ordering::Greater => Value::Error {
error: Box::new(ShellError::TypeMismatch {
err_message: "End must be greater than or equal to Start".to_string(),
span: arg.arg_span,
}),
},
Ordering::Less => Value::Binary {
val: {
let input_iter = input.iter().copied().skip(start as usize);
if end == isize::max_value() {
input_iter.collect()
} else {
input_iter.take((end - start) as usize).collect()
}
},
span,
},
}
} else {
Value::Binary { val: vec![], span }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(BytesAt {})
}
}

View File

@ -49,12 +49,12 @@ impl Command for BytesLen {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Return the length of a binary",
description: "Return the lengths of multiple strings",
example: "0x[1F FF AA AB] | bytes length",
result: Some(Value::test_int(4)),
},
Example {
description: "Return the lengths of multiple binaries",
description: "Return the lengths of multiple strings",
example: "[0x[1F FF AA AB] 0x[1F]] | bytes length",
result: Some(Value::List {
vals: vec![Value::test_int(4), Value::test_int(1)],

View File

@ -240,11 +240,7 @@ mod test {
},
Value::CellPath {
val: CellPath {
members: vec![PathMember::Int {
val: 0,
span,
optional: false,
}],
members: vec![PathMember::Int { val: 0, span }],
},
span,
},

View File

@ -98,17 +98,11 @@ impl Command for Histogram {
let frequency_name_arg = call.opt::<Spanned<String>>(engine_state, stack, 1)?;
let frequency_column_name = match frequency_name_arg {
Some(inner) => {
let forbidden_column_names = ["value", "count", "quantile", "percentage"];
if forbidden_column_names.contains(&inner.item.as_str()) {
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
return Err(ShellError::TypeMismatch {
err_message: format!(
"frequency-column-name can't be {}",
forbidden_column_names
.iter()
.map(|val| format!("'{}'", val))
.collect::<Vec<_>>()
.join(", ")
),
err_message:
"frequency-column-name can't be 'value', 'count' or 'percentage'"
.to_string(),
span: inner.span,
});
}

View File

@ -105,7 +105,7 @@ impl Command for Fill {
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",
example: "1 | fill --alignment right --character 0 --width 5",
result: Some(Value::String {
val: "00001".into(),
span: Span::test_data(),
@ -113,7 +113,7 @@ impl Command for Fill {
},
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",
example: "1.1 | fill --alignment center --character 0 --width 5",
result: Some(Value::String {
val: "01.10".into(),
span: Span::test_data(),
@ -122,7 +122,7 @@ impl Command for Fill {
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",
example: "1kib | fill --alignment middle --character 0 --width 10",
result: Some(Value::String {
val: "0001024000".into(),
span: Span::test_data(),

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -25,7 +25,6 @@ impl Command for SubCommand {
SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths",
)
.category(Category::Conversions)
}
fn usage(&self) -> &str {

View File

@ -145,30 +145,6 @@ impl Command for SubCommand {
span,
}),
},
Example {
description: "Convert µs duration to the requested duration as a string",
example: "1000000µs | into duration --convert sec",
result: Some(Value::String {
val: "1 sec".to_string(),
span,
}),
},
Example {
description: "Convert duration to the µs duration as a string",
example: "1sec | into duration --convert µs",
result: Some(Value::String {
val: "1000000 µs".to_string(),
span,
}),
},
Example {
description: "Convert duration to µs as a string if unit asked for was us",
example: "1sec | into duration --convert us",
result: Some(Value::String {
val: "1000000 µs".to_string(),
span,
}),
},
]
}
}
@ -179,10 +155,7 @@ fn into_duration(
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = match input.span() {
Some(t) => t,
None => call.head,
};
let head = call.head;
let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config();
@ -191,14 +164,14 @@ fn into_duration(
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, &convert_to_unit, float_precision, span)
action(&v, &convert_to_unit, float_precision, head)
} else {
let mut ret = v;
for path in &column_paths {
let d = convert_to_unit.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &d, float_precision, span)),
Box::new(move |old| action(old, &d, float_precision, head)),
);
if let Err(error) = r {
return Value::Error {
@ -224,8 +197,6 @@ fn convert_str_from_unit_to_unit(
match (from_unit, to_unit) {
("ns", "ns") => Ok(val as f64),
("ns", "us") => Ok(val as f64 / 1000.0),
("ns", "µs") => Ok(val as f64 / 1000.0), // Micro sign
("ns", "μs") => Ok(val as f64 / 1000.0), // Greek small letter
("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
@ -240,8 +211,6 @@ fn convert_str_from_unit_to_unit(
("us", "ns") => Ok(val as f64 * 1000.0),
("us", "us") => Ok(val as f64),
("us", "µs") => Ok(val as f64), // Micro sign
("us", "μs") => Ok(val as f64), // Greek small letter
("us", "ms") => Ok(val as f64 / 1000.0),
("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
@ -252,40 +221,8 @@ fn convert_str_from_unit_to_unit(
("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
// Micro sign
("µs", "ns") => Ok(val as f64 * 1000.0),
("µs", "us") => Ok(val as f64),
("µs", "µs") => Ok(val as f64), // Micro sign
("µs", "μs") => Ok(val as f64), // Greek small letter
("µs", "ms") => Ok(val as f64 / 1000.0),
("µs", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("µs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
("µs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
("µs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("µs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("µs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("µs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("µs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
// Greek small letter
("μs", "ns") => Ok(val as f64 * 1000.0),
("μs", "us") => Ok(val as f64),
("μs", "µs") => Ok(val as f64), // Micro sign
("μs", "μs") => Ok(val as f64), // Greek small letter
("μs", "ms") => Ok(val as f64 / 1000.0),
("μs", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("μs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
("μs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
("μs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("μs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("μs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("μs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("μs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
("ms", "us") => Ok(val as f64 * 1000.0),
("ms", "µs") => Ok(val as f64 * 1000.0), // Micro sign
("ms", "μs") => Ok(val as f64 * 1000.0), // Greek small letter
("ms", "ms") => Ok(val as f64),
("ms", "sec") => Ok(val as f64 / 1000.0),
("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
@ -298,8 +235,6 @@ fn convert_str_from_unit_to_unit(
("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
("sec", "µs") => Ok(val as f64 * 1000.0 * 1000.0), // Micro sign
("sec", "μs") => Ok(val as f64 * 1000.0 * 1000.0), // Greek small letter
("sec", "ms") => Ok(val as f64 * 1000.0),
("sec", "sec") => Ok(val as f64),
("sec", "min") => Ok(val as f64 / 60.0),
@ -312,8 +247,6 @@ fn convert_str_from_unit_to_unit(
("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
("min", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Micro sign
("min", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Greek small letter
("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
("min", "sec") => Ok(val as f64 * 60.0),
("min", "min") => Ok(val as f64),
@ -326,8 +259,6 @@ fn convert_str_from_unit_to_unit(
("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Micro sign
("hr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Greek small letter
("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
("hr", "min") => Ok(val as f64 * 60.0),
@ -340,8 +271,6 @@ fn convert_str_from_unit_to_unit(
("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Micro sign
("day", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Greek small letter
("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
("day", "min") => Ok(val as f64 * 60.0 * 24.0),
@ -354,8 +283,6 @@ fn convert_str_from_unit_to_unit(
("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Micro sign
("wk", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Greek small letter
("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
@ -368,8 +295,6 @@ fn convert_str_from_unit_to_unit(
("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Micro sign
("month", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Greek small letter
("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
@ -382,8 +307,6 @@ fn convert_str_from_unit_to_unit(
("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Micro sign
("yr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Greek small letter
("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
@ -401,7 +324,7 @@ fn convert_str_from_unit_to_unit(
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec"
.to_string(),
),
}),
@ -434,8 +357,7 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
.to_string(),
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
),
})
}
@ -450,7 +372,7 @@ fn string_to_unit_duration(
if let Expr::Int(x) = value.expr {
match unit.item {
Unit::Nanosecond => return Ok(("ns", x)),
Unit::Microsecond => return Ok(("µs", x)),
Unit::Microsecond => return Ok(("us", x)),
Unit::Millisecond => return Ok(("ms", x)),
Unit::Second => return Ok(("sec", x)),
Unit::Minute => return Ok(("min", x)),
@ -471,8 +393,7 @@ fn string_to_unit_duration(
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
.to_string(),
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
),
})
}
@ -499,19 +420,14 @@ fn action(
*value_span,
) {
Ok(d) => {
let unit = if &to_unit.item == "us" {
"µs"
} else {
&to_unit.item
};
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, unit),
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, unit),
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
@ -538,19 +454,14 @@ fn action(
*value_span,
) {
Ok(d) => {
let unit = if &to_unit.item == "us" {
"µs"
} else {
&to_unit.item
};
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, unit),
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, unit),
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
@ -625,34 +536,6 @@ mod test {
assert_eq!(actual, expected);
}
#[test]
fn turns_micro_sign_s_to_duration() {
let span = Span::new(0, 2);
let word = Value::test_string("4\u{00B5}s");
let expected = Value::Duration {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
#[test]
fn turns_mu_s_to_duration() {
let span = Span::new(0, 2);
let word = Value::test_string("4\u{03BC}s");
let expected = Value::Duration {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
#[test]
fn turns_ms_to_duration() {
let span = Span::new(0, 2);

View File

@ -270,7 +270,6 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.into_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -97,7 +97,7 @@ fn command(
};
df.as_ref()
.unique(subset_slice, keep_strategy, None)
.unique(subset_slice, keep_strategy)
.map_err(|e| {
ShellError::GenericError(
"Error dropping duplicates".into(),

View File

@ -9,8 +9,8 @@ use nu_protocol::{
use std::{fs::File, io::BufReader, path::PathBuf};
use polars::prelude::{
CsvEncoding, CsvReader, IpcReader, JsonReader, LazyCsvReader, LazyFileListReader, LazyFrame,
ParallelStrategy, ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
CsvEncoding, CsvReader, IpcReader, JsonReader, LazyCsvReader, LazyFrame, ParallelStrategy,
ParquetReader, ScanArgsIpc, ScanArgsParquet, SerReader,
};
#[derive(Clone)]
@ -22,7 +22,7 @@ impl Command for OpenDataFrame {
}
fn usage(&self) -> &str {
"Opens CSV, JSON, arrow, or parquet file to create dataframe."
"Opens csv, json, arrow, or parquet file to create dataframe."
}
fn signature(&self) -> Signature {
@ -146,7 +146,6 @@ fn from_parquet(
row_count: None,
low_memory: false,
cloud_options: None,
use_statistics: false,
};
let df: NuLazyFrame = LazyFrame::scan_parquet(file, args)

View File

@ -163,8 +163,8 @@ impl SQLContext {
.collect()
.unwrap_or_default()
.schema()
.get_at_index(shm_p)
.unwrap_or((&"".into(), &DataType::Null))
.get_index(shm_p)
.unwrap_or((&"".to_string(), &DataType::Null))
.0)
})
.collect::<Vec<_>>();

View File

@ -19,7 +19,7 @@ impl Command for ToCSV {
}
fn usage(&self) -> &str {
"Saves dataframe to CSV file."
"Saves dataframe to csv file."
}
fn signature(&self) -> Signature {
@ -40,12 +40,12 @@ impl Command for ToCSV {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Saves dataframe to CSV file",
description: "Saves dataframe to csv file",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv",
result: None,
},
Example {
description: "Saves dataframe to CSV file using other delimiter",
description: "Saves dataframe to csv file using other delimiter",
example: "[[a b]; [1 2] [3 4]] | dfr into-df | dfr to-csv test.csv -d '|'",
result: None,
},

View File

@ -134,15 +134,15 @@ macro_rules! expr_command {
// Expands to a command definition for a list expression
expr_command!(
ExprList,
"dfr implode",
"dfr list",
"Aggregates a group to a Series",
vec![Example {
description: "",
example: "",
result: None,
}],
implode,
test_implode
list,
test_list
);
// ExprAggGroups command

View File

@ -36,7 +36,7 @@ impl Command for ExprLit {
example: "dfr lit 2 | dfr into-nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![Value::test_string("literal"), Value::test_string("2")],
vals: vec![Value::test_string("literal"), Value::test_string("2i64")],
span: Span::test_data(),
}),
}]

View File

@ -160,7 +160,7 @@ fn get_col_name(expr: &Expr) -> Option<String> {
| polars::prelude::AggExpr::First(e)
| polars::prelude::AggExpr::Last(e)
| polars::prelude::AggExpr::Mean(e)
| polars::prelude::AggExpr::Implode(e)
| polars::prelude::AggExpr::List(e)
| polars::prelude::AggExpr::Count(e)
| polars::prelude::AggExpr::Sum(e)
| polars::prelude::AggExpr::AggGroups(e)
@ -170,7 +170,6 @@ fn get_col_name(expr: &Expr) -> Option<String> {
},
Expr::Filter { input: expr, .. }
| Expr::Slice { input: expr, .. }
| Expr::Cache { input: expr, .. }
| Expr::Cast { expr, .. }
| Expr::Sort { expr, .. }
| Expr::Take { expr, .. }

View File

@ -98,10 +98,7 @@ fn command(
multithreaded: true,
};
let mut res = df
.as_series(call.head)?
.arg_sort(sort_options)
.into_series();
let mut res = df.as_series(call.head)?.argsort(sort_options).into_series();
res.rename("arg_sort");
NuDataFrame::try_from_series(vec![res], call.head)

View File

@ -146,7 +146,6 @@ fn command(
by: None,
closed_window: None,
tu: None,
tz: None,
};
let res = match roll_type {
RollType::Max => series.rolling_max(rolling_opts),

View File

@ -82,7 +82,7 @@ fn command(
)
})?;
let res = chunked.contains(&pattern, false).map_err(|e| {
let res = chunked.contains(&pattern).map_err(|e| {
ShellError::GenericError(
"Error searching in series".into(),
e.to_string(),

Some files were not shown because too many files have changed in this diff Show More