Compare commits

...

96 Commits

Author SHA1 Message Date
9a56665c6b Commit the lockfile for 0.74 (#7719)
Was missed in #7718
2023-01-10 21:12:41 +01:00
8044fb2db0 Bump version to 0.74.0 (#7718)
Preparing the release
2023-01-10 20:58:13 +01:00
3a59ab9f14 Improve wording of str replace help messages (#7708)
# Description

See title.

# User-Facing Changes

See title.

# 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

# 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-01-10 20:46:50 +01:00
f609a4f26a Auto-Completion: put ` tildes around filenames with parentheses (#7712)
# Description

Fixes: #7706

# User-Facing Changes


![img](https://user-images.githubusercontent.com/22256154/211286663-3d07a650-5e2d-406e-99f6-cff90dba352b.png)


# 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

# 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.

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-10 20:41:54 +01:00
80463d12fb Revert "Primitives now use color closures..." (#7710)
This temporarily reverts commit c5639cd9fa
(PR https://github.com/nushell/nushell/pull/7650). See
[here](https://github.com/nushell/nushell/pull/7650#issuecomment-1375036213)
for details; the PR is accidentally adding ANSI escape codes to strings
piped to externals.

I think we should revert the PR because we're only 1-2 days away from a
release; reverting it will give us more time to land+test a proper fix
in the next release cycle.
2023-01-08 21:53:52 -08:00
cef05d3553 Fix line-end trimming in subexpression (#7543)
# Description

Currently the implementation is different for Windows and Unix.

Thus certain operations will fail if the platform foreign line ending is
used:

example failing under windows

```
git show (git merge-base main HEAD)
```

Temporary cheat is to strip all `\r` and `\n` from the end. Proper
solution should trim them as correct patterns.

Also needed: test of behavior with both platform newline and
platform-foreign line endings

cc @WindSoilder 


# User-Facing Changes

Line endings should be trimmed no matter the source and no matter the
platform

# Tests + Formatting

Still missing
2023-01-08 22:51:51 +01:00
5879b0df99 clean up some extra logging code in the cli (#7709)
I have been recently going through some info logging in the cli and
noticed that there is too much info being printed to get a handle on
whats going on...

This is an attempt to do some minor logging clean up to print out "less
stuff",
in info logging mode mainly having to do with the prompt...

If someone really want to see what is going on they can very easily add
it
back in without too much trouble.
2023-01-08 15:05:46 -05:00
95cd9dd2b2 move BufferedReader out of nu-command (#7697)
src/main.rs has a dependency on BufferedReader
which is currently located in nu_command.

I am moving BufferedReader to a more relevant
location (crate) which will allow / eliminate main's dependency
on nu_command in a benchmark / testing environment...

now that @rgwood  has landed benches I want
to start experimenting with benchmarks related
to the parser.

For benchmark purposes when dealing with parsing
you need a very simple set of commands that show
how well the parser is doing, in other words
just the core commands... Not all of nu_command...

Having a smaller nu binary when running the benchmark CI
would enable building nushell quickly, yet still show us
how well the parser is performing...

Once this PR lands the only dependency main will have
on nu_command is create_default_context ---
meaning for benchmark purposes we can swap in a tiny
crate of commands instead of the gigantic nu_command
which has its "own" create_default_context...

It will also enable other crates going forward to
use BufferedReader.  Right now it is not accessible
to other lower level crates because it is located in a
"top of the stack crate".
2023-01-06 15:22:17 -08:00
424d5611a5 Bump tokio from 1.21.2 to 1.24.1 (#7701)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.21.2 to 1.24.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.24.1</h2>
<p>This release fixes a compilation failure on targets without
<code>AtomicU64</code> when using rustc older than 1.63. (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5356">#5356</a>)</p>
<p><a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5356">#5356</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5356">tokio-rs/tokio#5356</a></p>
<h2>Tokio v1.24.0</h2>
<p>The highlight of this release is the reduction of lock contention for
all I/O operations (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5300">#5300</a>).
We have received reports of up to a 20% improvement in CPU utilization
and increased throughput for real-world I/O heavy applications.</p>
<h3>Fixed</h3>
<ul>
<li>rt: improve native <code>AtomicU64</code> support detection (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5284">#5284</a>)</li>
</ul>
<h3>Added</h3>
<ul>
<li>rt: add configuration option for max number of I/O events polled
from the OS
per tick (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5186">#5186</a>)</li>
<li>rt: add an environment variable for configuring the default number
of worker
threads per runtime instance (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/4250">#4250</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>sync: reduce MPSC channel stack usage (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5294">#5294</a>)</li>
<li>io: reduce lock contention in I/O operations (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5300">#5300</a>)</li>
<li>fs: speed up <code>read_dir()</code> by chunking operations (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5309">#5309</a>)</li>
<li>rt: use internal <code>ThreadId</code> implementation (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5329">#5329</a>)</li>
<li>test: don't auto-advance time when a <code>spawn_blocking</code>
task is running (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5115">#5115</a>)</li>
</ul>
<p><a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5186">#5186</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5186">tokio-rs/tokio#5186</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5294">#5294</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5294">tokio-rs/tokio#5294</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5284">#5284</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5284">tokio-rs/tokio#5284</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/4250">#4250</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/4250">tokio-rs/tokio#4250</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5300">#5300</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5300">tokio-rs/tokio#5300</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5329">#5329</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5329">tokio-rs/tokio#5329</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5115">#5115</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5115">tokio-rs/tokio#5115</a>
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5309">#5309</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5309">tokio-rs/tokio#5309</a></p>
<h2>Tokio v1.23.1</h2>
<p>This release forward ports changes from 1.18.4.</p>
<h3>Fixed</h3>
<ul>
<li>net: fix Windows named pipe server builder to maintain option when
toggling
pipe mode (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5336">#5336</a>).</li>
</ul>
<p><a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5336">#5336</a>:
<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/pull/5336">tokio-rs/tokio#5336</a></p>
<h2>Tokio v1.23.0</h2>
<h3>Fixed</h3>
<ul>
<li>net: fix Windows named pipe connect (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5208">#5208</a>)</li>
<li>io: support vectored writes for <code>ChildStdin</code> (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5216">#5216</a>)</li>
<li>io: fix <code>async fn ready()</code> false positive for OS-specific
events (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5231">#5231</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="31c7e82919"><code>31c7e82</code></a>
chore: prepare Tokio v1.24.1 (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5357">#5357</a>)</li>
<li><a
href="8d8db27442"><code>8d8db27</code></a>
tokio: add load and compare_exchange_weak to loom StaticAtomicU64 (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5356">#5356</a>)</li>
<li><a
href="dfe252d1fa"><code>dfe252d</code></a>
chore: prepare Tokio v1.24.0 release (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5353">#5353</a>)</li>
<li><a
href="21b233fa9c"><code>21b233f</code></a>
test: bump version of async-stream (<a
href="https://github-redirect.dependabot.com/tokio-rs/tokio/issues/5347">#5347</a>)</li>
<li><a
href="72993044e6"><code>7299304</code></a>
Merge branch 'tokio-1.23.x' into master</li>
<li><a
href="1a997ffbd6"><code>1a997ff</code></a>
chore: prepare Tokio v1.23.1 release</li>
<li><a
href="a8fe333cc4"><code>a8fe333</code></a>
Merge branch 'tokio-1.20.x' into tokio-1.23.x</li>
<li><a
href="ba81945ffc"><code>ba81945</code></a>
chore: prepare Tokio 1.20.3 release</li>
<li><a
href="763bdc967e"><code>763bdc9</code></a>
ci: run WASI tasks using latest Rust</li>
<li><a
href="9f98535877"><code>9f98535</code></a>
Merge remote-tracking branch 'origin/tokio-1.18.x' into
fix-named-pipes-1.20</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.21.2...tokio-1.24.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio&package-manager=cargo&previous-version=1.21.2&new-version=1.24.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/nushell/nushell/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-06 16:37:37 -06:00
9bff68a4f6 align durations to the right (#7700)
# Description

This PR aligns durations to the right side versus the left.
Before this PR

![image](https://user-images.githubusercontent.com/343840/211092575-2199f4ce-7972-4726-a243-5499e656fb46.png)

After this PR

![image](https://user-images.githubusercontent.com/343840/211092601-ff63ecd2-9710-4e5f-8c32-85476f4b7110.png)


# 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

# 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-01-06 16:36:59 -06:00
a9bdc655c1 Add benchmarks for evaluating default env+config (#7688)
A quick follow-up to https://github.com/nushell/nushell/pull/7686. This
adds benchmarks for evaluating `default_env.nu` and `default_config.nu`,
because evaluating config takes up the lion's share of Nushell's startup
time. The benchmarks will help us speed up Nu's startup and test
execution.

```
eval default_env.nu     time:   [4.2417 ms 4.2596 ms 4.2780 ms]
...
eval default_config.nu  time:   [1.9362 ms 1.9439 ms 1.9523 ms]
```
2023-01-05 14:14:58 -08:00
9b617de6f0 Continue and Break on Try/Catch (#7683)
Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
Fixes https://github.com/nushell/nushell/issues/7656
2023-01-05 21:41:51 +01:00
771270d526 Add Criterion benchmarks for parser (#7686)
This PR sets up [Criterion](https://github.com/bheisler/criterion.rs)
for benchmarking in the main `nu` crate, and adds some simple parser
benchmarks.

To run the benchmarks, just do `cargo bench` or `cargo bench -- <regex
matching benchmark names>` in the repo root:

```bash
〉cargo bench -- parse
...
     Running benches/parser_benchmark.rs (target/release/deps/parser_benchmark-75d224bac82d5b0b)
parse_default_env_file  time:   [221.17 µs 222.34 µs 223.61 µs]
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe

parse_default_config_file
                        time:   [1.4935 ms 1.4993 ms 1.5059 ms]
Found 11 outliers among 100 measurements (11.00%)
  7 (7.00%) high mild
  4 (4.00%) high severe
```

Existing benchmarks from `nu-plugin` have been moved into the main `nu`
crate to keep all our benchmarks in one place.
2023-01-05 11:39:54 -08:00
26d1307476 Url encode to escape special characters (#7664)
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-05 19:24:38 +01:00
86707b9972 Remove environment variable hiding from hide (#7687) 2023-01-05 20:08:43 +02:00
52cb865c5c Upgrade all remaining crates to Rust 2021 (#7681)
2 crates were still using Rust 2018, including the base `nu` crate. This
PR upgrades them to Rust 2021. If you're aware of any reason why this is
a bad idea, please speak now or forever hold your peace.

## Context

I was moving benchmarks from `nu-plugin` to the base crate and couldn't
figure out why they wouldn't compile. Turns out the benchmarks rely on
some Rust 2021 features. Would be nice to have everything on 2021.
2023-01-05 06:24:42 -06:00
3ea027a136 Make user parameter optional in fetch (#7680)
# Description

This commit makes the `user` parameter optional in the `fetch` command.
Previously when attempting to _only_ pass a `password`, the command
would ignore authentication. Now when a `user` is not supplied, but a
`password` is, an empty user is implied.

Before this PR, consider the following:
```nushell
fetch -password "mypassword" $url
```
This would result in the `password` parameter being ignored entirely.

Now, with changes made in this PR, consider the same code snippet as
above. The following HTTP header will be used:
```
Authentication: Basic <base64_encode(":{password}")>
```
Note that the `user` field is implied as empty if one is not supplied
when `password` is.

# User-Facing Changes

* `fetch` now supports `password`-only authentication, using an empty
`user` if one is not supplied.

# 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

# 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-01-04 19:57:56 -08:00
00469de93e Limit recursion to avoid stack overflow (#7657)
Add recursion limit to `def` and `block`.
Summary of this PR , it will detect if `def` call itself or not .
Then execute by using `stack` which I think best choice to use with this
design and core as it is available in all crates and mutable and
calculate the recursion limit on calling `def`.
Set 50 as recursion limit on `Config`.
Add some tests too .

Fixes #5899

Co-authored-by: Reilly Wood <reilly.wood@icloud.com>
2023-01-04 18:38:50 -08:00
9bc4e6794d Remove math eval command (#7284)
Reasoning: 

Most missing math commands are implemented with #7258.
The `meval` crate itself declares that it doesn't strive to stringent
standards (https://docs.rs/meval/latest/meval/#related-projects).
For example no particular special casing or transformations are
performed to ensure numerical stability. It uses the same rust `std`
library functions we use or have access to (and `f64`).
While the command call syntax in nushell may be a bit more verbose,
having a single source of truth and common commands is beneficial.
Furthermore the `math` commands can themselves implement broadcasting
over lists (or table columns).

Closes #7073

Removed dependencies:
- `meval`
- `nom 1.2.4` (duplicate)

User-Facing Changes:

Scripts using `math eval` will break. 
We remove a further `eval` like behavior to get results through runtime evaluation (albeit limited in scope)

Tests:

- Updated tests that internally used `math eval`.
- Removed one test that primarily used `math eval` to obtain a result from `str join`
2023-01-04 23:50:18 +01:00
429127793f [Chore] cleanup in where implementation (#7679)
- Remove commented out example that is unnecessary after #7365
- remove unnecessary closure map
2023-01-04 22:50:02 +01:00
75cb3fcc5f uniq and uniq-by optimization (#7477) (#7534)
# Description

Refactored the quadratic complexity on `uniq` to use a HashMap, as key I
converted the Value to string.
I tried to use the HashableValue, but it looks it is not very developed
yet and it was getting more complex and difficult.

This improves performance on large data sets.

Fixes https://github.com/nushell/nushell/issues/7477


# Tests + Formatting
```
> let data = fetch "https://home.treasury.gov/system/files/276/yield-curve-rates-1990-2021.csv"
> $data | uniq
```

it keeps original attribute order in Records:
```
> [ {b:2, a:1} {a:1, b:2} ] | uniq 
╭───┬───┬───╮
│ # │ b │ a │
├───┼───┼───┤
│ 0 │ 2 │ 1 │
╰───┴───┴───╯
```
2023-01-04 11:35:49 -08:00
f0e87da830 fix register-plugins script (#7677)
# Description

The register-plugins.nu script was broken on Windows where it was trying
to register files that ended in .d. Hopefully this will fix it once and
for all.

# User-Facing 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

# 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-01-04 11:22:54 -06:00
c5639cd9fa Primitives now use color closures when printed on the command line (#7650)
# Description

Closes #7554


![image](https://user-images.githubusercontent.com/83939/210177700-4890fcf2-1be9-4da9-9974-58d4ed403430.png)

# User-Facing Changes

See above.

# 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

# 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.

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>
2023-01-03 23:59:10 -08:00
95d4922e44 Make stream info visible to users in describe (#7589)
Closes #7581.

After this PR, `describe` shows `(stream)` next to input that arrived at
`describe` as a `ListStream`:
```bash
〉ls | describe
table<name: string, type: string, size: filesize, modified: date> (stream)
〉[1 2 3] | each {|i| $i} | describe
list<int> (stream)
```

`describe` must collect all items of the stream to display type
information for lists and tables. If users need to avoid collecting
input, they can use the `-n`/`--no-collect` flag:

```bash
〉[1 2 3] | each {|i| $i} | describe --no-collect
stream
```
2023-01-03 21:08:05 -08:00
bdd52f0111 Fix build-all-windows.cmd (#7674)
# Description

Fixed a minor scripting error 😀

Change a command in `build-all-windows.cmd`
`cargo build cargo build --features=dataframe` → `cargo build
--features=dataframe`


# User-Facing Changes

No

# Tests + Formatting

Yes
2023-01-03 18:31:36 -08:00
7bd07cb351 Reorder flags in nu --help (#7672)
The ordering of flags in `nu --help` was a bit of a mess; I think it
grew organically over time. Related commands (like
`--config`/`--env-config`/`--plugin-config`) weren't grouped together,
common flags weren't near the top, and we weren't following alphabetical
ordering.

### Before

```bash
Flags:
  -h, --help - Display the help message for this command
  --stdin - redirect standard input to a command (with `-c`) or a script file
  -l, --login - start as a login shell
  -i, --interactive - start as an interactive shell
  -v, --version - print the version
  --testbin <String> - run internal test binary
  -c, --commands <String> - run the given commands and then exit
  --config <String> - start with an alternate config file
  --env-config <String> - start with an alternate environment config file
  --log-level <String> - log level for diagnostic logs (error, warn, info, debug, trace). Off by default
  --log-target <String> - set the target for the log to output. stdout, stderr(default), mixed or file
  -e, --execute <String> - run the given commands and then enter an interactive shell
  -t, --threads <Int> - threads to use for parallel commands
  -m, --table-mode <String> - the table mode to use. rounded is default.
  --plugin-config <String> - start with an alternate plugin signature file
```

### After

```bash
Flags:
  -h, --help - Display the help message for this command
  -c, --commands <String> - run the given commands and then exit
  -e, --execute <String> - run the given commands and then enter an interactive shell
  -i, --interactive - start as an interactive shell
  -l, --login - start as a login shell
  -m, --table-mode <String> - the table mode to use. rounded is default.
  -t, --threads <Int> - threads to use for parallel commands
  -v, --version - print the version
  --config <String> - start with an alternate config file
  --env-config <String> - start with an alternate environment config file
  --plugin-config <String> - start with an alternate plugin signature file
  --log-level <String> - log level for diagnostic logs (error, warn, info, debug, trace). Off by default
  --log-target <String> - set the target for the log to output. stdout, stderr(default), mixed or file
  --stdin - redirect standard input to a command (with `-c`) or a script file
  --testbin <String> - run internal test binary
```

The new ordering:

1. Groups commands with short flags together, sorted alphabetically by
short flag
1. Groups commands with only long flags together, sorted alphabetically
(with the exception of `--plugin-config` so we can keep related flags
together)

Conveniently, this puts the very commonly used `-c` at the top and the
very rarely used `--testbin` at the bottom.
2023-01-03 16:18:37 -08:00
249afc5df4 Clarify url base command (#7670)
I noticed that the help for the `url` command was confusing (it wasn't
clear that `url` is just a base command that does nothing itself) and
the input type was also wrong. Fixed.

Before:
```bash
〉help url
Apply url function.

Search terms: network, parse

Usage:
  > url 

Subcommands:
  url parse - Parses a url

Flags:
  -h, --help - Display the help message for this command

Signatures:
  <string> | url -> <string>
```

After:
```bash
〉help url
Various commands for working with URLs

You must use one of the following subcommands. Using this command as-is will only produce this help message.

Search terms: network, parse

Usage:
  > url 

Subcommands:
  url parse - Parses a url

Flags:
  -h, --help - Display the help message for this command

Signatures:
  <nothing> | url -> <string>
  ```
2023-01-03 15:49:43 -08:00
d7af461173 Delete unused files (#7668)
Just tidying up a bit, deleting the unused files in `crates/old`. The
files can still be accessed in source control history if anyone needs
them.
2023-01-03 13:03:05 -08:00
b17e9f4ed0 Extend config support from F1-F12 to F1-F20, #7666 (#7669)
Co-authored-by: Piotr Meyer <aniou@smutek.pl>
2023-01-03 22:00:21 +01:00
6862734580 let start open anything and everything (#7580)
# Description

Fixes #7546 's request. I'm unsure, so hopefully someone in charge of
design can chip in.

# User-Facing Changes

`open` now opens directories in the default file manager.

# 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

# 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-01-03 10:47:37 -08:00
9e1f645428 Fix save error handling (#7608)
Closes #5178

Modularizes `save` implementation
2023-01-03 14:22:28 +01:00
c4818d79f3 adding link to list of nu-plugins (#7649)
Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2023-01-03 13:50:21 +01:00
d1a78a58cd revert changes on prepend and append (#7660)
# Description

#7623 causes a break on PATH convertion, this pr is going to revert
`prepend` and `append` bahavior.

# 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

# 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-01-02 17:09:55 -08:00
65d0b5b9d9 Make get hole errors and cell path hole errors identical (improvement on #7002) (#7647)
# Description

This closes #7498, as well as fixes an issue reported in
https://github.com/nushell/nushell/pull/7002#issuecomment-1368340773

BEFORE:
```
〉[{foo: 'bar'} {}] | get foo
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #5:1:1]
 1 │ [{foo: 'bar'} {}] | get foo
   · ────────┬────────   ─┬─
   ·         │            ╰── value originates here
   ·         ╰── cannot find column 'Empty cell'
   ╰────

〉[{foo: 'bar'} {}].foo
╭───┬─────╮
│ 0 │ bar │
│ 1 │     │
╰───┴─────╯
```
AFTER:
```
〉[{foo: 'bar'} {}] | get foo
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #1:1:1]
 1 │ [{foo: 'bar'} {}] | get foo
   ·               ─┬        ─┬─
   ·                │         ╰── cannot find column 'foo'
   ·                ╰── value originates here
   ╰────

〉[{foo: 'bar'} {}].foo
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #3:1:1]
 1 │ [{foo: 'bar'} {}].foo
   ·               ─┬  ─┬─
   ·                │   ╰── cannot find column 'foo'
   ·                ╰── value originates here       
   ╰────
```

EDIT: This also changes the semantics of `get`/`select` `-i` somewhat.
I've decided to leave it like this because it works more intuitively
with `default` and `compact`.
BEFORE:
```
〉[{a:1} {b:2} {a:3}] | select -i foo | to nuon
null
```
AFTER:
```
〉[{a:1} {b:2} {a:3}] | select -i foo | to nuon
[[foo]; [null], [null], [null]]
```

# User-Facing Changes

See above. EDIT: the issue with holes in cases like ` [{foo: 'bar'}
{}].foo.0` versus ` [{foo: 'bar'} {}].0.foo` has been resolved.

# 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

# 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-01-02 14:45:43 -08:00
614bc2a943 early return for parsing closure and block with interchanged shape (#7618) 2023-01-01 12:26:51 +02:00
27b06358ea Tweak new input type error message (#7646)
A tiny follow-up from https://github.com/nushell/nushell/pull/7623,
changes "Only supports for specific input types" to "Input type not
supported"

Before:

```
〉"asdf" | append "foo"
Error: nu:🐚:only_supports_this_input_type (link)

  × Only supports for specific input types.
   ╭─[entry #2:1:1]
 1 │ "asdf" | append "foo"
   · ───┬──   ───┬──
   ·    │        ╰── only list, binary, raw data or range input data is supported
   ·    ╰── input type: string
   ╰────
```
   
After:
```
〉"asdf" | append "foo"
Error: nu:🐚:only_supports_this_input_type (link)

  × Input type not supported.
   ╭─[entry #2:1:1]
 1 │ "asdf" | append "foo"
   · ───┬──   ───┬──
   ·    │        ╰── only list, binary, raw data or range input data is supported
   ·    ╰── input type: string
   ╰────
```
2022-12-31 21:56:59 -08:00
e56c01d0e2 Simplify register-plugins.nu (#7636) 2022-12-31 18:32:34 -06:00
ececca7ad2 fix: ci problem (#7643) 2022-12-31 14:00:35 +02:00
e9cc417fd5 last, skip, drop, take until, take while, skip until, skip while, where, reverse, shuffle, append, prepend and sort-by raise error when given non-lists (#7623)
Closes https://github.com/nushell/nushell/issues/6941
2022-12-31 13:35:12 +02:00
81a7d17b33 return Error if get meet nothing and without "i" (#7002) 2022-12-31 13:27:09 +02:00
9382dd6d55 fix: empty cell in select (#7639) 2022-12-31 13:19:10 +02:00
7aa2a57434 def: make various punctuation misuses into errors (#7624)
Closes https://github.com/nushell/nushell/issues/7604
2022-12-31 13:18:53 +02:00
9b88ea5b60 Try to use the latest tagged virtualenv (#7638) 2022-12-31 12:26:01 +02:00
8bfcea8054 Expand Nushell's help system (#7611) 2022-12-30 17:44:37 +02:00
f3d2be7a56 doc: correct some really tiny typos. (#7635) 2022-12-30 13:18:28 +01:00
be31182969 Fix quoting of empty string in to nuon (#7632)
Closes https://github.com/nushell/nushell/issues/7631
2022-12-30 09:49:35 +01:00
b543063749 Fix the syntax highlighting in help metadata (#7628) 2022-12-29 17:45:55 +01:00
ce0060e6b0 Update Cargo.lock to powierza-coefficient 1.0.2 (#7629)
Follow-up to #7625
2022-12-29 17:40:11 +01:00
35b12fe5ec Fix usage of deprecated C-style logical and (#7627)
n untested examples we still had `&&`
2022-12-29 16:47:33 +01:00
6ac26094da Slight edits to ls and zip's help text (#7626) 2022-12-29 16:24:08 +01:00
8c6a0f68d4 Update powierza-coefficient to 1.0.2 (#7625) 2022-12-29 15:55:00 +01:00
f5d6672ccf Disallow ^ in def command names (#7606)
# Description

Closes #7273.

Also slightly edits/tidies up parser.rs.

# User-Facing Changes

`^` is now forbidden in `def` and `def-env` command names. EDIT: also
`alias`.

# 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

# 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.
2022-12-27 15:00:44 -08:00
db06edc5d3 add --mime-type(-m) to ls in the type column (#7616)
# Description

This PR adds the `mime-type` to the `type` column if you add the
`--mime-type(-m)` flag to `ls`.
<img width="853" alt="Screenshot 2022-12-27 at 11 43 20 AM"
src="https://user-images.githubusercontent.com/343840/209705499-27fe40fe-0356-4d9d-97f2-4b2dc52e0963.png">

<img width="781" alt="Screenshot 2022-12-27 at 11 45 53 AM"
src="https://user-images.githubusercontent.com/343840/209705509-4d677389-fd68-401e-a7af-3fc6052743b6.png">

# User-Facing Changes

If you specify the `-m` flag, you get the "guessed at" mime type. The
guess is based on the file name and uses this crate
https://docs.rs/mime_guess/latest/mime_guess/ for the guessing.

Part of issue #7612 and and #7524

There's some debate on if the `mime-type` should be added to the `type`
column or if there should be a separate `mime` column. I tend to lean on
the side of `type` since it's technically a type and it's only in that
column if you ask it to be there. Also, I'd prefer to reuse a column
rather than having a list of sprawling columns. Also, as @KodiCraft
suggested, there is precedence as with `ls -d` where the summed size is
in the size column.

I could go either way and if someone wants to create a `mime` column,
we'd probably accept it.

# 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

# 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.
2022-12-27 12:46:23 -06:00
568927349d Fix and Allow Number and Boolean type to be key in from yaml (#7607)
Fix and Allow Number and Boolean type to be key in Yaml .

For example : 
`"200 : " | from yaml` not allowed because of Number key type.

PR allow , we can use Boolean and Number for key. 
For example :
`"true : false" | from yaml`
`"5050 : it is number" | from yaml`

Fixes #7222 .
2022-12-27 08:28:24 -08:00
4f812a7f34 Fix table expand wrap in case no header is there (#7605)
ref #7598

To be honest I was not able to obtain such results in basic mode as you
@rgwood.
But I've got it in `table -e`.

So this must fix the `table -e` wrapping.

Could you verify if it got fixed?

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-27 07:44:34 -06:00
38fc42d352 Fix const examples (#7610)
# Description

Fix const examples

# 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
2022-12-27 15:44:03 +08:00
b4c5693ac6 Fix an example of env command (#7603)
# Description

Fix an example of `env` command

# 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

# 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.
2022-12-26 16:40:34 +08:00
79000aa5e0 Fix typos by codespell (#7600)
# Description

Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`

# User-Facing Changes

None.

# Tests + Formatting

None and done.

# After Submitting

None.
2022-12-26 02:31:26 -05:00
2415381682 fix python plugin example (#7599)
# 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.)_

# 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

# 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.
2022-12-24 18:31:44 -06:00
b499e7c682 Fix some issues with table wrapping of lists (#7598)
close #7591 

I tend to think it must be addressed.
But I'd verify it @rgwood.

PS: I've noticed how `table -e` and `table` with the same width wraps a
bit differently sometimes. (I guess it also must be addressed......)

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-24 18:27:34 -06:00
d8cde2ae89 Include clippy check for dataframe in CI (#7596) 2022-12-24 22:44:52 +01:00
ddc00014be To toml fix (#7597)
# Description

Fixes #7510 .
Remove support for tables from `to toml` command and update description.
Previously, as indicated in #7510 , a table could be converted to toml
and would result in this invalid toml:


![image](https://user-images.githubusercontent.com/17511668/209443930-c3dd3a3f-5ffd-4273-9c10-acbb345c788e.png)

This commit removes functionality of serializing tables and now `to
toml` produces an error:


![image](https://user-images.githubusercontent.com/17511668/209443975-be119465-8946-4644-8994-489ca94f6006.png)

The `from toml` command already acknowledges the fact that toml can
contain only records as indicated in its signature


![image](https://user-images.githubusercontent.com/17511668/209443995-1590d044-a790-4be3-a967-b26292a6e70c.png)

Now help of `to toml` reflects this feature of format as well:


![image](https://user-images.githubusercontent.com/17511668/209444014-7cfe8f8e-ad8a-4845-a151-24df6b99a1a2.png)

Additionally new tests were created for `to toml` command. See
`crates\nu-command\tests\format_conversions\toml.rs`.

Also removed undocumented behavior that would accept and validate a
string as toml:


![image](https://user-images.githubusercontent.com/17511668/209449482-5d876074-fc5b-4b21-b8a5-64e643a50083.png)


# User-Facing Changes

- Serializing tables to toml now produces error instead of invalid toml
- Updated `to toml` help
- Remove undocumented "validation" (not really user-facing)

# Tests + Formatting

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

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

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

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
2022-12-24 15:12:09 -06:00
9ffa3e55c2 Fix #6888 and rename fill-na to fill-nan (#7565)
Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
Fixes https://github.com/nushell/nushell/issues/6888
2022-12-24 16:04:46 +01:00
45fe3be83e Further cleanup of Span::test_data usage + span fixes (#7595)
# Description

Inspired by #7592

For brevity use `Value::test_{string,int,float,bool}`

Includes fixes to commands that were abusing `Span::test_data` in their
implementation. Now the call span is used where possible or the explicit
`Span::unknonw` is used.

## Command fixes
- Fix abuse of `Span::test_data()` in `query_xml`
- Fix abuse of `Span::test_data()` in `term size`
- Fix abuse of `Span::test_data()` in `seq date`
- Fix two abuses of `Span::test_data` in `nu-cli`
- Change `Span::test_data` to `Span::unknown` in `keybindings listen`
- Add proper call span to `registry query`
- Fix span use in `nu_plugin_query`
- Fix span assignment in `select`
- Use `Span::unknown` instead of `test_data` in more places

## Other
- Use `Value::test_int`/`test_float()` consistently
- More `test_string` and `test_bool`
- Fix unused imports


# User-Facing Changes

Some commands may now provide more helpful spans for downstream use in
errors
2022-12-24 07:41:57 -06:00
dd6fe6a04a Add extra_usage messages for subcommand-only commands (#7594)
# Description

The message reads "You must use one of the following subcommands. Using
this command as-is will only produce this help message." and is added to
commands like `into`, `bytes`, `str`, etc.

# User-Facing Changes

See above.

# 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

# 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.
2022-12-24 07:16:29 -06:00
e76b38882c columns now errors when given a non-record non-table (#7593)
# Description

This is for consistency with the new `values` command. Previously it
would return a completely empty record (??!) when given an
incorrectly-typed value.

# User-Facing Changes

See title.

# 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

# 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.
2022-12-24 07:08:25 -06:00
11bdab7e61 Change instances of Value::string("foo", Span::test_data()) to Value::test_string("foo") (#7592) 2022-12-24 10:25:38 +01:00
3d682fe957 Fix error message when interrupting table with ctrl+c (#7588)
`table` was displaying an incorrect "Couldn't fit table into X columns!"
error when streaming was interrupted by `ctrl+c`:

![image](https://user-images.githubusercontent.com/26268125/209415204-cc20964b-fc43-42a0-867f-1b01cefb3213.png)

This PR fixes that:

![image](https://user-images.githubusercontent.com/26268125/209415163-b3041357-7f16-4a17-b15a-170b4d50f5ee.png)
2022-12-23 16:38:38 -08:00
a43e66ef92 Add LRU regex cache (#7587)
Closes #7572 by adding a cache for compiled regexes of type
`Arc<Mutex<LruCache<String, Regex>>>` to `EngineState` .

The cache is limited to 100 entries (limit chosen arbitrarily) and
evicts least-recently-used items first.

This PR makes a noticeable difference when using regexes for
`color_config`, e.g.:
```bash
#first set string formatting in config.nu like:
string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }`

# then try displaying and exploring a table with many strings
# this is instant after the PR, but takes hundreds of milliseconds before
['#ff0033', '#0025ee', '#0087aa', 'string', '#4101ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff', '#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff','#ff0033', '#0025ee', '#0087aa', 'string', '#6103ff']
```

## New dependency (`lru`)
This uses [the popular `lru` crate](https://lib.rs/crates/lru). The new
dependency adds 19.8KB to a Linux release build of Nushell. I think this
is OK, especially since the crate can be useful elsewhere in Nu.
2022-12-23 14:30:04 -08:00
3be7996e79 add metadata to wrap (#7586)
# Description

This PR allows `wrap` to pass through metadata.

# User-Facing Changes

This change allows this:
<img width="789" alt="Screenshot 2022-12-23 at 3 12 37 PM"
src="https://user-images.githubusercontent.com/343840/209406010-1da9b814-1892-4961-bb01-9f88ddc83474.png">
Instead of this:
<img width="786" alt="Screenshot 2022-12-23 at 3 12 48 PM"
src="https://user-images.githubusercontent.com/343840/209406021-6e5eb860-0911-42c4-a39e-5fe76c61af03.png">

Strangely enough, this command doesn't result in LS_COLORS `(ls |
values).0 | wrap name`

/cc @webbedspace - we were talking about LS_COLORS in `values` earlier.

# 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

# 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.
2022-12-23 15:56:28 -06:00
5041a4ffa3 Re-enable test_bits (#7585)
The tests in `src/tests/test_bits.rs` weren't being run because we were
missing a `mod test_bits;`. Fixed.
2022-12-23 11:19:10 -08:00
b16b3c0b7f Add values command (see #7166) (#7583) 2022-12-23 12:49:19 -06:00
852ec3f9a0 Fix signatures of commands which accept records also (#7582)
# Description

Certain commands that operate on tables also work on bare records, but
their type sig didn't reflect that. This corrects this.

I did not fix certain commands which, I feel, currently give unintended
behaviour when given plain records. These are `sort-by` and `uniq-by`.

Also corrected the wording of some stuff in headers.rs, and removed a
wrong comment in insert.rs.

# User-Facing Changes

See above.

# 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

# 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.
2022-12-23 07:38:37 -06:00
dd7b7311b3 Standardise the use of ShellError::UnsupportedInput and ShellError::TypeMismatch and add spans to every instance of the former (#7217)
# Description

* I was dismayed to discover recently that UnsupportedInput and
TypeMismatch are used *extremely* inconsistently across the codebase.
UnsupportedInput is sometimes used for input type-checks (as per the
name!!), but *also* used for argument type-checks. TypeMismatch is also
used for both.
I thus devised the following standard: input type-checking *only* uses
UnsupportedInput, and argument type-checking *only* uses TypeMismatch.
Moreover, to differentiate them, UnsupportedInput now has *two* error
arrows (spans), one pointing at the command and the other at the input
origin, while TypeMismatch only has the one (because the command should
always be nearby)
* In order to apply that standard, a very large number of
UnsupportedInput uses were changed so that the input's span could be
retrieved and delivered to it.
* Additionally, I noticed many places where **errors are not propagated
correctly**: there are lots of `match` sites which take a Value::Error,
then throw it away and replace it with a new Value::Error with
less/misleading information (such as reporting the error as an
"incorrect type"). I believe that the earliest errors are the most
important, and should always be propagated where possible.
* Also, to standardise one broad subset of UnsupportedInput error
messages, who all used slightly different wordings of "expected
`<type>`, got `<type>`", I created OnlySupportsThisInputType as a
variant of it.
* Finally, a bunch of error sites that had "repeated spans" - i.e. where
an error expected two spans, but `call.head` was given for both - were
fixed to use different spans.

# Example
BEFORE
```
〉20b | str starts-with 'a'
Error: nu:🐚:unsupported_input (link)

  × Unsupported input
   ╭─[entry #31:1:1]
 1 │ 20b | str starts-with 'a'
   ·   ┬
   ·   ╰── Input's type is filesize. This command only works with strings.
   ╰────

〉'a' | math cos
Error: nu:🐚:unsupported_input (link)

  × Unsupported input
   ╭─[entry #33:1:1]
 1 │ 'a' | math cos
   · ─┬─
   ·  ╰── Only numerical values are supported, input type: String
   ╰────

〉0x[12] | encode utf8
Error: nu:🐚:unsupported_input (link)

  × Unsupported input
   ╭─[entry #38:1:1]
 1 │ 0x[12] | encode utf8
   ·          ───┬──
   ·             ╰── non-string input
   ╰────
```
AFTER
```
〉20b | str starts-with 'a'
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #1:1:1]
 1 │ 20b | str starts-with 'a'
   ·   ┬   ───────┬───────
   ·   │          ╰── only string input data is supported
   ·   ╰── input type: filesize
   ╰────

〉'a' | math cos
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #2:1:1]
 1 │ 'a' | math cos
   · ─┬─   ────┬───
   ·  │        ╰── only numeric input data is supported
   ·  ╰── input type: string
   ╰────

〉0x[12] | encode utf8
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #3:1:1]
 1 │ 0x[12] | encode utf8
   · ───┬──   ───┬──
   ·    │        ╰── only string input data is supported
   ·    ╰── input type: binary
   ╰────
```

# User-Facing Changes

Various error messages suddenly make more sense (i.e. have two arrows
instead of one).

# 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

# 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.
2022-12-23 01:48:53 -05:00
9364bad625 Make to text stream ListStreams (#7577)
This PR changes `to text` so that when given a `ListStream`, it streams
the incoming values instead of collecting them all first.

The easiest way to observe/verify this PR is to convert a list to a very
slow `ListStream` with `each`:
```bash
ls | get name | each {|n| sleep 1sec; $n} | to text
```
The `to text` output will appear 1 item at a time.
2022-12-22 16:38:07 -08:00
6fc5244439 tighter restrictions on alias and def names (#7392)
# Description

Prevent a situation where a `def` can't be run due to a poor choice of
name. Related: #6335. Hashtags, numbers and filesizes are no longer
allowed. `alias` check has been moved because previously `alias 123`
would be caught but `alias "123"` would be permitted.

# User-Facing Changes

Some definitions can no longer be made, but because they couldn't be run
previously anyway, it doesn't really matter.

# 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

# 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.
2022-12-22 12:31:34 -08:00
8e1112c1dd Change other instances of $nothing to null (#7569)
# Description

Purely for consistency, various remaining instances of `$nothing`
(almost all of which were in test code) have been changed to `null`.
Now, the only place that refers to `$nothing` is the parser code which
implements it.

# User-Facing Changes

The default config.nu now uses `null` in certain places where it used
`$nothing`.

# 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

# 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.
2022-12-22 12:30:10 -08:00
9d1cb1bfaf Make $in work in catch closures (#7458)
# Description

This now works:
```
try { 'x' | math abs } catch { $in }
```

# User-Facing Changes

See above.

# 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

# 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.
2022-12-22 09:35:41 -06:00
216d7d035f Add cross-rs config (#7559)
Cross-compiling Nu can be a little tricky due to dependencies. This PR
makes it easy to use [`cross-rs`](https://github.com/cross-rs/cross), a
popular tool for cross-compiling Rust code using Docker:
```bash
cross build --target aarch64-unknown-linux-musl --release
```

I find this useful for compiling ARM binaries from x64. Easy to add more
target triples later as needed.
2022-12-22 08:52:07 -06:00
ead6fbdf9c let case_insensitive option work for variable completion as well (#7539)
# Description

Fixes #7529.

# 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

# 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.
2022-12-22 08:50:59 -06:00
5b616770df Make config.filesize_format/config.filesize_metric conflict resolution consistent (#7410)
# Description

Currently, `filesize_format`/`filesize_metric` conflicts are resolved as
follows: if the `filesize_format` ends in "ib", then that overrides
`filesize_metric`, otherwise, `filesize_metric` overrides
`filesize_format`. This removes this difficult-to-predict asymmetric
behaviour, and makes it so that `filesize_metric` always overrides
`filesize_format`.

This also adds tests for `$env.config.filesize.format` and
`$env.config.filesize.metric` values.

REMINDER: `filesize_metric` means "increments of 1000", and refers to
KB-MB-GB-TB etc.

# User-Facing Changes

See above.

# 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

# 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.

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-22 08:46:55 -06:00
23a5c5dc09 Remove shape-directed import pattern parsing (#7570) 2022-12-22 16:36:13 +02:00
74656bf976 Fix #7551 record support in color_config (#7567)
Closes https://github.com/nushell/nushell/issues/7551
2022-12-22 12:55:50 +01:00
d8a2e0e9a3 Small parser refactors (#7568) 2022-12-22 13:41:44 +02:00
046e46b962 avoid panic when using from nuon (#7533)
# Description

Fixes #5996 

Just found a relative easy way to fix the issue

# User-Facing Changes

```
❯ open $nu.plugin-path | from nuon
Error:
  × error when loading nuon text
   ╭─[entry #36:1:1]
 1 │ open $nu.plugin-path | from nuon
   ·                        ────┬────
   ·                            ╰── could not load nuon text
   ╰────

Error:
  × Error when loading


❯ open $nu.config-path | from nuon
Error:
  × error when loading nuon text
   ╭─[entry #37:1:1]
 1 │ open $nu.config-path | from nuon
   ·                        ────┬────
   ·                            ╰── could not load nuon text
   ╰────

Error:
  × error when loading
```

# 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

# 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.
2022-12-21 16:42:23 -08:00
ec08e4bc6d Fix && quotation in to nuon after proptest fail (#7564)
`proptest` caught a failing test condition for `&&` as a literal string. It requires a quotation to be parsed correctly by current `from nuon`
    
https://github.com/nushell/nushell/actions/runs/3753242377/jobs/6376308675

The change in the parser that now returns an error was introduced by https://github.com/nushell/nushell/pull/7241

This in theory doesn't have to be an error (it is a diagnostic for nushell code) but it is probably better safe than sorry to require quotation here.

- Add a test for `&&` in `to nuon` from proptest fail
- Fix `to nuon` generating invalid `&&` literal
- Add a test for `,` in `to nuon`/`from nuon` cycle
  - Bonus: should already be properly quoted
2022-12-22 00:36:07 +01:00
05e07ddf5c Add some cell path tests (#7563) 2022-12-21 23:54:39 +01:00
757d7479af Add "fall-through" signatures (#7527)
Fixes https://github.com/nushell/nushell/issues/4659
Fixes https://github.com/nushell/nushell/issues/5294
Fixes https://github.com/nushell/nushell/issues/6124
fix https://github.com/nushell/nushell/issues/5103
2022-12-22 00:33:26 +02:00
440feaf74a Clarify --stdin flag (#7541)
Just change the description of the `--stdin` flag as shown in `nu
--help`:

"redirect the stdin" -> "redirect standard input to a command (with
`-c`) or a script file"

The old description was a little too terse and I had to look in the code
to see what it was doing.
2022-12-21 14:30:53 -08:00
22c50185b5 table: Check stream timeout on every item (#7509)
`table` handles slow `ListStream`s in a special way: every 100 items, it
checks whether 1 second has elapsed since the last table page, and if so
it outputs a new page with all the items in its buffer.

**I would like to remove the "every 100 items" condition and always
output whatever we have if a second has elapsed.** I think this will be
a better user experience for very slow streams.

As a motivating example, imagine tailing a log file and doing some
string parsing/projection on each line. The user will be really annoyed
if they have to wait for 100 lines to be written to the log before
seeing new results!

I did some quick performance measurements with Criterion, and the
elapsed-time check takes about 16ns on my machine (Linux, 12900k). I
think the performance impact of checking that for every item will be
negligible.
2022-12-21 14:28:27 -08:00
3a2c7900d6 Initial support for parse-time constants (#7436) 2022-12-22 00:21:03 +02:00
fa8629300f chore: make the config setup messages consistent (#7560) 2022-12-21 23:16:08 +01:00
37dc226996 Remove preview.rs (#7555)
Leftover from `explore` development

Closes https://github.com/nushell/nushell/issues/7553
2022-12-21 21:51:30 +01:00
4e1f94026c Add more input/output type annotations (#7532) 2022-12-21 20:20:46 +01:00
d27263af97 Bump to new development version 0.73.1 (#7544) 2022-12-21 12:35:50 -06:00
215f1af1da fix the wix file to overwrite with save -f (#7545) 2022-12-21 12:34:49 -06:00
534 changed files with 9219 additions and 7863 deletions

View File

@ -15,8 +15,22 @@ jobs:
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
# revisiting this when 20.04 is closer to EOL (April 2025)
platform: [windows-latest, macos-latest, ubuntu-20.04]
style: [default, dataframe]
rust:
- stable
include:
- style: default
flags: ""
- style: dataframe
flags: "--features=dataframe "
exclude:
# only test dataframes on Ubuntu (the fastest platform)
- platform: windows-latest
style: dataframe
- platform: macos-latest
style: dataframe
runs-on: ${{ matrix.platform }}
env:
@ -32,7 +46,7 @@ jobs:
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
run: cargo clippy --workspace ${{ matrix.flags }}--exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
nu-tests:
env:
@ -99,8 +113,9 @@ jobs:
- run: python -m pip install tox
# Get only the latest tagged version for stability reasons
- name: Install virtualenv
run: git clone https://github.com/pypa/virtualenv.git
run: git clone https://github.com/pypa/virtualenv.git && cd virtualenv && git checkout $(git describe --tags | cut -d - -f 1)
shell: bash
- name: Test Nushell in virtualenv

248
Cargo.lock generated
View File

@ -80,6 +80,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "ansi-str"
version = "0.5.0"
@ -104,7 +110,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
dependencies = [
"nom 7.1.1",
"nom",
"vte",
]
@ -479,7 +485,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom 7.1.1",
"nom",
]
[[package]]
@ -542,6 +548,33 @@ dependencies = [
"phf_codegen 0.11.1",
]
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clang-sys"
version = "1.4.0"
@ -555,13 +588,23 @@ dependencies = [
[[package]]
name = "clap"
version = "2.34.0"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"bitflags",
"textwrap 0.11.0",
"unicode-width",
"clap_lex",
"indexmap",
"textwrap 0.16.0",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
@ -673,15 +716,16 @@ dependencies = [
[[package]]
name = "criterion"
version = "0.3.6"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
@ -690,7 +734,6 @@ dependencies = [
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
@ -699,9 +742,9 @@ dependencies = [
[[package]]
name = "criterion-plot"
version = "0.4.5"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
@ -1167,7 +1210,7 @@ checksum = "e11dcc7e4d79a8c89b9ab4c6f5c30b1fc4a83c420792da3542fd31179ed5f517"
dependencies = [
"cfg-if 1.0.0",
"rustix",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -1188,7 +1231,7 @@ dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -2110,6 +2153,15 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "lru"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909"
dependencies = [
"hashbrown",
]
[[package]]
name = "lscolors"
version = "0.12.0"
@ -2216,16 +2268,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "meval"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9"
dependencies = [
"fnv",
"nom 1.2.4",
]
[[package]]
name = "miette"
version = "5.3.0"
@ -2262,6 +2304,16 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -2305,7 +2357,7 @@ dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -2420,12 +2472,6 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
[[package]]
name = "nom"
version = "7.1.1"
@ -2446,7 +2492,7 @@ dependencies = [
"indent_write",
"joinery",
"memchr",
"nom 7.1.1",
"nom",
]
[[package]]
@ -2478,11 +2524,12 @@ dependencies = [
[[package]]
name = "nu"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"assert_cmd",
"atty",
"chrono",
"criterion",
"crossterm 0.24.0",
"ctrlc",
"hamcrest2",
@ -2532,7 +2579,7 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"atty",
"chrono",
@ -2561,7 +2608,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"nu-ansi-term",
"nu-engine",
@ -2575,7 +2622,7 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"Inflector",
"alphanumeric-sort",
@ -2609,8 +2656,8 @@ dependencies = [
"log",
"lscolors",
"md-5",
"meval",
"mime",
"mime_guess",
"notify",
"nu-ansi-term",
"nu-color-config",
@ -2631,7 +2678,9 @@ dependencies = [
"num-format",
"num-traits",
"once_cell",
"open",
"pathdiff",
"percent-encoding",
"polars",
"powierza-coefficient",
"proptest",
@ -2674,7 +2723,7 @@ dependencies = [
[[package]]
name = "nu-engine"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"chrono",
"nu-glob",
@ -2686,7 +2735,7 @@ dependencies = [
[[package]]
name = "nu-explore"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"ansi-str 0.7.2",
"crossterm 0.24.0",
@ -2706,14 +2755,14 @@ dependencies = [
[[package]]
name = "nu-glob"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"doc-comment",
]
[[package]]
name = "nu-json"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"linked-hash-map",
"num-traits",
@ -2722,7 +2771,7 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"bytesize",
"chrono",
@ -2739,7 +2788,7 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"dirs-next",
"omnipath",
@ -2748,10 +2797,9 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"bincode",
"criterion",
"nu-engine",
"nu-protocol",
"rmp",
@ -2762,7 +2810,7 @@ dependencies = [
[[package]]
name = "nu-pretty-hex"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"heapless",
"nu-ansi-term",
@ -2771,13 +2819,14 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"byte-unit",
"chrono",
"chrono-humanize",
"fancy-regex",
"indexmap",
"lru",
"miette",
"nu-json",
"nu-test-support",
@ -2794,7 +2843,7 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"atty",
"chrono",
@ -2812,7 +2861,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"atty",
"json_to_table",
@ -2828,7 +2877,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"nu-utils",
"unicode-width",
@ -2836,7 +2885,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"getset",
"hamcrest2",
@ -2850,7 +2899,7 @@ dependencies = [
[[package]]
name = "nu-utils"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"crossterm_winapi",
"lscolors",
@ -2871,7 +2920,7 @@ dependencies = [
[[package]]
name = "nu_plugin_example"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2879,7 +2928,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"git2",
"nu-engine",
@ -2889,7 +2938,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2898,7 +2947,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.73.0"
version = "0.74.0"
dependencies = [
"gjson",
"nu-engine",
@ -3089,6 +3138,16 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "open"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
dependencies = [
"pathdiff",
"windows-sys 0.42.0",
]
[[package]]
name = "openssl"
version = "0.10.42"
@ -3144,6 +3203,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "output_vt100"
version = "0.1.3"
@ -3198,7 +3263,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -3640,14 +3705,14 @@ version = "0.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a63d338dec139f56dacc692ca63ad35a6be6a797442479b55acd611d79e906"
dependencies = [
"nom 7.1.1",
"nom",
]
[[package]]
name = "powierza-coefficient"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e2c03bca73af2a7a2c021a6b3b4991658b760b2e0a84e3e425a9c9eda2ba7f"
checksum = "04123079750026568dff0e68efe1ca676f6686023f3bf7686b87dab661c0375b"
[[package]]
name = "ppv-lite86"
@ -4252,7 +4317,7 @@ dependencies = [
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -4295,7 +4360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -4416,16 +4481,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
@ -4934,7 +4989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
dependencies = [
"rustix",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -4943,15 +4998,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.1"
@ -4963,6 +5009,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thin-slice"
version = "0.1.1"
@ -5065,9 +5117,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.21.2"
version = "1.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
dependencies = [
"autocfg",
"bytes",
@ -5077,7 +5129,7 @@ dependencies = [
"num_cpus",
"pin-project-lite",
"socket2",
"winapi 0.3.9",
"windows-sys 0.42.0",
]
[[package]]
@ -5231,6 +5283,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
@ -5533,7 +5594,7 @@ dependencies = [
"bstr",
"const_format",
"itertools",
"nom 7.1.1",
"nom",
"nom-supreme",
"pori",
"regex",
@ -5634,6 +5695,21 @@ dependencies = [
"windows_x86_64_msvc 0.36.1",
]
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.42.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"

View File

@ -3,14 +3,14 @@ authors = ["The Nushell Project Developers"]
default-run = "nu"
description = "A new type of shell"
documentation = "https://www.nushell.sh/book/"
edition = "2018"
edition = "2021"
exclude = ["images"]
homepage = "https://www.nushell.sh"
license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.73.0"
version = "0.74.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -45,20 +45,20 @@ ctrlc = "3.2.1"
log = "0.4"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.73.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.73.0" }
nu-command = { path="./crates/nu-command", version = "0.73.0" }
nu-engine = { path="./crates/nu-engine", version = "0.73.0" }
nu-json = { path="./crates/nu-json", version = "0.73.0" }
nu-parser = { path="./crates/nu-parser", version = "0.73.0" }
nu-path = { path="./crates/nu-path", version = "0.73.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.73.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.73.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.73.0" }
nu-system = { path = "./crates/nu-system", version = "0.73.0" }
nu-table = { path = "./crates/nu-table", version = "0.73.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.73.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.73.0" }
nu-cli = { path="./crates/nu-cli", version = "0.74.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.74.0" }
nu-command = { path="./crates/nu-command", version = "0.74.0" }
nu-engine = { path="./crates/nu-engine", version = "0.74.0" }
nu-json = { path="./crates/nu-json", version = "0.74.0" }
nu-parser = { path="./crates/nu-parser", version = "0.74.0" }
nu-path = { path="./crates/nu-path", version = "0.74.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.74.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.74.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.74.0" }
nu-system = { path = "./crates/nu-system", version = "0.74.0" }
nu-table = { path = "./crates/nu-table", version = "0.74.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.74.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.74.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
rayon = "1.5.1"
@ -80,9 +80,10 @@ nix = { version = "0.25", default-features = false, features = ["signal", "proce
atty = "0.2"
[dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.73.0" }
nu-test-support = { path="./crates/nu-test-support", version = "0.74.0" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
criterion = "0.4"
pretty_assertions = "1.0.0"
serial_test = "0.8.0"
hamcrest2 = "0.3.0"
@ -140,3 +141,18 @@ path = "src/main.rs"
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
# Criterion benchmarking setup
# Run all benchmarks with `cargo bench`
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
[[bench]]
name = "encoder_benchmark"
harness = false
[[bench]]
name = "eval_benchmark"
harness = false
[[bench]]
name = "parser_benchmark"
harness = false

9
Cross.toml Normal file
View File

@ -0,0 +1,9 @@
# Configuration for cross-rs: https://github.com/cross-rs/cross
# Run cross-rs like this:
# cross build --target aarch64-unknown-linux-musl --release
[target.aarch64-unknown-linux-gnu]
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-gnu.dockerfile"
[target.aarch64-unknown-linux-musl]
dockerfile = "./docker/cross-rs/aarch64-unknown-linux-musl.dockerfile"

View File

@ -47,7 +47,7 @@ brew install nushell
winget install nushell
```
To use `Nu` in Github Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/actions/setup-nu) for more detail.
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
@ -174,6 +174,8 @@ 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.
## Goals
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.

7
benches/README.md Normal file
View File

@ -0,0 +1,7 @@
# Criterion benchmarks
These are benchmarks using [Criterion](https://github.com/bheisler/criterion.rs), a microbenchmarking tool for Rust.
Run all benchmarks with `cargo bench`
Or run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`

42
benches/eval_benchmark.rs Normal file
View File

@ -0,0 +1,42 @@
use criterion::{criterion_group, criterion_main, Criterion};
use nu_cli::eval_source;
use nu_protocol::{PipelineData, Span, Value};
use nu_utils::{get_default_config, get_default_env};
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("eval default_env.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_env().as_bytes(),
"default_env.nu",
PipelineData::empty(),
)
})
});
c.bench_function("eval default_config.nu", |b| {
b.iter(|| {
let mut engine_state = nu_command::create_default_context();
// parsing config.nu breaks without PWD set
engine_state.add_env_var(
"PWD".into(),
Value::string("/some/dir".to_string(), Span::test_data()),
);
let mut stack = nu_protocol::engine::Stack::new();
eval_source(
&mut engine_state,
&mut stack,
get_default_config().as_bytes(),
"default_config.nu",
PipelineData::empty(),
)
})
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -0,0 +1,34 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use nu_parser::parse;
use nu_protocol::{Span, Value};
use nu_utils::{get_default_config, get_default_env};
fn criterion_benchmark(c: &mut Criterion) {
let mut engine_state = nu_command::create_default_context();
// parsing config.nu breaks without PWD set
engine_state.add_env_var(
"PWD".into(),
Value::string("/some/dir".to_string(), Span::test_data()),
);
let default_env = get_default_env().as_bytes();
c.bench_function("parse_default_env_file", |b| {
b.iter_batched(
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
BatchSize::SmallInput,
)
});
let default_config = get_default_config().as_bytes();
c.bench_function("parse_default_config_file", |b| {
b.iter_batched(
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
BatchSize::SmallInput,
)
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -5,7 +5,7 @@
@echo.
echo Building nushell.exe
cargo build cargo build --features=dataframe
cargo build --features=dataframe
@echo.
@cd crates\nu_plugin_example

View File

@ -5,21 +5,21 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.73.0"
version = "0.74.0"
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.73.0" }
nu-command = { path = "../nu-command", version = "0.73.0" }
nu-test-support = { path="../nu-test-support", version = "0.74.0" }
nu-command = { path = "../nu-command", version = "0.74.0" }
rstest = {version = "0.15.0", default-features = false}
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.73.0" }
nu-path = { path = "../nu-path", version = "0.73.0" }
nu-parser = { path = "../nu-parser", version = "0.73.0" }
nu-protocol = { path = "../nu-protocol", version = "0.73.0" }
nu-utils = { path = "../nu-utils", version = "0.73.0" }
nu-engine = { path = "../nu-engine", version = "0.74.0" }
nu-path = { path = "../nu-path", version = "0.74.0" }
nu-parser = { path = "../nu-parser", version = "0.74.0" }
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
nu-utils = { path = "../nu-utils", version = "0.74.0" }
nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.73.0" }
nu-color-config = { path = "../nu-color-config", version = "0.74.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
atty = "0.2.14"

View File

@ -66,6 +66,7 @@ impl Completer for CustomCompletion {
],
redirect_stdout: true,
redirect_stderr: true,
parser_info: vec![],
},
PipelineData::empty(),
);

View File

@ -146,6 +146,8 @@ pub fn file_path_completion(
|| path.contains('"')
|| path.contains(' ')
|| path.contains('#')
|| path.contains('(')
|| path.contains(')')
{
path = format!("`{}`", path);
}

View File

@ -9,6 +9,8 @@ use reedline::Suggestion;
use std::str;
use std::sync::Arc;
use super::MatchAlgorithm;
#[derive(Clone)]
pub struct VariableCompletion {
engine_state: Arc<EngineState>, // TODO: Is engine state necessary? It's already a part of working set in fetch()
@ -73,10 +75,11 @@ impl Completer for VariableCompletion {
for suggestion in
nested_suggestions(val.clone(), nested_levels, current_span)
{
if options
.match_algorithm
.matches_u8(suggestion.value.as_bytes(), &prefix)
{
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
suggestion.value.as_bytes(),
&prefix,
) {
output.push(suggestion);
}
}
@ -86,10 +89,11 @@ impl Completer for VariableCompletion {
} else {
// No nesting provided, return all env vars
for env_var in env_vars {
if options
.match_algorithm
.matches_u8(env_var.0.as_bytes(), &prefix)
{
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
env_var.0.as_bytes(),
&prefix,
) {
output.push(Suggestion {
value: env_var.0,
description: None,
@ -116,10 +120,11 @@ impl Completer for VariableCompletion {
for suggestion in
nested_suggestions(nuval, self.var_context.1.clone(), current_span)
{
if options
.match_algorithm
.matches_u8(suggestion.value.as_bytes(), &prefix)
{
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
suggestion.value.as_bytes(),
&prefix,
) {
output.push(suggestion);
}
}
@ -138,10 +143,11 @@ impl Completer for VariableCompletion {
for suggestion in
nested_suggestions(value, self.var_context.1.clone(), current_span)
{
if options
.match_algorithm
.matches_u8(suggestion.value.as_bytes(), &prefix)
{
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
suggestion.value.as_bytes(),
&prefix,
) {
output.push(suggestion);
}
}
@ -153,10 +159,11 @@ impl Completer for VariableCompletion {
// Variable completion (e.g: $en<tab> to complete $env)
for builtin in builtins {
if options
.match_algorithm
.matches_u8(builtin.as_bytes(), &prefix)
{
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
builtin.as_bytes(),
&prefix,
) {
output.push(Suggestion {
value: builtin.to_string(),
description: None,
@ -178,7 +185,11 @@ impl Completer for VariableCompletion {
.rev()
{
for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8(v.0, &prefix) {
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
v.0,
&prefix,
) {
output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
@ -200,7 +211,11 @@ impl Completer for VariableCompletion {
.rev()
{
for v in &overlay_frame.vars {
if options.match_algorithm.matches_u8(v.0, &prefix) {
if options.match_algorithm.matches_u8_insensitive(
options.case_sensitive,
v.0,
&prefix,
) {
output.push(Suggestion {
value: String::from_utf8_lossy(v.0).to_string(),
description: None,
@ -281,3 +296,13 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
val
}
impl MatchAlgorithm {
pub fn matches_u8_insensitive(&self, sensitive: bool, haystack: &[u8], needle: &[u8]) -> bool {
if sensitive {
self.matches_u8(haystack, needle)
} else {
self.matches_u8(&haystack.to_ascii_lowercase(), &needle.to_ascii_lowercase())
}
}
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Value};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type, Value};
use reedline::Highlighter;
#[derive(Clone)]
@ -12,7 +12,9 @@ impl Command for NuHighlight {
}
fn signature(&self) -> Signature {
Signature::build("nu-highlight").category(Category::Strings)
Signature::build("nu-highlight")
.category(Category::Strings)
.input_output_types(vec![(Type::String, Type::String)])
}
fn usage(&self) -> &str {

View File

@ -2,7 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Type,
Value,
};
#[derive(Clone)]
@ -15,6 +16,7 @@ impl Command for Print {
fn signature(&self) -> Signature {
Signature::build("print")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest("rest", SyntaxShape::Any, "the values to print")
.switch(
"no-newline",

View File

@ -1,6 +1,6 @@
use crate::util::report_error;
use crate::NushellPrompt;
use log::info;
use log::trace;
use nu_engine::eval_subexpression;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
@ -39,7 +39,7 @@ fn get_prompt_string(
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val =
eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
info!(
trace!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
@ -59,7 +59,7 @@ fn get_prompt_string(
let block = engine_state.get_block(block_id);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
info!(
trace!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
@ -148,7 +148,7 @@ pub(crate) fn update_prompt<'prompt>(
);
let ret_val = nu_prompt as &dyn Prompt;
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
ret_val
}

View File

@ -7,8 +7,7 @@ use nu_parser::parse;
use nu_protocol::{
create_menus,
engine::{EngineState, Stack, StateWorkingSet},
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
ShellError, Span, Value,
extract_value, Config, ParsedKeybinding, ParsedMenu, PipelineData, ShellError, Span, Value,
};
use reedline::{
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
@ -110,7 +109,7 @@ pub(crate) fn add_menus(
};
let mut temp_stack = Stack::new();
let input = Value::nothing(Span::test_data()).into_pipeline_data();
let input = PipelineData::Empty;
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
if let PipelineData::Value(value, None) = res {
@ -680,9 +679,9 @@ fn add_parsed_keybinding(
let fn_num: u8 = c[1..]
.parse()
.ok()
.filter(|num| matches!(num, 1..=12))
.filter(|num| matches!(num, 1..=20))
.ok_or(ShellError::UnsupportedConfigValue(
"(f1|f2|...|f12)".to_string(),
"(f1|f2|...|f20)".to_string(),
format!("unknown function key: {}", c),
keybinding.keycode.span()?,
))?;
@ -990,7 +989,7 @@ mod test {
#[test]
fn test_send_event() {
let cols = vec!["send".to_string()];
let vals = vec![Value::string("Enter", Span::test_data())];
let vals = vec![Value::test_string("Enter")];
let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1010,7 +1009,7 @@ mod test {
#[test]
fn test_edit_event() {
let cols = vec!["edit".to_string()];
let vals = vec![Value::string("Clear", Span::test_data())];
let vals = vec![Value::test_string("Clear")];
let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1034,8 +1033,8 @@ mod test {
fn test_send_menu() {
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::string("Menu", Span::test_data()),
Value::string("history_menu", Span::test_data()),
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let span = Span::test_data();
@ -1061,8 +1060,8 @@ mod test {
// Menu event
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::string("Menu", Span::test_data()),
Value::string("history_menu", Span::test_data()),
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let menu_event = Value::Record {
@ -1073,7 +1072,7 @@ mod test {
// Enter event
let cols = vec!["send".to_string()];
let vals = vec![Value::string("Enter", Span::test_data())];
let vals = vec![Value::test_string("Enter")];
let enter_event = Value::Record {
cols,
@ -1114,8 +1113,8 @@ mod test {
// Menu event
let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![
Value::string("Menu", Span::test_data()),
Value::string("history_menu", Span::test_data()),
Value::test_string("Menu"),
Value::test_string("history_menu"),
];
let menu_event = Value::Record {
@ -1126,7 +1125,7 @@ mod test {
// Enter event
let cols = vec!["send".to_string()];
let vals = vec![Value::string("Enter", Span::test_data())];
let vals = vec![Value::test_string("Enter")];
let enter_event = Value::Record {
cols,
@ -1154,7 +1153,7 @@ mod test {
#[test]
fn test_error() {
let cols = vec!["not_exist".to_string()];
let vals = vec![Value::string("Enter", Span::test_data())];
let vals = vec![Value::test_string("Enter")];
let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span);

View File

@ -639,7 +639,7 @@ pub fn eval_string_with_input(
false,
true,
)
.map(|x| x.into_value(Span::test_data()))
.map(|x| x.into_value(Span::unknown()))
}
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
@ -734,60 +734,63 @@ pub fn eval_hook(
}
}
Value::Record { .. } => {
let do_run_hook =
if let Ok(condition) = value.clone().follow_cell_path(&[condition_path], 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);
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,
));
}
}
}
other => {
return Err(ShellError::UnsupportedConfigValue(
"block".to_string(),
format!("{}", other.get_type()),
other.span()?,
));
Err(err) => {
return Err(err);
}
}
}
} else {
// always run the hook
true
};
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)? {
match value.clone().follow_cell_path(&[code_path], false, false)? {
Value::String {
val,
span: source_span,

View File

@ -352,6 +352,7 @@ fn find_matching_block_end_in_expr(
let opt_expr = match arg {
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr),
Argument::Unknown(inner_expr) => Some(inner_expr),
};
if let Some(inner_expr) = opt_expr {

View File

@ -34,6 +34,26 @@ fn completer_strings() -> NuCompleter {
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[fixture]
fn extern_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = r#"
def animals [] { [ "cat", "dog", "eel" ] }
extern spam [
animal: string@animals
--foo (-f): string@animals
-b: string@animals
]
"#;
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[test]
fn variables_dollar_sign_with_varialblecompletion() {
let (_, _, engine, stack) = new_engine();
@ -89,7 +109,7 @@ fn dotnu_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test source completion
@ -149,7 +169,7 @@ fn file_completions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
@ -424,6 +444,7 @@ fn file_completion_quoted() {
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
"`te(st).txt`".to_string(),
];
match_suggestions(expected_paths, suggestions)
@ -434,12 +455,12 @@ fn flag_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the 'ls' flags
let suggestions = completer.complete("ls -", 4);
assert_eq!(14, suggestions.len());
assert_eq!(16, suggestions.len());
let expected: Vec<String> = vec![
"--all".into(),
@ -448,6 +469,7 @@ fn flag_completions() {
"--full-paths".into(),
"--help".into(),
"--long".into(),
"--mime-type".into(),
"--short-names".into(),
"-D".into(),
"-a".into(),
@ -455,6 +477,7 @@ fn flag_completions() {
"-f".into(),
"-h".into(),
"-l".into(),
"-m".into(),
"-s".into(),
];
@ -467,7 +490,7 @@ fn folder_with_directorycompletions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
@ -495,7 +518,7 @@ fn variables_completions() {
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for $nu
@ -652,7 +675,7 @@ fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
config.external_completer = Some(latest_block_id);
engine_state.set_config(&config);
// Instatiate a new completer
// Instantiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
completer.complete(input, input.len())
@ -750,3 +773,45 @@ fn filecompletions_triggers_after_cursor() {
match_suggestions(expected_paths, suggestions);
}
#[rstest]
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam ", 5);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo=", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam --foo ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -f ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -b ", 8);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn extern_complete_flags(mut extern_completer: NuCompleter) {
let suggestions = extern_completer.complete("spam -", 6);
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
match_suggestions(expected, suggestions);
}

View File

@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.73.0"
version = "0.74.0"
[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.73.0" }
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
nu-ansi-term = "0.46.0"
nu-utils = { path = "../nu-utils", version = "0.73.0" }
nu-engine = { path = "../nu-engine", version = "0.73.0" }
nu-json = { path="../nu-json", version = "0.73.0" }
nu-utils = { path = "../nu-utils", version = "0.74.0" }
nu-engine = { path = "../nu-engine", version = "0.74.0" }
nu-json = { path="../nu-json", version = "0.74.0" }
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.73.0" }
nu-test-support = { path="../nu-test-support", version = "0.74.0" }

View File

@ -1,45 +1,56 @@
use crate::color_config::lookup_ansi_color_style;
use crate::{color_config::lookup_ansi_color_style, color_record_to_nustyle};
use nu_ansi_term::{Color, Style};
use nu_protocol::Config;
use nu_protocol::{Config, Value};
// The default colors for shapes, used when there is no config for them.
pub fn default_shape_color(shape: String) -> Style {
match shape.as_ref() {
"shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_binary" => Style::new().fg(Color::Purple).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_bool" => Style::new().fg(Color::LightCyan),
"shape_custom" => Style::new().fg(Color::Green),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_directory" => Style::new().fg(Color::Cyan),
"shape_external" => Style::new().fg(Color::Cyan),
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
"shape_filepath" => Style::new().fg(Color::Cyan),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_float" => Style::new().fg(Color::Purple).bold(),
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"shape_int" => Style::new().fg(Color::Purple).bold(),
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_literal" => Style::new().fg(Color::Blue),
"shape_nothing" => Style::new().fg(Color::LightCyan),
"shape_operator" => Style::new().fg(Color::Yellow),
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
"shape_range" => Style::new().fg(Color::Yellow).bold(),
"shape_record" => Style::new().fg(Color::Cyan).bold(),
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_signature" => Style::new().fg(Color::Green).bold(),
"shape_string" => Style::new().fg(Color::Green),
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_variable" => Style::new().fg(Color::Purple),
_ => Style::default(),
}
}
pub fn get_shape_color(shape: String, conf: &Config) -> Style {
match conf.color_config.get(shape.as_str()) {
Some(int_color) => match int_color.as_string() {
Ok(int_color) => lookup_ansi_color_style(&int_color),
Err(_) => Style::default(),
},
None => match shape.as_ref() {
"shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_binary" => Style::new().fg(Color::Purple).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_bool" => Style::new().fg(Color::LightCyan),
"shape_custom" => Style::new().fg(Color::Green),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_directory" => Style::new().fg(Color::Cyan),
"shape_external" => Style::new().fg(Color::Cyan),
"shape_externalarg" => Style::new().fg(Color::Green).bold(),
"shape_filepath" => Style::new().fg(Color::Cyan),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_float" => Style::new().fg(Color::Purple).bold(),
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"shape_int" => Style::new().fg(Color::Purple).bold(),
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_literal" => Style::new().fg(Color::Blue),
"shape_nothing" => Style::new().fg(Color::LightCyan),
"shape_operator" => Style::new().fg(Color::Yellow),
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
"shape_range" => Style::new().fg(Color::Yellow).bold(),
"shape_record" => Style::new().fg(Color::Cyan).bold(),
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_signature" => Style::new().fg(Color::Green).bold(),
"shape_string" => Style::new().fg(Color::Green),
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_variable" => Style::new().fg(Color::Purple),
_ => Style::default(),
},
Some(int_color) => {
// Shapes do not use color_config closures, currently.
match int_color {
Value::Record { .. } => color_record_to_nustyle(int_color),
Value::String { val, .. } => lookup_ansi_color_style(val),
// Defer to the default in the event of incorrect types being given
// (i.e. treat null, etc. as the value being unset)
_ => default_shape_color(shape),
}
}
None => default_shape_color(shape),
}
}

View File

@ -126,7 +126,7 @@ impl<'a> StyleComputer<'a> {
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),

View File

@ -5,25 +5,25 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021"
license = "MIT"
name = "nu-command"
version = "0.73.0"
version = "0.74.0"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.73.0" }
nu-engine = { path = "../nu-engine", version = "0.73.0" }
nu-glob = { path = "../nu-glob", version = "0.73.0" }
nu-json = { path = "../nu-json", version = "0.73.0" }
nu-parser = { path = "../nu-parser", version = "0.73.0" }
nu-path = { path = "../nu-path", version = "0.73.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.73.0" }
nu-protocol = { path = "../nu-protocol", version = "0.73.0" }
nu-system = { path = "../nu-system", version = "0.73.0" }
nu-table = { path = "../nu-table", version = "0.73.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.73.0" }
nu-utils = { path = "../nu-utils", version = "0.73.0" }
nu-explore = { path = "../nu-explore", version = "0.73.0" }
nu-color-config = { path = "../nu-color-config", version = "0.74.0" }
nu-engine = { path = "../nu-engine", version = "0.74.0" }
nu-glob = { path = "../nu-glob", version = "0.74.0" }
nu-json = { path = "../nu-json", version = "0.74.0" }
nu-parser = { path = "../nu-parser", version = "0.74.0" }
nu-path = { path = "../nu-path", version = "0.74.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.74.0" }
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
nu-system = { path = "../nu-system", version = "0.74.0" }
nu-table = { path = "../nu-table", version = "0.74.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.74.0" }
nu-utils = { path = "../nu-utils", version = "0.74.0" }
nu-explore = { path = "../nu-explore", version = "0.74.0" }
nu-ansi-term = "0.46.0"
num-format = { version = "0.4.3" }
@ -57,14 +57,15 @@ itertools = "0.10.0"
log = "0.4.14"
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" }
meval = "0.2.0"
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.0"
open = "3.2.0"
pathdiff = "0.2.1"
powierza-coefficient = "1.0.1"
powierza-coefficient = "1.0.2"
quick-xml = "0.25"
rand = "0.8"
rayon = "1.5.1"
@ -87,6 +88,7 @@ titlecase = "2.0.0"
toml = "0.5.8"
unicode-segmentation = "1.8.0"
url = "2.2.1"
percent-encoding = "2.2.0"
uuid = { version = "1.1.2", features = ["v4"] }
which = { version = "4.3.0", optional = true }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
@ -156,7 +158,7 @@ sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, shoul
shadow-rs = { version = "0.16.1", default-features = false }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.73.0" }
nu-test-support = { path = "../nu-test-support", version = "0.74.0" }
hamcrest2 = "0.3.0"
dirs-next = "2.0.0"

View File

@ -43,6 +43,10 @@ impl Command for SubCommand {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
@ -54,7 +58,7 @@ impl Command for SubCommand {
Example {
description: "Apply bits and to two numbers",
example: "2 | bits and 2",
result: Some(Value::int(2, Span::test_data())),
result: Some(Value::test_int(2)),
},
Example {
description: "Apply logical and to a list of numbers",
@ -74,13 +78,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val & target,
span,
},
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
Category, IntoPipelineData, PipelineData, Signature, Type, Value,
};
#[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Bits {
}
fn signature(&self) -> Signature {
Signature::build("bits").category(Category::Bits)
Signature::build("bits")
.category(Category::Bits)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
"Various commands for working with bits"
}
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run(
&self,
engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Bits {
.into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use crate::Bits;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Bits {})
}
}

View File

@ -56,11 +56,17 @@ impl Command for SubCommand {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
}
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, head, signed, bytes_len),
engine_state.ctrlc.clone(),
@ -140,14 +146,17 @@ fn operate(value: Value, head: Span, signed: bool, number_size: NumberBytes) ->
Value::Int { val: out_val, span }
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only numerical values are supported, input type: {:?}",
other.get_type()
other => match other {
// Propagate errors inside the value
Value::Error { .. } => other,
_ => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"numeric".into(),
other.get_type().to_string(),
head,
other.expect_span(),
),
other.span().unwrap_or(head),
),
},
},
}
}

View File

@ -43,6 +43,10 @@ impl Command for SubCommand {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
@ -54,7 +58,7 @@ impl Command for SubCommand {
Example {
description: "Apply bits or to two numbers",
example: "2 | bits or 6",
result: Some(Value::int(6, Span::test_data())),
result: Some(Value::test_int(6)),
},
Example {
description: "Apply logical or to a list of numbers",
@ -74,13 +78,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val | target,
span,
},
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
}
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
@ -76,7 +81,7 @@ impl Command for SubCommand {
Example {
description: "Rotate left a number with 2 bits",
example: "17 | bits rol 2",
result: Some(Value::int(68, Span::test_data())),
result: Some(Value::test_int(68)),
},
Example {
description: "Rotate left a list of numbers with 2 bits",
@ -130,13 +135,15 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedEight => get_rotate_left(val as i64, bits, span),
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
}
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
@ -76,7 +81,7 @@ impl Command for SubCommand {
Example {
description: "Rotate right a number with 60 bits",
example: "17 | bits ror 60",
result: Some(Value::int(272, Span::test_data())),
result: Some(Value::test_int(272)),
},
Example {
description: "Rotate right a list of numbers of one byte",
@ -134,13 +139,15 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedEight => get_rotate_right(val as i64, bits, span),
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
}
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
@ -76,17 +81,17 @@ impl Command for SubCommand {
Example {
description: "Shift left a number by 7 bits",
example: "2 | bits shl 7",
result: Some(Value::int(256, Span::test_data())),
result: Some(Value::test_int(256)),
},
Example {
description: "Shift left a number with 1 byte by 7 bits",
example: "2 | bits shl 7 -n 1",
result: Some(Value::int(0, Span::test_data())),
result: Some(Value::test_int(0)),
},
Example {
description: "Shift left a signed number by 1 bit",
example: "0x7F | bits shl 1 -s",
result: Some(Value::int(254, Span::test_data())),
result: Some(Value::test_int(254)),
},
Example {
description: "Shift left a list of numbers",
@ -156,13 +161,15 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedEight => get_shift_left(val as i64, bits, span),
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -60,11 +60,16 @@ impl Command for SubCommand {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
"value originates from here".to_string(),
head,
val.span,
));
}
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
@ -76,7 +81,7 @@ impl Command for SubCommand {
Example {
description: "Shift right a number with 2 bits",
example: "8 | bits shr 2",
result: Some(Value::int(2, Span::test_data())),
result: Some(Value::test_int(2)),
},
Example {
description: "Shift right a list of numbers",
@ -146,13 +151,15 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
SignedEight => get_shift_right(val as i64, bits, span),
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -42,7 +42,10 @@ impl Command for SubCommand {
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty(head));
}
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
@ -54,7 +57,7 @@ impl Command for SubCommand {
Example {
description: "Apply bits xor to two numbers",
example: "2 | bits xor 2",
result: Some(Value::int(0, Span::test_data())),
result: Some(Value::test_int(0)),
},
Example {
description: "Apply logical xor to a list of numbers",
@ -74,13 +77,15 @@ fn operate(value: Value, target: i64, head: Span) -> Value {
val: val ^ target,
span,
},
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => value,
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -122,13 +122,15 @@ fn add(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => add_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -30,7 +30,9 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
Value::List { mut vals, span } => {
if vals.len() != 2 {
return Err(ShellError::UnsupportedInput(
"More than two indices given".to_string(),
"More than two indices in range".to_string(),
"value originates from here".to_string(),
head,
span,
));
} else {
@ -38,10 +40,14 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
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(
"could not perform subbytes. Expecting a string or int".to_string(),
other.span().unwrap_or(head),
"Only string or list<int> ranges are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
))
}
};
@ -49,10 +55,14 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
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(
"could not perform subbytes. Expecting a string or int".to_string(),
other.span().unwrap_or(head),
"Only string or list<int> ranges are supported".into(),
format!("input type: {:?}", other.get_type()),
head,
other.expect_span(),
))
}
};
@ -66,15 +76,21 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
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(),
other.span().unwrap_or(head),
"with this range".to_string(),
head,
other.expect_span(),
))
}
};
@ -87,6 +103,8 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
Err(_) => {
return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span,
))
}
@ -100,6 +118,8 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
Err(_) => {
return Err(ShellError::UnsupportedInput(
"could not perform subbytes".to_string(),
"with this range".to_string(),
head,
span,
))
}
@ -232,13 +252,15 @@ fn at(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => at_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}
@ -262,7 +284,7 @@ fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
match start.cmp(&end) {
Ordering::Equal => Value::Binary { val: vec![], span },
Ordering::Greater => Value::Error {
error: ShellError::UnsupportedInput(
error: ShellError::TypeMismatch(
"End must be greater than or equal to Start".to_string(),
arg.arg_span,
),

View File

@ -52,10 +52,12 @@ impl Command for BytesBuild {
let val = eval_expression(engine_state, stack, expr)?;
match val {
Value::Binary { mut val, .. } => output.append(&mut val),
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => {
return Err(ShellError::UnsupportedInput(
"only support expression which yields to binary data".to_string(),
other.span().unwrap_or(call.head),
return Err(ShellError::TypeMismatch(
"only binary data arguments are supported".to_string(),
other.expect_span(),
))
}
}

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
Category, IntoPipelineData, PipelineData, Signature, Type, Value,
};
#[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Bytes {
}
fn signature(&self) -> Signature {
Signature::build("bytes").category(Category::Bytes)
Signature::build("bytes")
.category(Category::Bytes)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
"Various commands for working with byte data"
}
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run(
&self,
engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Bytes {
.into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use crate::Bytes;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Bytes {})
}
}

View File

@ -54,14 +54,16 @@ impl Command for BytesCollect {
output_binary.append(&mut work_sep)
}
}
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => return Err(error),
other => {
return Err(ShellError::UnsupportedInput(
format!(
"The element type is {}, this command only works with bytes.",
other.get_type()
),
other.span().unwrap_or(call.head),
))
return Err(ShellError::OnlySupportsThisInputType(
"integer".into(),
other.get_type().to_string(),
call.head,
// This line requires the Value::Error match above.
other.expect_span(),
));
}
}
}

View File

@ -68,17 +68,17 @@ impl Command for BytesEndsWith {
Example {
description: "Checks if binary ends with `0x[AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
result: Some(Value::boolean(true, Span::test_data())),
result: Some(Value::test_bool(true)),
},
Example {
description: "Checks if binary ends with `0x[FF AA AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
result: Some(Value::boolean(true, Span::test_data())),
result: Some(Value::test_bool(true)),
},
Example {
description: "Checks if binary ends with `0x[11]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
result: Some(Value::boolean(false, Span::test_data())),
result: Some(Value::test_bool(false)),
},
]
}
@ -90,13 +90,15 @@ fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => Value::boolean(val.ends_with(&args.pattern), *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -132,13 +132,15 @@ fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => index_of_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -71,13 +71,15 @@ fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
val,
span: val_span,
} => Value::int(val.len() as i64, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -61,7 +61,7 @@ impl Command for BytesRemove {
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if pattern_to_remove.item.is_empty() {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"the pattern to remove cannot be empty".to_string(),
pattern_to_remove.span,
));
@ -139,13 +139,15 @@ fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => remove_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}
@ -191,7 +193,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
right += 1;
}
}
// append the remaing thing to result, this can happened when
// append the remaining thing to result, this can happened when
// we have something to remove and remove_all is False.
let mut remain = input[left..].to_vec();
result.append(&mut remain);

View File

@ -61,7 +61,7 @@ impl Command for BytesReplace {
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if find.item.is_empty() {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"the pattern to find cannot be empty".to_string(),
find.span,
));
@ -130,13 +130,15 @@ fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => replace_impl(val, args, *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -81,13 +81,15 @@ fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
span: *val_span,
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -74,17 +74,17 @@ impl Command for BytesStartsWith {
Example {
description: "Checks if binary starts with `0x[1F FF AA]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
result: Some(Value::boolean(true, Span::test_data())),
result: Some(Value::test_bool(true)),
},
Example {
description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
result: Some(Value::boolean(true, Span::test_data())),
result: Some(Value::test_bool(true)),
},
Example {
description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
result: Some(Value::boolean(false, Span::test_data())),
result: Some(Value::test_bool(false)),
},
]
}
@ -96,13 +96,15 @@ fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
val,
span: val_span,
} => Value::boolean(val.starts_with(&args.pattern), *val_span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => val.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
error: ShellError::OnlySupportsThisInputType(
"binary".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -78,13 +78,14 @@ impl HashableValue {
Value::String { val, span } => Ok(HashableValue::String { val, span }),
Value::Binary { val, span } => Ok(HashableValue::Binary { val, span }),
_ => {
let input_span = value.span().unwrap_or(span);
Err(ShellError::UnsupportedInput(
format!("input value {value:?} is not hashable"),
input_span,
))
}
// Explicitly propagate errors instead of dropping them.
Value::Error { error } => Err(error),
_ => Err(ShellError::UnsupportedInput(
"input value is not hashable".into(),
format!("input type: {:?}", value.get_type()),
span,
value.expect_span(),
)),
}
}

View File

@ -98,12 +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 span = inner.span;
if ["value", "count", "quantile", "percentage"].contains(&inner.item.as_str()) {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"frequency-column-name can't be 'value', 'count' or 'percentage'"
.to_string(),
span,
inner.span,
));
}
inner.item
@ -119,7 +118,7 @@ impl Command for Histogram {
"normalize" => PercentageCalcMethod::Normalize,
"relative" => PercentageCalcMethod::Relative,
_ => {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"calc method can only be 'normalize' or 'relative'".to_string(),
inner.span,
))
@ -137,6 +136,8 @@ impl Command for Histogram {
frequency_column_name,
calc_method,
span,
// Note that as_list() filters out Value::Error here.
data_as_value.expect_span(),
),
Err(e) => Err(e),
}
@ -149,6 +150,7 @@ fn run_histogram(
freq_column: String,
calc_method: PercentageCalcMethod,
head_span: Span,
list_span: Span,
) -> Result<PipelineData, ShellError> {
let mut inputs = vec![];
// convert from inputs to hashable values.
@ -157,14 +159,24 @@ fn run_histogram(
// some invalid input scenario needs to handle:
// Expect input is a list of hashable value, if one value is not hashable, throw out error.
for v in values {
let current_span = v.span().unwrap_or(head_span);
inputs.push(HashableValue::from_value(v, head_span).map_err(|_| {
ShellError::UnsupportedInput(
"--column-name is not provided, can only support a list of simple value."
.to_string(),
current_span,
)
})?);
match v {
// Propagate existing errors.
Value::Error { error } => return Err(error),
_ => {
let t = v.get_type();
let span = v.expect_span();
inputs.push(HashableValue::from_value(v, head_span).map_err(|_| {
ShellError::UnsupportedInput(
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
format!(
"input type: {:?}", t
),
head_span,
span,
)
})?)
}
}
}
}
Some(ref col) => {
@ -186,14 +198,17 @@ fn run_histogram(
}
}
}
// Propagate existing errors.
Value::Error { error } => return Err(error),
_ => continue,
}
}
if inputs.is_empty() {
return Err(ShellError::UnsupportedInput(
format!("expect input is table, and inputs doesn't contain any value which has {col_name} column"),
return Err(ShellError::CantFindColumn(
col_name.clone(),
head_span,
list_span,
));
}
}

View File

@ -44,14 +44,14 @@ impl Command for Fmt {
"upperhex".into(),
],
vals: vec![
Value::string("0b101010", Span::test_data()),
Value::string("42", Span::test_data()),
Value::string("42", Span::test_data()),
Value::string("4.2e1", Span::test_data()),
Value::string("0x2a", Span::test_data()),
Value::string("0o52", Span::test_data()),
Value::string("4.2E1", Span::test_data()),
Value::string("0x2A", Span::test_data()),
Value::test_string("0b101010"),
Value::test_string("42"),
Value::test_string("42"),
Value::test_string("4.2e1"),
Value::test_string("0x2a"),
Value::test_string("0o52"),
Value::test_string("4.2E1"),
Value::test_string("0x2A"),
],
span: Span::test_data(),
}),
@ -84,10 +84,15 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Int { val, .. } => fmt_it(*val, span),
Value::Filesize { val, .. } => fmt_it(*val, span),
_ => Value::Error {
error: ShellError::UnsupportedInput(
format!("unsupported input type: {:?}", input.get_type()),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer or filesize".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -177,13 +177,24 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
val: int_to_endian(i64::from(*val)),
span,
},
Value::Duration { val, .. } => Value::Binary {
val: int_to_endian(*val),
span,
},
Value::Date { val, .. } => Value::Binary {
val: val.format("%c").to_string().as_bytes().to_vec(),
span,
},
_ => Value::Error {
error: ShellError::UnsupportedInput("'into binary' for unsupported type".into(), span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer, float, filesize, string, date, duration, binary or bool".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}
}

View File

@ -163,10 +163,15 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
Ok(val) => Value::Bool { val, span },
Err(error) => Value::Error { error },
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"'into bool' does not support this input".into(),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"bool, integer, float or string".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
Category, IntoPipelineData, PipelineData, Signature, Type, Value,
};
#[derive(Clone)]
@ -14,13 +14,19 @@ impl Command for Into {
}
fn signature(&self) -> Signature {
Signature::build("into").category(Category::Conversions)
Signature::build("into")
.category(Category::Conversions)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
"Commands to convert data from one type to another."
}
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run(
&self,
engine_state: &EngineState,
@ -41,15 +47,3 @@ impl Command for Into {
.into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Into {})
}
}

View File

@ -147,41 +147,19 @@ impl Command for SubCommand {
}
fn examples(&self) -> Vec<Example> {
let example_result_1 = |secs: i64, nsecs: u32| {
let dt = match Utc.timestamp_opt(secs, nsecs) {
LocalResult::Single(dt) => Some(dt),
_ => None,
};
match dt {
Some(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
None => Some(Value::Error {
error: ShellError::UnsupportedInput(
"The given datetime representation is unsupported.".to_string(),
Span::test_data(),
),
}),
}
let example_result_1 = |secs: i64, nsecs: u32| match Utc.timestamp_opt(secs, nsecs) {
LocalResult::Single(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
_ => panic!("datetime: help example is invalid"),
};
let example_result_2 = |millis: i64| {
let dt = match Utc.timestamp_millis_opt(millis) {
LocalResult::Single(dt) => Some(dt),
_ => None,
};
match dt {
Some(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
None => Some(Value::Error {
error: ShellError::UnsupportedInput(
"The given datetime representation is unsupported.".to_string(),
Span::test_data(),
),
}),
}
let example_result_2 = |millis: i64| match Utc.timestamp_millis_opt(millis) {
LocalResult::Single(dt) => Some(Value::Date {
val: dt.into(),
span: Span::test_data(),
}),
_ => panic!("datetime: help example is invalid"),
};
vec![
Example {
@ -213,7 +191,7 @@ impl Command for SubCommand {
},
Example {
description:
"Convert timestamps like the sqlite history t",
"Convert a millisecond-precise timestamp",
example: "1656165681720 | into datetime",
result: example_result_2(1656165681720)
},
@ -231,11 +209,16 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let timestamp = match input {
Value::Int { val, .. } => Ok(*val),
Value::String { val, .. } => val.parse::<i64>(),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => return input.clone(),
other => {
return Value::Error {
error: ShellError::UnsupportedInput(
format!("Expected string or int, got {} instead", other.get_type()),
error: ShellError::OnlySupportsThisInputType(
"string and integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
};
}
@ -248,113 +231,68 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
if ts.abs() > TIMESTAMP_BOUND {
return Value::Error {
error: ShellError::UnsupportedInput(
"Given timestamp is out of range, it should between -8e+12 and 8e+12"
.to_string(),
"timestamp is out of range; it should between -8e+12 and 8e+12".to_string(),
format!("timestamp is {:?}", ts),
head,
// Again, can safely unwrap this from here on
input.expect_span(),
),
};
}
macro_rules! match_datetime {
($expr:expr) => {
match $expr {
LocalResult::Single(dt) => Value::Date {
val: dt.into(),
span: head,
},
_ => {
return Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".into(),
format!("timestamp is {:?}", ts),
head,
head,
),
};
}
}
};
}
return match timezone {
// default to UTC
None => {
// be able to convert chrono::Utc::now()
let dt = match ts.to_string().len() {
x if x > 13 => Utc.timestamp_nanos(ts).into(),
x if x > 10 => match Utc.timestamp_millis_opt(ts) {
LocalResult::Single(dt) => dt.into(),
_ => {
return Value::Error {
// This error message is from chrono
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
head,
),
};
}
match ts.to_string().len() {
x if x > 13 => Value::Date {
val: Utc.timestamp_nanos(ts).into(),
span: head,
},
_ => match Utc.timestamp_opt(ts, 0) {
LocalResult::Single(dt) => dt.into(),
_ => {
return Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid."
.to_string(),
head,
),
}
}
},
};
Value::Date {
val: dt,
span: head,
x if x > 10 => match_datetime!(Utc.timestamp_millis_opt(ts)),
_ => match_datetime!(Utc.timestamp_opt(ts, 0)),
}
}
Some(Spanned { item, span }) => match item {
Zone::Utc => match Utc.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date {
val: val.into(),
span: head,
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Zone::Local => match Local.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date {
val: val.into(),
span: head,
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Zone::Utc => match_datetime!(Utc.timestamp_opt(ts, 0)),
Zone::Local => match_datetime!(Local.timestamp_opt(ts, 0)),
Zone::East(i) => match FixedOffset::east_opt((*i as i32) * HOUR) {
Some(eastoffset) => match eastoffset.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date { val, span: head },
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Some(eastoffset) => match_datetime!(eastoffset.timestamp_opt(ts, 0)),
None => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
error: ShellError::DatetimeParseError(*span),
},
},
Zone::West(i) => match FixedOffset::west_opt((*i as i32) * HOUR) {
Some(westoffset) => match westoffset.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date { val, span: head },
_ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Some(westoffset) => match_datetime!(westoffset.timestamp_opt(ts, 0)),
None => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
error: ShellError::DatetimeParseError(*span),
},
},
Zone::Error => Value::Error {
error: ShellError::UnsupportedInput(
"Cannot convert given timezone or offset to timestamp".to_string(),
// This is an argument error, not an input error
error: ShellError::TypeMismatch(
"Invalid timezone or offset".to_string(),
*span,
),
},
@ -391,10 +329,15 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
},
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!("Expected string, got {} instead", other.get_type()),
error: ShellError::OnlySupportsThisInputType(
"string".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -105,18 +105,17 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
},
span: *span,
},
other => {
let span = other.span();
match span {
Ok(s) => {
let got = format!("Expected a string, got {} instead", other.get_type());
Value::Error {
error: ShellError::UnsupportedInput(got, s),
}
}
Err(e) => Value::Error { error: e },
}
}
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"string, integer or bool".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}
}

View File

@ -468,9 +468,11 @@ fn action(
}
} else {
Value::Error {
error: ShellError::UnsupportedInput(
"'into duration' does not support this string input".into(),
error: ShellError::CantConvert(
"string".into(),
"duration".into(),
span,
None,
),
}
}
@ -481,10 +483,15 @@ fn action(
}
}
}
_ => Value::Error {
error: ShellError::UnsupportedInput(
"'into duration' does not support this input".into(),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"string or duration".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}

View File

@ -116,20 +116,18 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
val: 0,
span: value_span,
},
_ => Value::Error {
error: ShellError::UnsupportedInput(
"'into filesize' for unsupported type".into(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"string and integer".into(),
other.get_type().to_string(),
span,
value_span,
),
},
}
} else {
Value::Error {
error: ShellError::UnsupportedInput(
"'into filesize' for unsupported type".into(),
span,
),
}
// Propagate existing errors
input.clone()
}
}
fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {

View File

@ -70,7 +70,7 @@ impl Command for SubCommand {
let radix: u32 = match radix {
Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"Radix must lie in the range [2, 36]".to_string(),
span,
));
@ -113,7 +113,7 @@ impl Command for SubCommand {
Example {
description: "Convert file size to integer",
example: "4KB | into int",
result: Some(Value::int(4000, Span::test_data())),
result: Some(Value::test_int(4000)),
},
Example {
description: "Convert bool to integer",
@ -187,9 +187,11 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Ok(v) => v,
_ => {
return Value::Error {
error: ShellError::UnsupportedInput(
"Could not convert float to integer".to_string(),
error: ShellError::CantConvert(
"float".to_string(),
"integer".to_string(),
span,
None,
),
}
}
@ -219,6 +221,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
val: val.timestamp(),
span,
},
Value::Duration { val, .. } => Value::Int { val: *val, span },
Value::Binary { val, span } => {
use byteorder::{BigEndian, ByteOrder, LittleEndian};
@ -240,10 +243,15 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::int(BigEndian::read_i64(&val), *span)
}
}
_ => Value::Error {
error: ShellError::UnsupportedInput(
format!("'into int' for unsupported type '{}'", input.get_type()),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"integer, float, filesize, date, string, binary, duration or bool".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
}
@ -281,13 +289,18 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
}
val.to_string()
}
_ => {
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => return input.clone(),
other => {
return Value::Error {
error: ShellError::UnsupportedInput(
"only strings or integers are supported".to_string(),
error: ShellError::OnlySupportsThisInputType(
"string and integer".into(),
other.get_type().to_string(),
head,
// This line requires the Value::Error match above.
other.expect_span(),
),
}
};
}
};
match i64::from_str_radix(i.trim(), radix) {

View File

@ -183,12 +183,16 @@ fn into_record(
Value::Record { cols, vals, span }
}
Value::Record { cols, vals, span } => Value::Record { cols, vals, span },
other => {
return Err(ShellError::UnsupportedInput(
"'into record' does not support this input".into(),
other.span().unwrap_or(call.head),
))
}
Value::Error { .. } => input,
other => Value::Error {
error: ShellError::OnlySupportsThisInputType(
"string".into(),
other.get_type().to_string(),
call.head,
// This line requires the Value::Error match above.
other.expect_span(),
),
},
};
Ok(res.into_pipeline_data())
}

View File

@ -79,22 +79,22 @@ impl Command for SubCommand {
Example {
description: "convert integer to string and append three decimal places",
example: "5 | into string -d 3",
result: Some(Value::string("5.000", Span::test_data())),
result: Some(Value::test_string("5.000")),
},
Example {
description: "convert decimal to string and round to nearest integer",
example: "1.7 | into string -d 0",
result: Some(Value::string("2", Span::test_data())),
result: Some(Value::test_string("2")),
},
Example {
description: "convert decimal to string",
example: "1.7 | into string -d 1",
result: Some(Value::string("1.7", Span::test_data())),
result: Some(Value::test_string("1.7")),
},
Example {
description: "convert decimal to string and limit to 2 decimals",
example: "1.734 | into string -d 2",
result: Some(Value::string("1.73", Span::test_data())),
result: Some(Value::test_string("1.73")),
},
Example {
description: "try to convert decimal to string and provide negative decimal points",
@ -111,17 +111,17 @@ impl Command for SubCommand {
Example {
description: "convert decimal to string",
example: "4.3 | into string",
result: Some(Value::string("4.3", Span::test_data())),
result: Some(Value::test_string("4.3")),
},
Example {
description: "convert string to string",
example: "'1234' | into string",
result: Some(Value::string("1234", Span::test_data())),
result: Some(Value::test_string("1234")),
},
Example {
description: "convert boolean to string",
example: "true | into string",
result: Some(Value::string("true", Span::test_data())),
result: Some(Value::test_string("true")),
},
// TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032
// Example {
@ -154,7 +154,7 @@ fn string_helper(
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() {
return Err(ShellError::UnsupportedInput(
return Err(ShellError::TypeMismatch(
"Cannot accept negative integers for decimals arguments".to_string(),
head,
));
@ -251,9 +251,11 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
vals: _,
span: _,
} => Value::Error {
error: ShellError::UnsupportedInput(
"Cannot convert Record into string".to_string(),
error: ShellError::CantConvert(
"record".into(),
"string".into(),
span,
Some("try using the `to nuon` command".into()),
),
},
Value::Binary { .. } => Value::Error {

View File

@ -0,0 +1,104 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Const;
impl Command for Const {
fn name(&self) -> &str {
"const"
}
fn usage(&self) -> &str {
"Create a parse-time constant."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("const")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("const_name", SyntaxShape::VarWithOptType, "constant name")
.required(
"initial_value",
SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)),
"equals sign followed by constant value",
)
.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 search_terms(&self) -> Vec<&str> {
vec!["set", "let"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let var_id = call
.positional_nth(0)
.expect("checked through parser")
.as_var()
.expect("internal error: missing variable");
if let Some(constval) = engine_state.find_constant(var_id, &[]) {
// Instead of creating a second copy of the value in the stack, we could change
// stack.get_var() to check engine_state.find_constant().
stack.add_var(var_id, constval.clone());
Ok(PipelineData::empty())
} else {
Err(ShellError::NushellFailedSpanned(
"Missing Constant".to_string(),
"constant not added by the parser".to_string(),
call.head,
))
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create a new parse-time constant.",
example: "const x = 10",
result: None,
},
Example {
description: "Create a composite constant value",
example: "const x = { a: 10, b: 20 }",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use nu_protocol::engine::CommandType;
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Const {})
}
#[test]
fn test_command_type() {
assert!(matches!(Const.command_type(), CommandType::Keyword));
}
}

View File

@ -39,6 +39,8 @@ impl Command for Debug {
let config = engine_state.get_config().clone();
let raw = call.has_flag("raw");
// Should PipelineData::Empty result in an error here?
input.map(
move |x| {
if raw {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct DefEnv;
@ -72,7 +72,7 @@ def-env cd_with_fallback [arg = ""] {
vec![Example {
description: "Set environment variable by call a custom command",
example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::string("BAZ", Span::test_data())),
result: Some(Value::test_string("BAZ")),
}]
}
}

View File

@ -19,6 +19,11 @@ impl Command for Describe {
fn signature(&self) -> Signature {
Signature::build("describe")
.input_output_types(vec![(Type::Any, Type::String)])
.switch(
"no-collect",
"do not collect streams of structured data",
Some('n'),
)
.category(Category::Core)
}
@ -30,32 +35,58 @@ impl Command for Describe {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
if matches!(input, PipelineData::ExternalStream { .. }) {
Ok(PipelineData::Value(
Value::string("raw input", call.head),
None,
))
} else {
let value = input.into_value(call.head);
let description = match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
};
Ok(Value::String {
val: description,
span: head,
let no_collect: bool = call.has_flag("no-collect");
let description = match input {
PipelineData::ExternalStream { .. } => "raw input".into(),
PipelineData::ListStream(_, _) => {
if no_collect {
"stream".into()
} else {
let value = input.into_value(head);
let base_description = match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
};
format!("{base_description} (stream)")
}
}
.into_pipeline_data())
_ => {
let value = input.into_value(head);
match value {
Value::CustomValue { val, .. } => val.value_string(),
_ => value.get_type().to_string(),
}
}
};
Ok(Value::String {
val: description,
span: head,
}
.into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Describe the type of a string",
example: "'hello' | describe",
result: Some(Value::test_string("string")),
}]
vec![
Example {
description: "Describe the type of a string",
example: "'hello' | describe",
result: Some(Value::test_string("string")),
},
Example {
description: "Describe a stream of data, collecting it first",
example: "[1 2 3] | each {|i| $i} | describe",
result: Some(Value::test_string("list<int> (stream)")),
},
Example {
description: "Describe the input but do not collect streams",
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
result: Some(Value::test_string("stream")),
},
]
}
fn search_terms(&self) -> Vec<&str> {

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -15,6 +15,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, "the error to create")
.switch(
"unspanned",

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value,
Category, Example, IntoPipelineData, PipelineData, Signature, Type, Value,
};
#[derive(Clone)]
@ -56,7 +56,7 @@ impl Command for ExportCommand {
vec![Example {
description: "Export a definition from a module",
example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#,
result: Some(Value::string("hello", Span::test_data())),
result: Some(Value::test_string("hello")),
}]
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportDef;
@ -46,7 +46,7 @@ impl Command for ExportDef {
vec![Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::string("foo", Span::test_data())),
result: Some(Value::test_string("foo")),
}]
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportDefEnv;
@ -72,7 +72,7 @@ export def-env cd_with_fallback [arg = ""] {
vec![Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::string("BAZ", Span::test_data())),
result: Some(Value::test_string("BAZ")),
}]
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct ExportUse;
@ -17,7 +17,12 @@ impl Command for ExportUse {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
@ -48,7 +53,7 @@ impl Command for ExportUse {
use eggs foo
foo
"#,
result: Some(Value::string("foo", Span::test_data())),
result: Some(Value::test_string("foo")),
}]
}

View File

@ -2,7 +2,7 @@ use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -19,6 +19,8 @@ impl Command for For {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("for")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required(
"var_name",
SyntaxShape::VarWithOptType,

View File

@ -1,17 +1,18 @@
use crate::help_aliases::help_aliases;
use crate::help_commands::help_commands;
use crate::help_modules::help_modules;
use fancy_regex::Regex;
use nu_ansi_term::{
Color::{Red, White},
Style,
};
use nu_color_config::StyleComputer;
use nu_engine::{get_full_help, CallExt};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
span, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value,
};
use std::borrow::Borrow;
#[derive(Clone)]
pub struct Help;
@ -26,7 +27,7 @@ impl Command for Help {
.rest(
"rest",
SyntaxShape::String,
"the name of command to get help on",
"the name of command, alias or module to get help on",
)
.named(
"find",
@ -38,7 +39,11 @@ impl Command for Help {
}
fn usage(&self) -> &str {
"Display help information about commands."
"Display help information about different parts of Nushell."
}
fn extra_usage(&self) -> &str {
r#"`help word` searches for "word" in commands, aliases and modules, in that order."#
}
fn run(
@ -48,269 +53,18 @@ impl Command for Help {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help(engine_state, stack, call)
}
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)?;
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all commands and sub-commands",
example: "help commands",
result: None,
},
Example {
description: "show help for single command",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command names, usage and search terms",
example: "help --find char",
result: None,
},
]
}
}
fn help(
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)?;
let commands = engine_state.get_decl_ids_sorted(false);
// 🚩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 org_search_string = f.item.clone();
let search_string = f.item.to_lowercase();
let mut found_cmds_vec = Vec::new();
for decl_id in commands {
let mut cols = vec![];
let mut vals = vec![];
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
let matches_term = if !search_terms.is_empty() {
search_terms
.iter()
.any(|term| term.to_lowercase().contains(&search_string))
} else {
false
};
let key_match = key.to_lowercase().contains(&search_string);
let usage_match = usage.to_lowercase().contains(&search_string);
if key_match || usage_match || matches_term {
cols.push("name".into());
vals.push(Value::String {
val: if key_match {
highlight_search_string(&key, &org_search_string, &string_style)?
} else {
key
},
span: head,
});
cols.push("category".into());
vals.push(Value::string(sig.category.to_string(), head));
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
cols.push("usage".into());
vals.push(Value::String {
val: if usage_match {
highlight_search_string(&usage, &org_search_string, &string_style)?
} else {
usage
},
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: if matches_term {
search_terms
.iter()
.map(|term| {
if term.to_lowercase().contains(&search_string) {
match highlight_search_string(
term,
&org_search_string,
&string_style,
) {
Ok(s) => s,
Err(_) => {
string_style.paint(term.to_string()).to_string()
}
}
} else {
string_style.paint(term.to_string()).to_string()
}
})
.collect::<Vec<_>>()
.join(", ")
} else {
search_terms.join(", ")
},
span: head,
}
});
found_cmds_vec.push(Value::Record {
cols,
vals,
span: head,
});
}
}
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if !rest.is_empty() {
let mut found_cmds_vec = Vec::new();
if rest[0].item == "commands" {
for decl_id in commands {
let mut cols = vec![];
let mut vals = vec![];
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
cols.push("name".into());
vals.push(Value::String {
val: key,
span: head,
});
cols.push("category".into());
vals.push(Value::string(sig.category.to_string(), head));
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span: head,
});
cols.push("usage".into());
vals.push(Value::String {
val: usage,
span: head,
});
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span: head,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(head)
} else {
Value::String {
val: search_terms.join(", "),
span: head,
}
});
found_cmds_vec.push(Value::Record {
cols,
vals,
span: 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,
])))
}
}
} else {
let msg = r#"Welcome to Nushell.
if rest.is_empty() && find.is_none() {
let msg = r#"Welcome to Nushell.
Here are some tips to help you get started.
* help -h or help help - show available `help` subcommands and examples
* help commands - list all available commands
* help <command name> - display help about a particular command
* help --find <text to search> - search through all of help
* help <name> - display help about a particular command, alias, or module
* help --find <text to search> - search through all help commands table
Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character.
Each stage in the pipeline works together to load, parse, and display information to you.
@ -328,8 +82,111 @@ Get the processes on your system actively using CPU:
You can also learn more at https://www.nushell.sh/book/"#;
Ok(Value::string(msg, head).into_pipeline_data())
Ok(Value::string(msg, head).into_pipeline_data())
} else if find.is_some() {
help_commands(engine_state, stack, call)
} else {
let result = help_commands(engine_state, stack, call);
let result = if let Err(ShellError::CommandNotFound(_)) = result {
help_aliases(engine_state, stack, call)
} else {
result
};
let result = if let Err(ShellError::AliasNotFound(_)) = result {
help_modules(engine_state, stack, call)
} else {
result
};
if let Err(ShellError::ModuleNotFoundAtRuntime(_, _)) = result {
let rest_spans: Vec<Span> = rest.iter().map(|arg| arg.span).collect();
Err(ShellError::NotFound(span(&rest_spans)))
} else {
result
}
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show help for single command, alias, or module",
example: "help match",
result: None,
},
Example {
description: "show help for single sub-command, alias, or module",
example: "help str lpad",
result: None,
},
Example {
description: "search for string in command names, usage and search terms",
example: "help --find char",
result: None,
},
]
}
}
pub fn highlight_search_in_table(
table: Vec<Value>, // list of records
search_string: &str,
searched_cols: &[&str],
string_style: &Style,
) -> Result<Vec<Value>, ShellError> {
let orig_search_string = search_string;
let search_string = search_string.to_lowercase();
let mut matches = vec![];
for record in table {
let (cols, mut vals, record_span) = if let Value::Record { cols, vals, span } = record {
(cols, vals, span)
} else {
return Err(ShellError::NushellFailedSpanned(
"Expected record".to_string(),
format!("got {}", record.get_type()),
record.span()?,
));
};
let has_match = cols.iter().zip(vals.iter_mut()).fold(
Ok(false),
|acc: Result<bool, ShellError>, (col, val)| {
if searched_cols.contains(&col.as_str()) {
if let Value::String { val: s, span } = val {
if s.to_lowercase().contains(&search_string) {
*val = Value::String {
val: highlight_search_string(s, orig_search_string, string_style)?,
span: *span,
};
Ok(true)
} else {
// column does not contain the searched string
acc
}
} else {
// ignore non-string values
acc
}
} else {
// don't search this column
acc
}
},
)?;
if has_match {
matches.push(Value::Record {
cols,
vals,
span: record_span,
});
}
}
Ok(matches)
}
// Highlight the search string using ANSI escape sequences and regular expressions.

View File

@ -0,0 +1,181 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{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,
};
use std::borrow::Cow;
#[derive(Clone)]
pub struct HelpAliases;
impl Command for HelpAliases {
fn name(&self) -> &str {
"help aliases"
}
fn usage(&self) -> &str {
"Show help on nushell aliases."
}
fn signature(&self) -> Signature {
Signature::build("help aliases")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of alias to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in alias 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 aliases",
example: "help aliases",
result: None,
},
Example {
description: "show help for single alias",
example: "help aliases my-alias",
result: None,
},
Example {
description: "search for string in alias names and usages",
example: "help aliases --find my-alias",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_aliases(engine_state, stack, call)
}
}
pub fn help_aliases(
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_aliases(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_aliases(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 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 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_usage = engine_state.build_alias_usage(alias_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
const C: &str = "\x1b[36m"; // cyan
const RESET: &str = "\x1b[0m"; // reset
let mut long_desc = String::new();
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}"));
long_desc.push_str("\n\n");
long_desc.push_str(&format!("{G}Expansion{RESET}:\n {alias_expansion}"));
let config = engine_state.get_config();
if !config.use_ansi_coloring {
long_desc = nu_utils::strip_ansi_string_likely(long_desc);
}
Ok(Value::String {
val: long_desc,
span: call.head,
}
.into_pipeline_data())
}
}
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_aliases();
scope_data.collect_aliases(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpAliases;
use crate::test_examples;
test_examples(HelpAliases {})
}
}

View File

@ -0,0 +1,189 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{get_full_help, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use std::borrow::Borrow;
#[derive(Clone)]
pub struct HelpCommands;
impl Command for HelpCommands {
fn name(&self) -> &str {
"help commands"
}
fn usage(&self) -> &str {
"Show help on nushell commands."
}
fn signature(&self) -> Signature {
Signature::build("help commands")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of command to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in command names, usage, and search terms",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_commands(engine_state, stack, call)
}
}
pub fn help_commands(
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_commands(engine_state, head);
let found_cmds_vec = highlight_search_in_table(
all_cmds_vec,
&f.item,
&["name", "usage", "search_terms"],
&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_commands(engine_state, 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_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
let commands = engine_state.get_decls_sorted(false);
let mut found_cmds_vec = Vec::new();
for (name_bytes, decl_id) in commands {
let mut cols = vec![];
let mut vals = vec![];
let name = String::from_utf8_lossy(&name_bytes).to_string();
let decl = engine_state.get_decl(decl_id);
let sig = decl.signature().update_from_command(name, decl.borrow());
let signatures = sig.to_string();
let key = sig.name;
let usage = sig.usage;
let search_terms = sig.search_terms;
cols.push("name".into());
vals.push(Value::String { val: key, span });
cols.push("category".into());
vals.push(Value::string(sig.category.to_string(), span));
cols.push("command_type".into());
vals.push(Value::String {
val: format!("{:?}", decl.command_type()).to_lowercase(),
span,
});
cols.push("usage".into());
vals.push(Value::String { val: usage, span });
cols.push("signatures".into());
vals.push(Value::String {
val: if decl.is_parser_keyword() {
"".to_string()
} else {
signatures
},
span,
});
cols.push("search_terms".into());
vals.push(if search_terms.is_empty() {
Value::nothing(span)
} else {
Value::String {
val: search_terms.join(", "),
span,
}
});
found_cmds_vec.push(Value::Record { cols, vals, span });
}
found_cmds_vec
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpCommands;
use crate::test_examples;
test_examples(HelpCommands {})
}
}

View File

@ -0,0 +1,258 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, AliasId, Category, DeclId, Example, IntoInterruptiblePipelineData, IntoPipelineData,
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct HelpModules;
impl Command for HelpModules {
fn name(&self) -> &str {
"help modules"
}
fn usage(&self) -> &str {
"Show help on nushell modules."
}
fn extra_usage(&self) -> &str {
r#"When requesting help for a single module, its commands and aliases will be highlighted if they
are also available in the current scope. Commands/aliases that were imported under a different name
(such as with a prefix after `use some-module`) will be highlighted in parentheses."#
}
fn signature(&self) -> Signature {
Signature::build("help modules")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of module to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in module 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 modules",
example: "help modules",
result: None,
},
Example {
description: "show help for single module",
example: "help modules my-module",
result: None,
},
Example {
description: "search for string in module names and usages",
example: "help modules --find my-module",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_modules(engine_state, stack, call)
}
}
pub fn help_modules(
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_modules(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_modules(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 module_id = if let Some(id) = engine_state.find_module(name.as_bytes(), &[]) {
id
} else {
return Err(ShellError::ModuleNotFoundAtRuntime(
name,
span(&rest.iter().map(|r| r.span).collect::<Vec<Span>>()),
));
};
let module = engine_state.get_module(module_id);
let module_usage = engine_state.build_module_usage(module_id);
// TODO: merge this into documentation.rs at some point
const G: &str = "\x1b[32m"; // green
const C: &str = "\x1b[36m"; // cyan
const CB: &str = "\x1b[1;36m"; // cyan bold
const RESET: &str = "\x1b[0m"; // reset
let mut long_desc = String::new();
if let Some((usage, extra_usage)) = module_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}Module{RESET}: {C}{name}{RESET}"));
long_desc.push_str("\n\n");
if !module.decls.is_empty() {
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
let mut module_commands: Vec<(&[u8], DeclId)> = module
.decls
.iter()
.map(|(name, id)| (name.as_ref(), *id))
.collect();
module_commands.sort_by(|a, b| a.0.cmp(b.0));
let commands_str = module_commands
.iter()
.map(|(name_bytes, id)| {
let name = String::from_utf8_lossy(name_bytes);
if let Some((used_name_bytes, _)) =
commands.iter().find(|(_, decl_id)| id == decl_id)
{
if engine_state.find_decl(name.as_bytes(), &[]).is_some() {
format!("{CB}{name}{RESET}")
} else {
let command_name = String::from_utf8_lossy(used_name_bytes);
format!("{name} ({CB}{command_name}{RESET})")
}
} else {
format!("{name}")
}
})
.collect::<Vec<String>>()
.join(", ");
long_desc.push_str(&format!("{G}Exported commands{RESET}:\n {commands_str}"));
long_desc.push_str("\n\n");
}
if !module.aliases.is_empty() {
let aliases: Vec<(Vec<u8>, AliasId)> = engine_state.get_aliases_sorted(false).collect();
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));
let aliases_str = module_aliases
.iter()
.map(|(name_bytes, id)| {
let name = String::from_utf8_lossy(name_bytes);
if let Some((used_name_bytes, _)) =
aliases.iter().find(|(_, alias_id)| id == alias_id)
{
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);
format!("{name} ({CB}{alias_name}{RESET})")
}
} else {
format!("{name}")
}
})
.collect::<Vec<String>>()
.join(", ");
long_desc.push_str(&format!("{G}Exported aliases{RESET}:\n {aliases_str}"));
long_desc.push_str("\n\n");
}
if module.env_block.is_some() {
long_desc.push_str(&format!("This module {C}exports{RESET} environment."));
} else {
long_desc.push_str(&format!(
"This module {C}does not export{RESET} environment."
));
}
let config = engine_state.get_config();
if !config.use_ansi_coloring {
long_desc = nu_utils::strip_ansi_string_likely(long_desc);
}
Ok(Value::String {
val: long_desc,
span: call.head,
}
.into_pipeline_data())
}
}
fn build_help_modules(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope_data = ScopeData::new(engine_state, stack);
scope_data.populate_modules();
scope_data.collect_modules(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpModules;
use crate::test_examples;
test_examples(HelpModules {})
}
}

View File

@ -1,7 +1,7 @@
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
Category, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
@ -17,7 +17,10 @@ impl Command for HelpOperators {
}
fn signature(&self) -> Signature {
Signature::build("help operators").category(Category::Core)
Signature::build("help operators")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn run(

View File

@ -1,8 +1,6 @@
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Hide;
@ -15,7 +13,12 @@ impl Command for Hide {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
@ -36,32 +39,11 @@ This command is a parser keyword. For details, check:
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let env_var_name = if let Some(Expression {
expr: Expr::ImportPattern(pat),
..
}) = call.positional_nth(0)
{
Spanned {
item: String::from_utf8_lossy(&pat.head.name).to_string(),
span: pat.head.span,
}
} else {
return Err(ShellError::GenericError(
"Unexpected import".into(),
"import pattern not supported".into(),
Some(call.head),
None,
Vec::new(),
));
};
stack.remove_env_var(engine_state, &env_var_name.item);
Ok(PipelineData::empty())
}
@ -77,11 +59,6 @@ This command is a parser keyword. For details, check:
example: r#"def say-hi [] { echo 'Hi!' }; hide say-hi"#,
result: None,
},
Example {
description: "Hide an environment variable",
example: r#"let-env HZ_ENV_ABC = 1; hide HZ_ENV_ABC; 'HZ_ENV_ABC' in (env).name"#,
result: Some(Value::boolean(false, Span::test_data())),
},
]
}
}

View File

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value,
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
Type, Value,
};
#[derive(Clone)]
@ -70,7 +70,7 @@ impl Command for HideEnv {
vec![Example {
description: "Hide an environment variable",
example: r#"let-env HZ_ENV_ABC = 1; hide-env HZ_ENV_ABC; 'HZ_ENV_ABC' in (env).name"#,
result: Some(Value::boolean(false, Span::test_data())),
result: Some(Value::test_bool(false)),
}]
}
}

View File

@ -70,9 +70,8 @@ impl Command for Let {
)?
.0;
//println!("Adding: {:?} to {}", rhs, var_id);
stack.add_var(var_id, rhs.into_value(call.head));
Ok(PipelineData::empty())
}

View File

@ -2,7 +2,7 @@ use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -19,6 +19,8 @@ impl Command for Loop {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("loop")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("block", SyntaxShape::Block, "block to loop")
.category(Category::Core)
}
@ -79,7 +81,7 @@ impl Command for Loop {
vec![Example {
description: "Loop while a condition is true",
example: "mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x",
result: Some(Value::int(11, Span::test_data())),
result: Some(Value::test_int(11)),
}]
}
}

View File

@ -106,7 +106,7 @@ impl Command for Metadata {
vec![
Example {
description: "Get the metadata of a variable",
example: "metadata $a",
example: "let a = 42; metadata $a",
result: None,
},
Example {

View File

@ -2,6 +2,7 @@ mod alias;
mod ast;
mod break_;
mod commandline;
mod const_;
mod continue_;
mod debug;
mod def;
@ -19,6 +20,9 @@ mod export_use;
mod extern_;
mod for_;
pub mod help;
pub mod help_aliases;
pub mod help_commands;
pub mod help_modules;
mod help_operators;
mod hide;
mod hide_env;
@ -40,6 +44,7 @@ pub use alias::Alias;
pub use ast::Ast;
pub use break_::Break;
pub use commandline::Commandline;
pub use const_::Const;
pub use continue_::Continue;
pub use debug::Debug;
pub use def::Def;
@ -57,6 +62,9 @@ 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_modules::HelpModules;
pub use help_operators::HelpOperators;
pub use hide::Hide;
pub use hide_env::HideEnv;

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)]
pub struct Module;
@ -46,17 +46,17 @@ impl Command for Module {
Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::string("foo", Span::test_data())),
result: Some(Value::test_string("foo")),
},
Example {
description: "Define an environment variable in a module",
example: r#"module foo { export-env { let-env FOO = "BAZ" } }; use foo; $env.FOO"#,
result: Some(Value::string("BAZ", Span::test_data())),
result: Some(Value::test_string("BAZ")),
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::string("BAZ", Span::test_data())),
result: Some(Value::test_string("BAZ")),
},
]
}

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
Category, IntoPipelineData, PipelineData, Signature, Type, Value,
};
#[derive(Clone)]
@ -14,7 +14,9 @@ impl Command for Overlay {
}
fn signature(&self) -> Signature {
Signature::build("overlay").category(Category::Core)
Signature::build("overlay")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
@ -23,7 +25,9 @@ impl Command for Overlay {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
https://www.nushell.sh/book/thinking_in_nu.html
You must use one of the following subcommands. Using this command as-is will only produce this help message."#
}
fn is_parser_keyword(&self) -> bool {
@ -50,15 +54,3 @@ impl Command for Overlay {
.into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Overlay {})
}
}

View File

@ -1,7 +1,9 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
};
#[derive(Clone)]
pub struct OverlayHide;
@ -17,6 +19,7 @@ impl Command for OverlayHide {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay hide")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.optional("name", SyntaxShape::String, "Overlay to hide")
.switch(
"keep-custom",

View File

@ -1,7 +1,7 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
@ -17,7 +17,9 @@ impl Command for OverlayList {
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay list").category(Category::Core)
Signature::build("overlay list")
.category(Category::Core)
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
}
fn extra_usage(&self) -> &str {
@ -50,7 +52,7 @@ impl Command for OverlayList {
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam
overlay list | last"#,
result: Some(Value::string("spam", Span::test_data())),
result: Some(Value::test_string("spam")),
}]
}
}

View File

@ -1,7 +1,9 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
};
#[derive(Clone)]
pub struct OverlayNew;
@ -17,6 +19,8 @@ impl Command for OverlayNew {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay new")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("name", SyntaxShape::String, "Name of the overlay")
// TODO:
// .switch(

View File

@ -3,7 +3,7 @@ use nu_parser::trim_quotes_str;
use nu_protocol::ast::{Call, Expr};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
};
use std::path::Path;
@ -22,6 +22,8 @@ impl Command for OverlayUse {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required(
"name",
SyntaxShape::String,
@ -64,7 +66,7 @@ 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.positional_nth(0) {
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 {
@ -82,25 +84,8 @@ impl Command for OverlayUse {
));
};
let overlay_name = if let Some(kw_expression) = call.positional_nth(1) {
// If renamed via the 'as' keyword, use the new name as the overlay name
if let Some(new_name_expression) = kw_expression.as_keyword() {
if let Some(new_name) = new_name_expression.as_string() {
new_name
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a string".to_string(),
new_name_expression.span,
));
}
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a keyword".to_string(),
kw_expression.span,
));
}
let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
name
} else if engine_state
.find_overlay(name_arg.item.as_bytes())
.is_some()

View File

@ -1,7 +1,9 @@
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, PipelineData, Signature, SyntaxShape, Type, Value};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct Try;
@ -55,13 +57,20 @@ impl Command for Try {
match result {
Err(error) | Ok(PipelineData::Value(Value::Error { error }, ..)) => {
if let nu_protocol::ShellError::Break(_) = error {
return Err(error);
} else if let nu_protocol::ShellError::Continue(_) = error {
return Err(error);
} else if let nu_protocol::ShellError::Return(_, _) = error {
return Err(error);
}
if let Some(catch_block) = catch_block {
let catch_block = engine_state.get_block(catch_block.block_id);
let err_value = Value::Error { error };
// Put the error value in the positional closure var
if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
let err_value = Value::Error { error };
stack.add_var(*var_id, err_value);
stack.add_var(*var_id, err_value.clone());
}
}
@ -69,7 +78,8 @@ impl Command for Try {
engine_state,
stack,
catch_block,
PipelineData::empty(),
// Make the error accessible with $in, too
err_value.into_pipeline_data(),
false,
false,
)
@ -86,6 +96,9 @@ impl Command for Try {
if let Some(var) = catch_block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
// Because external command errors aren't "real" errors,
// (unless do -c is in effect)
// they can't be passed in as Nushell values.
let err_value = Value::nothing(call.head);
stack.add_var(*var_id, err_value);
}
@ -95,7 +108,8 @@ impl Command for Try {
engine_state,
stack,
catch_block,
PipelineData::empty(),
// The same null as in the above block is set as the $in value.
Value::nothing(call.head).into_pipeline_data(),
false,
false,
)

View File

@ -2,7 +2,7 @@ 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::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
@ -20,7 +20,12 @@ impl Command for Use {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.required("module", SyntaxShape::String, "Module or module file")
.optional(
"members",
SyntaxShape::Any,
"Which members of the module to import",
)
.category(Category::Core)
}
@ -43,7 +48,7 @@ impl Command for Use {
let import_pattern = if let Some(Expression {
expr: Expr::ImportPattern(pat),
..
}) = call.positional_nth(0)
}) = call.parser_info_nth(0)
{
pat
} else {
@ -117,12 +122,12 @@ impl Command for Use {
Example {
description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::string("foo", Span::test_data())),
result: Some(Value::test_string("foo")),
},
Example {
description: "Define a custom command that participates in the environment in a module and call it",
example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::string("BAZ", Span::test_data())),
result: Some(Value::test_string("BAZ")),
},
]
}

View File

@ -1,7 +1,9 @@
use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct While;
@ -17,6 +19,8 @@ impl Command for While {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("while")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.allow_variants_without_examples(true)
.required("cond", SyntaxShape::Expression, "condition to check")
.required(
"block",

View File

@ -20,6 +20,9 @@ impl Command for IntoSqliteDb {
fn signature(&self) -> Signature {
Signature::build("into sqlite")
.input_output_types(vec![(Type::Any, Type::Nothing)])
.allow_variants_without_examples(true)
// TODO: narrow disallowed types
.required(
"file_name",
SyntaxShape::String,
@ -215,12 +218,14 @@ fn action(
// and we're done
Ok(Value::Nothing { span: *span })
}
_ => Err(ShellError::UnsupportedInput(
format!(
"Expected a list but instead received a {}",
input.get_type()
),
// Propagate errors by explicitly matching them before the final case.
Value::Error { error } => Err(error.clone()),
other => Err(ShellError::OnlySupportsThisInputType(
"list".into(),
other.get_type().to_string(),
span,
// This line requires the Value::Error match above.
other.expect_span(),
)),
}
}

View File

@ -18,6 +18,7 @@ impl Command for QueryDb {
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_output_types(vec![(Type::Any, Type::Any)])
.required(
"SQL",
SyntaxShape::String,

View File

@ -16,6 +16,7 @@ impl Command for SchemaDb {
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_output_types(vec![(Type::Any, Type::Any)])
.input_type(Type::Any)
.output_type(Type::Any)
.category(Category::Custom("database".into()))

View File

@ -29,10 +29,7 @@ impl Command for ColumnsDF {
description: "Dataframe columns",
example: "[[a b]; [1 2] [3 4]] | into df | columns",
result: Some(Value::List {
vals: vec![
Value::string("a", Span::test_data()),
Value::string("b", Span::test_data()),
],
vals: vec![Value::test_string("a"), Value::test_string("b")],
span: Span::test_data(),
}),
}]

View File

@ -70,7 +70,7 @@ fn command(
.ok_or_else(|| {
ShellError::GenericError(
"Empty names list".into(),
"No column names where found".into(),
"No column names were found".into(),
Some(col_span),
None,
Vec::new(),

View File

@ -30,6 +30,7 @@ mod with_column;
use nu_protocol::engine::StateWorkingSet;
pub use self::open::OpenDataFrame;
pub use append::AppendDF;
pub use columns::ColumnsDF;
pub use drop::DropDF;
@ -43,7 +44,6 @@ pub use get::GetDF;
pub use last::LastDF;
pub use list::ListDF;
pub use melt::MeltDF;
pub use open::OpenDataFrame;
pub use query_df::QueryDf;
pub use rename::RenameDF;
pub use sample::SampleDF;

View File

@ -31,10 +31,7 @@ impl Command for ExprAsNu {
example: "col a | into nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![
Value::string("column", Span::test_data()),
Value::string("a", Span::test_data()),
],
vals: vec![Value::test_string("column"), Value::test_string("a")],
span: Span::test_data(),
}),
}]

View File

@ -37,10 +37,7 @@ impl Command for ExprCol {
example: "col a | into nu",
result: Some(Value::Record {
cols: vec!["expr".into(), "value".into()],
vals: vec![
Value::string("column", Span::test_data()),
Value::string("a", Span::test_data()),
],
vals: vec![Value::test_string("column"), Value::test_string("a")],
span: Span::test_data(),
}),
}]

View File

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

View File

@ -1,68 +0,0 @@
use crate::dataframe::values::{NuExpression, NuLazyFrame};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct LazyFillNA;
impl Command for LazyFillNA {
fn name(&self) -> &str {
"fill-na"
}
fn usage(&self) -> &str {
"Replaces NA values with the given expression"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"fill",
SyntaxShape::Any,
"Expression to use to fill the NAN values",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "",
example: "",
result: None,
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let fill: Value = call.req(engine_state, stack, 0)?;
let value = input.into_value(call.head);
if NuExpression::can_downcast(&value) {
let expr = NuExpression::try_from_value(value)?;
let fill = NuExpression::try_from_value(fill)?.into_polars();
let expr: NuExpression = expr.into_polars().fill_nan(fill).into();
Ok(PipelineData::Value(
NuExpression::into_value(expr, call.head),
None,
))
} else {
let lazy = NuLazyFrame::try_from_value(value)?;
let expr = NuExpression::try_from_value(fill)?.into_polars();
let lazy = NuLazyFrame::new(lazy.from_eager, lazy.into_polars().fill_nan(expr));
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
}
}
}

View File

@ -0,0 +1,137 @@
use crate::dataframe::values::{Column, NuDataFrame, NuExpression};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct LazyFillNA;
impl Command for LazyFillNA {
fn name(&self) -> &str {
"fill-nan"
}
fn usage(&self) -> &str {
"Replaces NaN values with the given expression"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"fill",
SyntaxShape::Any,
"Expression to use to fill the NAN values",
)
.input_type(Type::Custom("dataframe".into()))
.output_type(Type::Custom("dataframe".into()))
.category(Category::Custom("lazyframe".into()))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Fills the NaN values with 0",
example: "[1 2 NaN 3 NaN] | into df | fill-nan 0",
result: Some(
NuDataFrame::try_from_columns(vec![Column::new(
"0".to_string(),
vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(0),
Value::test_int(3),
Value::test_int(0),
],
)])
.expect("Df for test should not fail")
.into_value(Span::test_data()),
),
},
Example {
description: "Fills the NaN values of a whole dataframe",
example: "[[a b]; [0.2 1] [0.1 NaN]] | into df | fill-nan 0",
result: Some(
NuDataFrame::try_from_columns(vec![
Column::new(
"a".to_string(),
vec![Value::test_float(0.2), Value::test_float(0.1)],
),
Column::new(
"b".to_string(),
vec![Value::test_int(1), Value::test_int(0)],
),
])
.expect("Df for test should not fail")
.into_value(Span::test_data()),
),
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let fill: Value = call.req(engine_state, stack, 0)?;
let value = input.into_value(call.head);
if NuExpression::can_downcast(&value) {
let expr = NuExpression::try_from_value(value)?;
let fill = NuExpression::try_from_value(fill)?.into_polars();
let expr: NuExpression = expr.into_polars().fill_nan(fill).into();
Ok(PipelineData::Value(
NuExpression::into_value(expr, call.head),
None,
))
} else {
let val_span = value.span()?;
let frame = NuDataFrame::try_from_value(value)?;
let columns = frame.columns(val_span)?;
let dataframe = columns
.into_iter()
.map(|column| {
let column_name = column.name().to_string();
let values = column
.into_iter()
.map(|value| match value {
Value::Float { val, .. } => {
if val.is_nan() {
fill.clone()
} else {
value
}
}
Value::List { vals, span } => {
NuDataFrame::fill_list_nan(vals, span, fill.clone())
}
_ => value,
})
.collect::<Vec<Value>>();
Column::new(column_name, values)
})
.collect::<Vec<Column>>();
Ok(PipelineData::Value(
NuDataFrame::try_from_columns(dataframe)?.into_value(call.head),
None,
))
}
}
}
#[cfg(test)]
mod test {
use super::super::super::test_dataframe::test_dataframe;
use super::*;
#[test]
fn test_examples() {
test_dataframe(vec![Box::new(LazyFillNA {})])
}
}

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