Compare commits

...

382 Commits

Author SHA1 Message Date
JT
1291b647ae bump to 0.73 (#7542)
# 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-20 21:58:48 +13:00
91df6c236f Remove unused deps or move to devdeps (#7537)
# Description

General tree shaking through `cargo +nightly udeps` and moving mentions
of `nu-test-support` to the dev deps.

Also since #7488 no separate import of `nu-path` necessary

cc @webbedspace
2022-12-20 12:53:17 +13:00
JT
58cea7e8b4 Turn off cd abbreviations by default (#7536)
# Description

This turns off `cd` abbreviations by default

# User-Facing Changes

`cd` goes back to requiring a name with the default configuration

# 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-19 13:32:24 -08:00
b01f50bd70 nu-explore/ Fix configuration issues (#7520)
close #7518

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-19 11:34:21 -06:00
5f48452e3b some filesystem command signatures (#7464)
# Description

Signature for the following commands:

- `cd` 
- `cp` 
- `glob` 
- `ls` 
- `mkdir` 
- `mv` 
- `open`
- `rm`
- `save`
- `touch`

Related to https://github.com/nushell/nushell/issues/7320

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2022-12-19 13:40:57 +01:00
dae1b9a996 prevent panic with format command (#7522)
Related: #7211.

Prevents numeric underflow in span.

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2022-12-19 13:10:02 +01:00
a21af0ade4 Revert "into cellpath command (#7417)" (#7523)
This reverts commit f0e93c2fa9 (PR #7417).

I'm currently [working on improving cell
paths](https://github.com/nushell/nushell/issues/7498#issuecomment-1356834798),
and I realized that I would need to make several improvements to `into
cellpath` along the lines of Jakub's comment here:
https://github.com/nushell/nushell/pull/7417#issuecomment-1345264955

I don't think `into cellpath` is quite ready for prime-time, and I'd
like to remove it before the upcoming release.
2022-12-18 23:02:18 -08:00
28123841ba Patch explore 4 (#7517)
ref #7339 - This PR updates explore to take some of the colors from
nushell, namely the line colors and the ls_colors.

note: Not sure why this regression appeared maybe it's a feature or it's
no longer supposed to be supported?

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-18 08:43:15 -06:00
183be911d0 Patch after fix after fix 7380 (#7501)
> I'm not sure how i feel about that. I mean if there are a lot of
columns, it should probably have a max width so 1 column doesn't take
the entire width of your screen. Ideally it would work closely like
table worked before we migrated to tabled, as far as how column widths
were allocated.

I believe it still not completely matched.
*To be honest I am not against the #7446 approach.

The PR makes a switch between logics on a premise of `termwidth`.
So if `termwidth > 120` we start prioritizing amount of columns we can
show (We try to show as many columns as we can).
Otherwise we do what I've described in #7446 (We show the least columns
but with least truncation involvement).

In case it's OK,
I guess we could make the value configurable.

cc @fdncred 
ref #7446

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-17 16:16:32 -06:00
1966809502 explore tweaks Round 1 (#7511)
A few small tweaks to the new `explore` command:

1. Rewrote the help text a bit.
    1. I think it's important to mention `:try` up front.
2. Removed the info about `:help foo` because it's currently supported
by very few subcommands
2. Make `exit_esc` default to true. I want to avoid people getting stuck
in `explore` like they get stuck in Vim
3. ~~Always show the help message ("For help type :help") on startup~~
1. The message is small+unobtrusive and I don't this is worth a
configuration item
4. Exit the information view when Escape is pressed
5. General typo+grammar cleanup
    
cc: @zhiburt @fdncred
2022-12-17 12:05:41 -08:00
c3c41a61b0 replace lazy_static with once_cell (#7502)
replacing the dependence on `lazy_static` with `once_cell`, this will
ensure that variables are initialized when needed instead of startup
time.
2022-12-17 10:30:04 -08:00
90849a067f Fix encode base64 type signature and examples (#7515)
# 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.
2022-12-17 11:57:16 -06:00
705f12c1d9 Fix cell path when getting columns of non-records (#7508)
A follow-up to #7497. That change made it so that `get foo` would
eliminate non-record rows; I think that was an unintentional and
undesirable side-effect.

Before #7497:
```bash
〉[$nothing, { item: "foo" }] | get item
╭───┬─────╮
│ 0 │     │
│ 1 │ foo │
╰───┴─────╯
```
After #7497:
```bash
〉[$nothing, {item: "foo"}] | get item
╭───┬─────╮
│ 0 │ foo │
╰───┴─────╯
```

After this PR:
```bash
〉[$nothing, { item: "foo" }] | get item
╭───┬─────╮
│ 0 │     │
│ 1 │ foo │
╰───┴─────╯
```

cc: @merelymyself
2022-12-17 09:14:12 -08:00
0826e66fe0 add xterm color names to ansi --list (#7513)
# Description

Follow up to #7141 to map @webbedspace's rgb colors to xterm 256 color
indexes. Also added the xterm 256 named colors to `ansi --list` in the
process.

The few dozen or so names that were duplicate in the xterm 256 names
from [here](https://www.ditig.com/256-colors-cheat-sheet) were renamed
by appending a,b,c,d. So, instead of two blue3's there will be blue3a
and blue3b.

# User-Facing Changes

More colors.

# 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-17 11:10:14 -06:00
774769a7ad color_config now accepts closures as color values (#7141)
# Description

Closes #6909. You can now add closures to your `color_config` themes.
Whenever a value would be printed with `table`, the closure is run with
the value piped-in. The closure must return either a {fg,bg,attr} record
or a color name (`'light_red'` etc.). This returned style is used to
colour the value.

This is entirely backwards-compatible with existing config.nu files.

Example code excerpt:
```
let my_theme = {
    header: green_bold
    bool: { if $in { 'light_cyan' } else { 'light_red' } }
    int: purple_bold
    filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } }
    duration: purple_bold
    date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } }
    range: yellow_bold
    string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }
    nothing: white
```
Example output with this in effect:
![2022-11-16 12 47 23 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png)
![2022-11-16 12 39 41 AM - style_computer
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png)
![2022-11-15 09 21 54 PM - run_external
rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png)

Slightly important notes:

* Some color_config names, namely "separator", "empty" and "hints", pipe
in `null` instead of a value.
* Currently, doing anything non-trivial inside a closure has an
understandably big perf hit. I currently do not actually recommend
something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' }
}` for serious work, mainly because of the abundance of string-type data
in the world. Nevertheless, lesser-used types like "date" and "duration"
work well with this.
* I had to do some reorganisation in order to make it possible to call
`eval_block()` that late in table rendering. I invented a new struct
called "StyleComputer" which holds the engine_state and stack of the
initial `table` command (implicit or explicit).
* StyleComputer has a `compute()` method which takes a color_config name
and a nu value, and always returns the correct Style, so you don't have
to worry about A) the color_config value was set at all, B) whether it
was set to a closure or not, or C) which default style to use in those
cases.
* Currently, errors encountered during execution of the closures are
thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors
result in a huge perf hit when they are encountered. I think what should
be done is to assume something terrible happened to the user's config
and invalidate the StyleComputer for that `table` run, thus causing
subsequent output to just be Style::default().)
* More thorough tests are forthcoming - ran into some difficulty using
`nu!` to take an alternative config, and for some reason `let-env config
=` statements don't seem to work inside `nu!` pipelines(???)
* The default config.nu has not been updated to make use of this yet. Do
tell if you think I should incorporate that into this.

# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-17 07:07:56 -06:00
e72cecf457 Remove unnecessary echo uses from examples (#7500)
`echo` tends to confuse new Nu users; they expect it to work like
`print` when it just passes a value to the next stage of the pipeline.

We haven't quite figured out what to do about `echo` in the long run,
but I think a good start is to remove `echo` from command examples where
it would be unnecessary and arguably unidiomatic.
2022-12-16 11:51:00 -05:00
9c1a3aa244 nu-explore/ A few things (#7339)
ref #7332

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-16 09:47:07 -06:00
2d07c6eedb ensure get doesn't delve too deep in nested lists (#7497)
# Description

Fixes #7494.

```
/home/gabriel/CodingProjects/nushell〉[[{foo: bar}]] | get foo          12/16/2022 12:31:17 PM
Error: nu::parser::not_found (link)

  × Not found.
   ╭─[entry #1:1:1]
 1 │ [[{foo: bar}]] | get foo
   · ───────┬──────
   ·        ╰── did not find anything under this name
   ╰────

```

# User-Facing Changes

cell paths no longer drill into nested tables.

# 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-15 22:03:38 -08:00
JT
fdd92b2dda Update README.md 2022-12-16 18:50:37 +13:00
8c70189422 (nu_plugin_python): Send back the signature required fields. (#7489)
# Description

The `Signature` data structure has changed. We need to add the required
fields for the nu plugin in python to work well when registering 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
2022-12-15 14:37:12 -06:00
075c83b3a1 add new plugins to script (#7493)
# Description

This PR adds more plugins to the match statement and documents the
plugin repos as comments.

# 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.
2022-12-15 14:36:14 -06:00
d3a19c5ac7 (register-plugins.nu): Filter out files ending with .d on systems other than windows. (#7492) 2022-12-15 14:00:48 -06:00
080874df10 Fix #7486 (#7487)
close #7486

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-15 09:55:15 -08:00
24848a1e35 Use nu-path correctly in nu! test macro to make dev-dependency transitive (#7488)
## Fix `nu-path` usage in `nu!` testing macro

The `nu-path` crate needs to be properly re-exported so the generated
code is valid if `nu-path` is not present among the dependencies of the
using crate.

Usage of crates in `macro_rules!` macros has to follow the
`$crate::symbol_in_crate` path pattern (With an absolute path-spec also
for macros defined in submodules)

## Move `nu-test-support` to devdeps in `nu-protocol`

Also remove the now unnecessary direct dependency on `nu-path`.
`nu!` macro had to be changed to make it a proper transitive dependency.
2022-12-15 18:53:26 +01:00
e215fbbd08 Add helper method to check whether ctrl+c was pressed, adopt it (#7482)
I've been working on streaming and pipeline interruption lately. It was
bothering me that checking ctrl+c (something we want to do often) always
requires a bunch of boilerplate like:
```rust
use std::sync::atomic::Ordering;

if let Some(ctrlc) = &engine_state.ctrlc {
     if ctrlc.load(Ordering::SeqCst) {
          ...
```
I added a helper method to cut that down to:

```rust
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
    ...
```
2022-12-15 09:39:24 -08:00
33aea56ccd Try to fix #7380 (#7446)
fix https://github.com/nushell/nushell/issues/7380
2022-12-15 08:47:04 -06:00
b6683a3010 add --long flag to history command for sqlite history (#7480) 2022-12-15 08:46:32 -06:00
578ef04988 remove output, append, bin flag from fetch command (#7468)
Closes https://github.com/nushell/nushell/issues/7439
2022-12-14 16:44:04 -06:00
735a7a21bd Add example showing first class closure to do (#7473)
# Description

Demonstrates that you can use `do` to execute stored closures and
evaluate their captures properly.

# Tests + Formatting

As an example test increases coverage of the usage to execute first
class closures.
Additional tests using that found in
`tests/shell/pipeline/commands/internal.rs`
2022-12-14 20:55:00 +01:00
80a69224f7 Handle ctrl-c in uniq and uniq-by (#7478)
A partial fix for #7477. `uniq` can be slow sometimes, so we should
check `ctrl-c` when it's running.

Tested on [this
file](https://home.treasury.gov/system/files/276/yield-curve-rates-1990-2021.csv),
I ran `open yield-curve-rates-1990-2021.csv | uniq` and confirmed that I
can now cancel the operation.

Future work is needed to figure out why `uniq` is so slow.
2022-12-14 11:31:54 -08:00
e0bf17930b refactor: introduce is_external_failed to PipelineData, and simplify try logic (#7476)
# Description

Just spot that there are some duplicate code about checking external
runs to failed, is pr is trying to refactor it and reduce lines of code

# User-Facing Changes

NaN

# 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-14 10:25:32 -08:00
db3177a5aa break for, loop, while execution when external command runs to failed (#7475)
Fixes: #7467

# User-Facing Changes

## for
```
❯ for i in 1..2 { echo 1;  ^false }
1
```

## loop
```
❯ loop { echo bb; ^false }
bb
```

## while
```
❯ mut x = 1; while $x < 3 { $x = $x + 1; echo bb; ^false }
bb
```
2022-12-14 16:20:18 +01:00
98b9839e3d Fix for escaping backslashes in interpolated strings (fixes #6737) (#7119)
Co-authored-by: Gavin Foley <gavinmfoley@gmail.com>
2022-12-14 07:54:13 -06:00
0db4d89838 add missing shapes to default_config (#7472)
# Description

We've added some shapes recently. This PR adds them to the
default_config and sorts them in order to make finding missing ones
easier.

# 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.
2022-12-14 07:11:17 -06:00
52278f8562 Help messages: edit various instances of "block" to "closure" (#7470) 2022-12-14 14:02:41 +01:00
c19d9597fd Add riscv64 binary release target (#7469)
# Description

Add `riscv64gc-unknown-linux-gnu` release target

TEST release workflow:
https://github.com/hustcer/nu-release/actions/runs/3693191329
TEST release: https://github.com/hustcer/nu-release/releases/tag/v0.73.0

# User-Facing Changes

New `nu-*-riscv64gc-unknown-linux-gnu.tar.gz` package will be added to
the following release

# 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-14 23:09:39 +13:00
d9d9916ccc Fix streaming page missing newline (#7466)
Fixes #7342. `0..1000 | table` before this change:


![image](https://user-images.githubusercontent.com/26268125/207474492-dead4267-d828-4840-8da0-4edfda3e3916.png)

`0..1000 | table` after this change:


![image](https://user-images.githubusercontent.com/26268125/207474583-26633db0-46c5-4c30-8681-654855e7042b.png)

When piping data to `table`, pages were not getting a newline at the
end[^1]. This problem was uncovered and exacerbated by the new
`display_output` hook which implicitly piped _everything_ to `table`.

## The Fix

`PagingTableCreator` now adds a newline to each page instead of relying
on later code to do it.

## Tests

I spent a while trying to write a regression test for this behaviour but
I couldn't get the test to fail before my fix! I think the test
infrastructure does something special with newlines when it's checking
command output. I eventually ran out of steam trying to investigate
that, sorry.

[^1]: unless the pipe to table was the implicit one that's done when
there is no `display_output` hook set. That situation was still working
OK.
2022-12-14 16:45:37 +13:00
26759c4af2 mkdir change flag -s to -v (#7462)
Change in `mkdir` `-s` flag to `-v` to be similar to other commands


# Description

Other commands like `rm`, `mv`, `cp` have a `-v` (`--verbose`) flag.
`mkdir` has a `-s` (`--show-created-paths`), but should be consistent
with other commands.

# User-Facing Changes

- flag `-s` replaced by `-v` in `mkdir` command.


# Tests + Formatting
```
> mkdir -v new_dir
╭───┬───────────────────────────────────╮
│ 0 │ C:\Users\ricardo.monteiro\new_dir │
╰───┴───────────────────────────────────╯
```
2022-12-13 11:56:44 -05:00
e529746294 sort enums add missing items to parse_shape_name (#7450)
# Description

This PR adds missing items in `parse_shape_name`, sorts the
`SyntaxShape` enum and the `Type` enum. It's a pain to hunt around for
particular items in an enum when they're unsorted.

# 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.
2022-12-13 10:46:22 -06:00
0c4d4632ef let UnknownFlag error list out available flags (#7443)
# Description
Fixes #6773.

```
/home/gabriel/CodingProjects/nushell〉ls -r                                                                                                                             12/12/2022 02:57:35 PM
Error: nu::parser::unknown_flag (link)

  × The `ls` command doesn't have flag `-r`.
   ╭─[entry #1:1:1]
 1 │ ls -r
   ·     ┬
   ·     ╰── unknown flag
   ╰────
  help: Available flags: --help(-h), --all(-a), --long(-l), --short-names(-s), --full-paths(-f), --du(-d), --directory(-D). Use `--help` for more information.
```

# User-Facing Changes

Different error for unknown flag.

# 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-13 06:45:33 -06:00
e2c1216c1b Fix du error message (#7460)
BEFORE:
```
〉du *.***
Error:
  × wildcards are either regular `*` or recursive `**`
   ╭─[entry #6:1:1]
 1 │ du *.***
   · ─┬
   ·  ╰── glob error
   ╰────
```
AFTER:
```
〉du *.***
Error:
  × glob error
   ╭─[entry #8:1:1]
 1 │ du *.***
   ·    ──┬──
   ·      ╰── wildcards are either regular `*` or recursive `**`
   ╰────

```
2022-12-13 13:11:55 +01:00
d83dbc3670 add input_output_types() to benchmark,cd and config reset (#7455)
# Description

add input_output_types() to benchmark, cd and config reset commands
It's an update to https://github.com/nushell/nushell/issues/7320


# 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-13 06:10:55 -06:00
JT
0242b30027 Revert "Add pipeline operators to help" (#7454)
Reverts nushell/nushell#7449
2022-12-13 16:49:00 +13:00
JT
0c656fd276 Revert "Pipeline operators: && and ||" (#7452)
Reverts nushell/nushell#7448

Some surprising behavior in how we do this. For example:

```
〉if (true || false) { print "yes!" } else { print "no!" }
no!
〉if (true or false) { print "yes!" } else { print "no!" }
yes!
```

This means for folks who are using the old `||`, they possibly get the
wrong answer once they upgrade. I don't think we can ship with that as
it will catch too many people by surprise and just make it easier to
write buggy code.
2022-12-13 16:36:13 +13:00
b7a3e5989d Make hook execution stream instead of collecting (#7440)
Closes #7431. In a nutshell:
- `run_hook_block()` in repl.rs was collecting all input into a `Value`
instead of handling streaming input properly
- this was a problem because now we have a default `display_output` hook
that _everything_ gets piped to
- this PR fixes the problem by tweaking `run_hook_block()` to return a
`PipelineData` instead of a `Value`

After this change, individual pages are rendered as they finish. This is
a little easier to see if I tweak `STREAM_PAGE_SIZE` in table.rs to 10:

![image](https://user-images.githubusercontent.com/26268125/206935370-412b2ee9-9401-4222-bc93-5bd5a9adc13b.png)

## Future work

This does _not_ fix https://github.com/nushell/nushell/issues/7342.
2022-12-12 15:23:04 -08:00
JT
5b5f1d1b92 Add pipeline operators to help (#7449)
# 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-13 11:31:03 +13:00
JT
35bea5e044 Pipeline operators: && and || (#7448)
# Description

We got some feedback from folks used to other shells that `try/catch`
isn't quite as convenient as things like `||`. This PR adds `&&` as a
synonym for `;` and `||` as equivalent to what `try/catch` would do.

# User-Facing Changes

Adds `&&` and `||` pipeline operators.

# 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-13 09:53:46 +13:00
7917cf9f00 Add config mutation tests (#7437)
# Description

Env config can be mutated by `=`, this pr is to add a few tests 

# 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-12 12:46:25 -06:00
5036672a58 add interact-once switch to rm (#7432)
# Description

Fixes: #7216 

Adds `interact-once` switch which numbers out the number of files to
delete and asks the user for confirmation.

```
/home/gabriel/test〉ls                                                                                                                                                  12/11/2022 11:25:42 AM
╭───┬───────┬──────┬──────┬──────────╮
│ # │ name  │ type │ size │ modified │
├───┼───────┼──────┼──────┼──────────┤
│ 0 │ a.txt │ file │  0 B │ now      │
│ 1 │ b.txt │ file │  0 B │ now      │
│ 2 │ c.txt │ file │  0 B │ now      │
╰───┴───────┴──────┴──────┴──────────╯
/home/gabriel/test〉rm *.txt -I                                                                                                                                         12/11/2022 11:25:42 AM
rm: remove 3 files? : y

/home/gabriel/test〉ls                                                                                                                                                  12/11/2022 11:25:51 AM
/home/gabriel/test〉                                                                                                                                                    12/11/2022 11:25:54 AM
```

# 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-11 13:22:27 -08:00
585ab56ea4 in for, loop, while, auto print final value in each iteration (#7433)
# Description

Fixes: #7404 
Fixes: #7402 

## About change
In `eval_block`, all pipelines(or called statements?) result will be
printed except the last one, the last one is returned by `eval_block`
function.

So if we want to print the last statement in eval block, we just need to
print that value.

# User-Facing Changes

### for
```
❯ for _ in 1..2 { echo "a" }
a
a
```

### while
```
❯ mut x = 1; while $x < 3 { $x = $x + 1; echo bb; }
bb
bb
```

### loop
```
❯ mut total = 0; loop {
∙     if $total > 1 {
∙         break
∙     } else {
∙         $total += 1
∙     }
∙     echo 3
∙ }
3
3
```

# 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-12 05:46:03 +13:00
9009f68e09 Tweak "Cannot convert {x} to a string argument" error in run_external (#7434)
# Description

The message arrow is altered to show the external command name in case
it wasn't obvious. (See example for an occasion where it is
non-obvious).

BEFORE:
```
〉else if (2mb) > 4mb
Error: nu:🐚:external_command (link)

  × External command failed
   ╭─[entry #35:1:1]
 1 │ else if (2mb) > 4mb
   ·           ─┬
   ·            ╰── Cannot convert filesize to a string
   ╰────
  help: All arguments to an external command need to be string-compatible
```
AFTER:
```
〉else if (2mb) > 4mb
Error: nu:🐚:external_command (link)

  × External command failed
   ╭─[entry #3:1:1]
 1 │ else if (2mb) > 4mb
   ·           ─┬
   ·            ╰── Cannot convert filesize to a string argument for 'else'
   ╰────
  help: All arguments to an external command need to be string-compatible

```
# 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-11 08:39:51 -08:00
2bacc29d30 Replace row conditions with closures in commands (#7428)
# Description

This PR changes some commands that previously accepted row conditions
(like `$it > 5`) as parameter to accept closures instead. The reasons
are:
a) The commands would need to move into parser keywords in the future
while they feel more like commands to be implemented in Nushell code as
a part of standard library.
b) In scripts, it is useful to store the predicate condition in a
variable which you can't do with row conditions.
c) These commands are not used that often to benefit enough from the
shorter row condition syntax

# User-Facing Changes

The following commands now accept **closure** instead of a **row
condition**:
- `take until`
- `take while`
- `skip until`
- `skip while`
- `any`
- `all`

This is a part of an effort to move away from shape-directed parsing.
Related PR: https://github.com/nushell/nushell/pull/7365

# 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-10 19:24:06 +02:00
4d7d97e0e6 Simplify FILE_PWD setting in 'overlay use' (#7425)
# Description

Changes the `FILE_PWD` setting mechanism to match the one used in the
`use` command.

Fixes https://github.com/nushell/nushell/pull/7424

# User-Facing Changes

None

# 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-10 19:23:55 +02:00
f1000a17b4 Add FILE_PWD environment variable when running 'nu script.nu' (#7424)
# Description

When running `nu script.nu`, the `$env.FILE_PWD` will be set to the
directory where the script is.

Also makes the error message a bit nicer:
```
> target/debug/nu asdihga
Error: nu:🐚:file_not_found (link)

  × File not found
   ╭─[source:1:1]
 1 │ nu
   · ▲
   · ╰── Could not access file 'asdihga': "No such file or directory (os error 2)"
   ╰────

```

# User-Facing Changes

`FILE_PWD` environment variable is available when running a script as
`nu script.nu`.

# 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-10 19:23:44 +02:00
f43edbccdc Make env-related tests more resilient (#7423)
# Description

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

The error message of environment variable not found could change
depending on the `$env` content which can produce random failures on
different systems. This PR hopefully makes the tests more resilient.

# User-Facing Changes

None

# 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-10 19:23:34 +02:00
6b4282eadf Move 'where' to parser keywords; Add 'filter' command (#7365)
# Description

This PR moves the `where` command to a parser keyword. While it still
uses the shape-directed parsing dictated by the signature, we're free to
change the parsing code now to a custom one once we remove the syntax
shapes.

As a side effect, the `where -b` flag was removed and its functionality
has moved to the new `filter` command.

Just FYI, other commands that take row conditions:
- `take until`
- `take while`
- `skip until`
- `skip while`
- `any`
- `all`

We can either move these to the parser as well or make them accept a
closure instead of row condition.

# User-Facing Changes

New `filter` command which replaces `where -b` functionality.

# 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-10 19:23:24 +02:00
7e2781a2af fix docker build (#7422)
# Description

Docker build fails with the latest release 0.72.1
```bash
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM alpine
 ---> 49176f190c7e
Step 2/6 : LABEL maintainer=nushell
 ---> Using cache
 ---> 7afa6864f008
Step 3/6 : RUN echo '/usr/bin/nu' >> /etc/shells     && adduser -D -s /usr/bin/nu nushell     && mkdir -p /home/nushell/.config/nushell/     && wget -q https://raw.githubusercontent.com/nushell/nushell/main/crates/nu-utils/src/sample_config/default_config.nu -O /home/nushell/.config/nushell/config.nu     && wget -q https://raw.githubusercontent.com/nushell/nushell/main/crates/nu-utils/src/sample_config/default_env.nu -O /home/nushell/.config/nushell/env.nu     && cd /tmp     && wget -qO - https://api.github.com/repos/nushell/nushell/releases/latest     |grep browser_download_url     |grep musl     |cut -f4 -d '"'     |xargs -I{} wget {}     && tar -xzf nu*     && chmod +x nu     && mv nu /usr/bin/nu     && chown -R nushell:nushell /home/nushell/.config/nushell     && rm -rf /tmp/*
 ---> Running in fa544239a3ea
Connecting to github.com (140.82.121.4:443)
Connecting to objects.githubusercontent.com (185.199.108.133:443)
saving to 'nu-0.72.1-x86_64-unknown-linux-musl.tar.gz'
nu-0.72.1-x86_64-unk   3% |*                               |  552k  0:00:29 ETA
nu-0.72.1-x86_64-unk  93% |*****************************   | 15.2M  0:00:00 ETA
nu-0.72.1-x86_64-unk 100% |********************************| 16.2M  0:00:00 ETA
'nu-0.72.1-x86_64-unknown-linux-musl.tar.gz' saved
chmod: nu: No such file or directory
```
2022-12-10 09:22:23 -06:00
32a53450a6 make split row works like python and rust ways (#7413)
# Description

Fixes: #7389 

Make split row works more like python or rust, especially, when the
input string stars/ends with separator, append a empty string to result.
Here are examples:

python:
```python
In [6]: "\nasdf\nghi\n".split("\n")
Out[6]: ['', 'asdf', 'ghi', '']
```
rust:
```rust
fn main() {
    let x = "\nabc\ndef\n";
    let y = x.split("\n").collect::<Vec<&str>>();
    println!("{:?}", y);   // outputs: ["", "abc", "def", ""]
}
```
# 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-10 09:21:53 -06:00
ce78817f41 $env.config now always holds a record with only valid values (#7309)
# Description
Closes #7059. Rather than generate a new Record each time $env.config is
accessed (as described in that issue), instead `$env.config = ` now A)
parses the input record, then B) un-parses it into a clean Record with
only the valid values, and stores that as an env-var. The reasoning for
this is that I believe `config_to_nu_record()` (the method that performs
step B) will be useful in later PRs. (See below)

As a result, this also "fixes" the following "bug":
```
〉$env.config = 'butts'
$env.config is not a record
〉$env.config
butts
```
~~Instead, `$env.config = 'butts'` now turns `$env.config` into the
default (not the default config.nu, but `Config::default()`, which
notably has empty keybindings, color_config, menus and hooks vecs).~~

This doesn't attempt to fix #7110. cc @Kangaxx-0

# Example of new behaviour

OLD:
```
〉$env.config = ($env.config | merge { foo: 1 })
$env.config.foo is an unknown config setting
〉$env.config.foo
1
```
NEW:
```
〉$env.config = ($env.config | merge { foo: 1 })
Error:
  × Config record contains invalid values or unknown settings

Error:
  × Error while applying config changes
   ╭─[entry #1:1:1]
 1 │ $env.config = ($env.config | merge { foo: 1 })
   ·                                           ┬
   ·                                           ╰── $env.config.foo is an unknown config setting
   ╰────
  help: This value has been removed from your $env.config record.

〉$env.config.foo
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #1:1:1]
 1 │ $env.config = ($env.config | merge { foo: 1 })
   ·                              ──┬──
   ·                                ╰── value originates here
   ╰────
   ╭─[entry #2:1:1]
 1 │ $env.config.foo
   ·             ─┬─
   ·              ╰── cannot find column 'foo'
   ╰────
```
# Example of new errors

OLD:
```
$env.config.cd.baz is an unknown config setting
$env.config.foo is an unknown config setting
$env.config.bar is an unknown config setting
$env.config.table.qux is an unknown config setting
$env.config.history.qux is an unknown config setting
```
NEW:
```
Error: 
  × Config record contains invalid values or unknown settings

Error:
  × Error while applying config changes
     ╭─[C:\Users\Leon\AppData\Roaming\nushell\config.nu:267:1]
 267 │     abbreviations: true # allows `cd s/o/f` to expand to `cd some/other/folder`
 268 │     baz: 3,
     ·          ┬
     ·          ╰── $env.config.cd.baz is an unknown config setting
 269 │   }
     ╰────
  help: This value has been removed from your $env.config record.

Error:
  × Error while applying config changes
     ╭─[C:\Users\Leon\AppData\Roaming\nushell\config.nu:269:1]
 269 │   }
 270 │   foo: 1,
     ·        ┬
     ·        ╰── $env.config.foo is an unknown config setting
 271 │   bar: 2,
     ╰────
  help: This value has been removed from your $env.config record.

Error:
  × Error while applying config changes
     ╭─[C:\Users\Leon\AppData\Roaming\nushell\config.nu:270:1]
 270 │   foo: 1,
 271 │   bar: 2,
     ·        ┬
     ·        ╰── $env.config.bar is an unknown config setting
     ╰────
  help: This value has been removed from your $env.config record.

Error:
  × Error while applying config changes
     ╭─[C:\Users\Leon\AppData\Roaming\nushell\config.nu:279:1]
 279 │     }
 280 │     qux: 4,
     ·          ┬
     ·          ╰── $env.config.table.qux is an unknown config setting
 281 │   }
     ╰────
  help: This value has been removed from your $env.config record.

Error:
  × Error while applying config changes
     ╭─[C:\Users\Leon\AppData\Roaming\nushell\config.nu:285:1]
 285 │     file_format: "plaintext" # "sqlite" or "plaintext"
 286 │  qux: 2
     ·       ┬
     ·       ╰── $env.config.history.qux is an unknown config setting
 287 │   }
     ╰────
  help: This value has been removed from your $env.config record.


```

# 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-10 15:34:46 +02:00
f0e93c2fa9 into cellpath command (#7417)
# Description

Address part of feature request #7337, add a small command `into
cellpath` to allow string -> cellpath auto-conversion, with this change,
we could run

```
let p = 'ls.use_ls_colors'
$env.config | upsert ($p | nito cellpath) false
```

<img width="710" alt="image"
src="https://user-images.githubusercontent.com/85712372/206782818-3024b34f-150b-482d-aebc-9426ef6a1cf9.png">

Note - This pr only covers `String` -> `CellPath`, any other conversions
should be considered as expected?

# 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-10 15:26:42 +02:00
fa6bb147ea remove example missed from an earlier refactor (#7419)
# Description

When `seq date` was changed to remove the `-s` param, the example was
missed. This PR removes that example.

# 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.
2022-12-09 20:04:14 -06:00
220b105efb Reduced LOC by replacing several instances of Value::Int {}, Value::Float{}, Value::Bool {}, and Value::String {} with Value::int(), Value::float(), Value::boolean() and Value::string() (#7412)
# Description

While perusing Value.rs, I noticed the `Value::int()`, `Value::float()`,
`Value::boolean()` and `Value::string()` constructors, which seem
designed to make it easier to construct various Values, but which aren't
used often at all in the codebase. So, using a few find-replaces
regexes, I increased their usage. This reduces overall LOC because
structures like this:
```
Value::Int {
  val: a,
  span: head
}
```
are changed into
```
Value::int(a, head)
```
and are respected as such by the project's formatter.
There are little readability concerns because the second argument to all
of these is `span`, and it's almost always extremely obvious which is
the span at every callsite.

# User-Facing Changes

None.

# 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-09 11:37:51 -05:00
b56ad92e25 ++= appendAssign operator (#7346) (#7354)
# Description

Closes  https://github.com/nushell/nushell/issues/7346



# Tests + Formatting
```
> mut a = [1 2 3]
> $a ++= [4 5 6]
> $a
[1 2 3 4 5 6]
```
2022-12-09 11:20:58 -05:00
fc5fe4b445 ensure error in else is forwarded appropriately (#7411)
# Description

Fixes #7407. 

```
/home/gabriel/CodingProjects/nushell〉if false { 'a' } else { $foo }    12/09/2022 08:14:48 PM
Error: nu::parser::variable_not_found (link)

  × Variable not found.
   ╭─[entry #1:1:1]
 1 │ if false { 'a' } else { $foo }
   ·                         ──┬─
   ·                           ╰── variable not found
   ╰────
```

# 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-09 15:48:12 +02:00
c01d44e37d Add arbitrary base math log (#7409)
Adds new command `math log` that takes as a required positional argument
a base.

Specialized for `math log 2` and `math log 10` for better performance
and precision that matches the expectations there. This leads to
discontinuities in numerical error but should make a better trade-off
for common usecases.


Example testing of the happy path
2022-12-09 11:20:42 +01:00
5a0e86aa70 fix external completions; add a caret when there is overlap (#7405)
# Description

Fixes #5424. Checking the code, apparently this was always supposed to
work; however, because it compared the `Suggestion`s directly, and
internal commands had descriptions while external commands did not, it
never did function properly.

# User-Facing Changes

Completing to external commands (with overlap) adds a caret so the
external command is actually run.

# 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-09 22:27:50 +13:00
b4529a20e8 Add math tau (#7408)
# Description

Prompted by
https://discord.com/channels/601130461678272522/615329862395101194/1050693116501426216

# User-Facing Changes

New command `math tau` and endless agony whether to use `math pi` or
`math tau`

# Tests + Formatting

Example test
2022-12-09 22:27:19 +13:00
b938adefaa Fix math e usage text (#7406)
Closes #7401

# Tests + Formatting

Doesn't apply, doc fix
2022-12-09 09:56:12 +01:00
b39d797c1f Add quotes to hash file autocomplete (#7398)
# Description

Fixes #6741. Autocompleting a dir/file named something like foo#bar will
now complete to \`foo#bar\`
2022-12-08 21:37:10 +01:00
4240bfb7b1 Fix tab not working in vi editor mode (#7396)
# Description

The "tab" key now cycles completions in all editor modes, not just
emacs.

# User-Facing Changes

Updated default config

# 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-08 11:07:40 +02:00
JT
b7572f107f Remove and/or from 'help operators' (#7388)
# 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-07 18:05:51 -06:00
JT
379e3d70ca Better errors when bash-like operators are used (#7241)
# Description

Adds improved errors for when a user uses a bashism that nu doesn't
support.

fixes #7237 

Examples:

```
Error: nu::parser::shell_andand (link)

  × The '&&' operator is not supported in Nushell
   ╭─[entry #1:1:1]
 1 │ ls && ls
   ·    ─┬
   ·     ╰── instead of '&&', use ';' or 'and'
   ╰────
  help: use ';' instead of the shell '&&', or 'and' instead of the boolean '&&'
```

```
Error: nu::parser::shell_oror (link)

  × The '||' operator is not supported in Nushell
   ╭─[entry #8:1:1]
 1 │ ls || ls
   ·    ─┬
   ·     ╰── instead of '||', use 'try' or 'or'
   ╰────
  help: use 'try' instead of the shell '||', or 'or' instead of the boolean '||'
```

```
Error: nu::parser::shell_err (link)

  × The '2>' shell operation is 'err>' in Nushell.
   ╭─[entry #9:1:1]
 1 │ foo 2> bar.txt
   ·     ─┬
   ·      ╰── use 'err>' instead of '2>' in Nushell
   ╰────
```

```
Error: nu::parser::shell_outerr (link)

  × The '2>&1' shell operation is 'out+err>' in Nushell.
   ╭─[entry #10:1:1]
 1 │ foo 2>&1 bar.txt
   ·     ──┬─
   ·       ╰── use 'out+err>' instead of '2>&1' in Nushell
   ╰────
  help: Nushell redirection will write all of stdout before stderr.
```


# User-Facing Changes

**BREAKING CHANGES**

This removes the `&&` and `||` operators. We previously supported by
`&&`/`and` and `||`/`or`. With this change, only `and` and `or` are
valid boolean operators.

# 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-08 12:02:11 +13:00
JT
6fc87fad72 Fix input redirect for externals (#7387)
# Description

Ooops, fix the input redirection logic so we don't break vim.

# 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-08 11:33:42 +13:00
JT
fa15a2856a Add OneOf shape to fix else (#7385)
# Description

fixes #7384 

This is a stop-gap fix until we remove type-directed parsing. With this,
you can create a `OneOf` shape that can be one of a list of syntax
shapes. This gives you a little more control than you get with `Any`,
allowing you to add `Block` without breaking other parsing rules.

# User-Facing Changes

`else` block will no longer capture variables as it will now use a block
instead of a closure.

# 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-08 08:58:54 +13:00
JT
eaec480f42 Improve empty pipelines (#7383)
# Description

This fix changes pipelines to allow them to actually be empty. Mapping
over empty pipelines gives empty pipelines. Empty pipelines immediately
return `None` when iterated.

This removes a some of where `Span::new(0, 0)` was coming from, though
there are other cases where we still use it.

# User-Facing Changes

None

# 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-08 07:31:57 +13:00
d18587330a fix semicolon doesn't work for some commands (#7373)
# Description

Fix semicolon working for the following commands which runs to failed:

1. into record (example: `into record; echo 'aa'`) -- the final aa
shouldn't be printed
2. save ( example: `touch a; chmod a-w a; echo 'aa' | save a; echo asdf`
) -- the final asdf shouldn't be printed
3. fetch ( example: `touch a; chmod a-w a; fetch https://www.google.com
-o a; echo asdf` ) -- the final asdf shouldn't be printed
4. registry_query (I don't have window machine, sorry for that I can't
demo for it)

I've reviewed most of built-in commands, and it seems that other
commands works fine.

# 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.
2022-12-06 19:48:39 -08:00
5114dfca7d Remove use of deprecated actions-rs/cargo GH action (#7375)
Our CI actions have a lot of warnings like this:

>  nu-fmt-clippy (ubuntu-20.04, stable)
> Node.js 12 actions are deprecated. For more information see:
https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.
Please update the following actions to use Node.js 16:
actions-rs/cargo@v1.0.1

[It looks like `actions-rs/cargo` is
abandoned](https://github.com/actions-rs/toolchain/issues/216). But the
good news is we don't actually need it, we can just run `cargo`
subcommands without a special action because we've already installed
`cargo` with `actions-rust-lang/setup-rust-toolchain`.
2022-12-06 18:57:07 -08:00
3395beaa56 Make seq return a ListStream where possible (#7367)
# Description

Title.

# User-Facing Changes

Faster seq that works better with functions that take in `ListStream`s.

# 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-06 18:48:03 -08:00
df66d9fcdf Fix watch for block+closure split (#7374)
The block+closure split broke `watch` for some use cases. Fixed.

We should eventually add some tests for `watch` but it's a little tricky
since it's an interactive command.

Closes #7362.
2022-12-06 18:20:20 -08:00
1af1e0b5a3 fix upsert index of zero (#7350)
# Description

Try to fix #7347, this pr does not fix the error message to `upserting
beyond 1 + the list's length doesn't work,`, I feel this is still not
explicit or general engouh, if we want to change, what would be the best
message to be printed out ?

Add tests too

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

- [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-06 15:26:27 -05:00
9b41f9ecb8 Allow $env and mutable records to be mutated by = (closes #7110) (#7318)
# Description

Closes #7110. ~~Note that unlike "real" `mut` vars, $env can be deeply
mutated via stuff like `$env.PYTHON_IO_ENCODING = utf8` or
`$env.config.history.max_size = 2000`. So, it's a slightly awkward
special case, arguably justifiable because of what $env represents (the
environment variables of your system, which is essentially "outside"
normal Nushell regulations).~~
EDIT: Now allows all `mut` vars to be deeply mutated using `=`, on
request.

# 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-06 19:51:55 +02:00
48ade4993d remove redundant code mentioning ToCsv (#7370)
# Description

```rust
ToCsv
```

ToCsv was in there twice so I removed the 2nd reference...

_(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-06 09:42:12 -08:00
4ecc807dbb fix split list when separater is the first element of list (#7355)
# Description

Fixes: #7278

# 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-06 08:51:38 -06:00
86b69cc5d1 add input_output_types() to ansi gradient (#7357)
# Description

Add the input_output_types() to the ansi gradient command in support of
#7320

# 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.
2022-12-06 08:51:18 -06:00
017a13fa3f bump to dev build v0.72.2 (#7360)
# Description

After the 0.72.1 hotfix, let's bump our dev builds to 0.72.2.

# 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.
2022-12-05 14:18:06 -06:00
ca12b2e30e Pin CI jobs to Ubuntu 20.04 (#7359)
The release job was pinned to Ubuntu 20.04 to avoid glibc breakage in
https://github.com/nushell/nushell/pull/7290. This PR updates the CI
jobs to keep things consistent (it would sure be unpleasant if things
worked in CI but not at release time).
2022-12-05 10:12:36 -08:00
db6c804b17 fix menus in default config (#7352)
Simply fixes menus commands_with_description, commands_menu
2022-12-04 19:23:58 -08:00
57ff668d2e Make SQLite queries cancellable (#7351)
This change makes SQLite queries (`open foo.db`, `open foo.db | query db
"select ..."`) cancellable using `ctrl+c`. Previously they were not
cancellable, which made it unpleasant to accidentally open a very large
database or run an unexpectedly slow query!

UX-wise there's not too much to show:


![image](https://user-images.githubusercontent.com/26268125/205519205-e1f2ab58-c92d-4b96-9f80-eb123f678ec3.png)

## Notes

I was hoping to make SQLite queries streamable as part of this work, but
I ran into 2 problems:
1. `rusqlite` lifetimes are nightmarishly complex and they make it hard
to create a `ListStream` iterator
2. The functions on Nu's `CustomValue` trait return `Value` not
`PipelineData` and so `CustomValue` implementations can't stream data
AFAICT.
2022-12-04 16:49:47 -08:00
9fb9b16b38 kill: don't show signal example on windows (#7353)
# Description

Windows doesn't support the --signal(-s) option, so don't show the
example in help.

# User-Facing Changes

N/A

# 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-04 16:39:54 -08:00
41178dff90 Try to fix #7338 (#7343)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-04 17:47:46 -06:00
12deff5d1b Add comments for nu syntax shape (#7349)
# Description

FIx the typo of `List` and also add more comments to other variants


# 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-04 11:20:47 -08:00
21a645b1a9 Improve error message for illegal filenames on Windows (#7348)
`ls` can fail when a directory contains a file that violates [the
Windows file naming
conventions](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions).
This PR tweaks the error message so we tell the user _which_ file caused
the problem.

Closes #7345.

### Before:

![image](https://user-images.githubusercontent.com/26268125/205508355-2875f851-6b61-4897-97b8-9094b24ea197.png)

### After:


![image](https://user-images.githubusercontent.com/26268125/205508325-0b4efd25-b454-4d1b-b8e9-cb26803fbcff.png)


## Future Work

Like Chris said in the linked issue, it would be even better if Nu could
just handle these naughty files like cmd.exe and pwsh do. If someone has
the time to dive into how PowerShell does this, that would be much
appreciated.
2022-12-04 10:33:30 -08:00
e8a55aa647 Overhaul schema command, remove database name (#7344)
This PR changes the `schema` command for viewing the schema of a SQLite
database file. It removes 1 level of nesting (intended to handle
multiple databases in the same connection) that I believe is
unnecessary.

### Before

![image](https://user-images.githubusercontent.com/26268125/205467643-05df0f64-bc8f-4135-9ff1-f978cc7a12bd.png)

### After

![image](https://user-images.githubusercontent.com/26268125/205467655-c4783184-9bde-46e2-9316-0f06acd1abe1.png)

## Rationale

A SQLite database connection can technically be associated with multiple
non-temporary databases using [the ATTACH DATABASE
command](https://www.sqlite.org/lang_attach.html). But it's not possible
to do that _in the context of Nushell_, and so I believe that there is
no benefit to displaying the schema as if there could be multiple
databases.

I initially raised this concern back in April, but we decided to keep
the database nesting because at the time we were still looking into more
generalized database functionality (i.e. not just SQLite). I believe
that rationale no longer applies.

Also, the existing code would not have worked correctly even if a
connection had multiple databases; for every database, it was looking up
tables without filtering them by database:

6295b20545/crates/nu-command/src/database/values/sqlite.rs (L104-L118)

## Future Work

I'd like to add information on views+triggers to the `schema` output.
I'm also working on making it possible to `ctrl+c` reading from a
database (which is turning into a massive yak shave).
2022-12-03 18:16:57 -08:00
6295b20545 add background colors to the ansi command (#7312)
# Description

This PR adds the ability to easily specific background colors using the
ansi command. It also allows you to use attributes.

![image](https://user-images.githubusercontent.com/343840/205151594-968cc7a6-f1d8-406e-aa8b-cb67e8487563.png)

I also updated the grid output to be clear that the output includes an
escape character, represented here by `\e`.

![image](https://user-images.githubusercontent.com/343840/205151066-09966f90-6341-4eeb-83c1-df3d169cfbf6.png)

# User-Facing Changes

You can see a list of these new items with `ansi --list` with their long
and short names.

# 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-03 13:08:51 -06:00
850ecf648a Protocol: debug_assert!() Span to reflect a valid slice (#6806)
Also enforce this by #[non_exhaustive] span such that going forward we
cannot, in debug builds (1), construct invalid spans.

The motivation for this stems from #6431 where I've seen crashes due to
invalid slice indexing.

My hope is this will mitigate such senarios

1. https://github.com/nushell/nushell/pull/6431#issuecomment-1278147241

# Description

(description of your pull request here)

# Tests

Make sure you've done the following:

- [ ] Add tests that cover your changes, either in the command examples,
the crate/tests folder, or in the /tests folder.
- [ ] Try to think about corner cases and various ways how your changes
could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a
minimal example with steps on how to reproduce so one can verify your
change works.

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)
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- [ ] `cargo test --workspace --features=extra` to check that all the
tests pass

# Documentation

- [ ] If your PR touches a user-facing nushell feature then make sure
that there is an entry in the documentation
(https://github.com/nushell/nushell.github.io) for the feature, and
update it if necessary.
2022-12-03 11:44:12 +02:00
e6cf18ea43 nu-explore/ A few fixes. (#7334)
close #7322
close #7323

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-03 00:12:33 +02:00
d28624796c make histogram sorted (#7267)
# Description

Closes:  #7208

After this pr, histogram will output by count descending

# 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-02 09:29:19 -08:00
380c216d77 Fix documentation for merge (#7329)
# Description

`merge` no longer accepts blocks as arguments.
2022-12-02 11:35:28 -05:00
ee5a387300 Handle mixed LF+CRLF in lines (#7316)
This closes #4989. Previously `lines` was unable to handle text input
with CRLF line breaks _and_ LF line breaks.

### Before:

![image](https://user-images.githubusercontent.com/26268125/205207685-b25da9e1-19fa-4abb-8ab2-0dd216c63fc0.png)

### After:


![image](https://user-images.githubusercontent.com/26268125/205207808-9f687242-a8c2-4b79-a12c-38b0583d8d52.png)
2022-12-02 11:30:26 -05:00
3ac36879e0 Handle ctrl-c in RawStream iterator (#7314)
Fixes #7246 and #1898.

Darren noticed that `open /dev/random` could not be interrupted by
`ctrl+c`. Thankfully the solution was very simple; it looks like we just
forgot to check `ctrlc` in the `impl Iterator for RawStream`!

To reproduce this, just run `open /dev/random` and then cancel it with
`ctrl+c`.
2022-12-02 08:00:56 -08:00
bc0c9ab698 add :q! alias to explore command (#7326)
# Description

This simply adds an alias to the explore quit command in order to quit
out of explore. `:q` and `:q!` work now.

# 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.
2022-12-02 08:13:19 -06:00
5762489070 Edited help text and examples in explore for readability (#7324)
# Description

* Various help messages were edited for clarity/grammar/etc.
* Some examples were made more interesting or relevant

# 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-02 08:01:02 -06:00
fcdc474731 uniq-by command (#7295)
New command `uniq-by` to get uniq results by column.

Closes https://github.com/nushell/nushell/issues/7109
2022-12-02 11:36:01 +01:00
f491d3e1e1 Rename $env.config.explore_config to $env.config.explore (for consistency with $env.config.ls, $env.config.table etc.) (#7317)
# Description

* `$env.config.explore_config` renamed to `$env.config.explore`. This
follows the principle that config subrecords relating to single commands
(as this relates to `explore`) should be exactly named after the command
(see `ls`, `rm`, `table` etc.)
* In `into_config()`, moved the match arm relating to
`$env.config.explore` out of the "legacy options" section (which is
slated for removal in a later version).

# User-Facing Changes

`explore` is not in any public releases yet, so this has no end-user
impact.

# 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-02 01:42:18 -05:00
94c89eb623 Use setup-rust-toolchain for release workflow (#7315)
# Description

Use setup-rust-toolchain for release workflow to inline with the CI
workflow

Test workflow:
https://github.com/hustcer/nu-release/actions/runs/3598316520
Demo Release: https://github.com/hustcer/nu-release/releases/tag/v0.72.8
2022-12-01 19:23:29 -08:00
cf0a18be51 Fix where -b flag (#7313)
# Description

The `where -b` flag was broken. The following now works:
```
let cond = {|x| $x.name !~ 'foo'}; ls | where -b $cond
```

This is just a quick fix. Ultimately, the `where` command shouldn't need
the flag and `where $cond` should work. The first step, however, is to
move `where` away from shape-directed parsing and make it a parser
keyword.

# User-Facing Changes

`where -b` is not broken

# 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-01 15:11:03 -08:00
JT
0621ab6652 couple minor updates to xml deps (#7311)
# Description

Just some minor updates to xml deps

# 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-02 08:25:13 +13:00
64a028cc76 Deliver a few fixes for explore command (#7310)
ref #6984

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-12-01 12:14:56 -06:00
f71a45235a fix try for external command runs to failed (#7300)
# Description

Fixes: #7298

So `try .. catch` works better on external command failed.

# User-Facing Changes

```
try {nu --testbin fail} catch {print "fail"}
```

After this pr, it will output "fail"

# 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: JT <547158+jntrnr@users.noreply.github.com>
2022-12-02 05:58:32 +13:00
718ee3d545 [MVP][WIP] less like pager (#6984)
Run it as `explore`.

#### example

```nu
ls | explore
```

Configuration points in `config.nu` file.
```
  # A 'explore' utility config
   explore_config: {
     highlight: { bg: 'yellow', fg: 'black' }
     status_bar: { bg: '#C4C9C6', fg: '#1D1F21' }
     command_bar: { fg: '#C4C9C6' }
     split_line: '#404040'
     cursor: true
     # selected_column: 'blue'
     # selected_row: { fg: 'yellow', bg: '#C1C2A3' }
     # selected_cell: { fg: 'white', bg: '#777777' }
     # line_shift: false,
     # line_index: false,
     # line_head_top: false,
     # line_head_bottom: false,
   }
```

You can start without a pipeline and type `explore` and it'll give you a
few tips.

![image](https://user-images.githubusercontent.com/343840/205088971-a8c0262f-f222-4641-b13a-027fbd4f5e1a.png)

If you type `:help` you an see the help screen with some information on
what tui keybindings are available.

![image](https://user-images.githubusercontent.com/343840/205089461-c4c54217-7ec4-4fa0-96c0-643d68dc0062.png)

From the `:help` screen you can now hit `i` and that puts you in
`cursor` aka `inspection` mode and you can move the cursor left right up
down and it you put it on an area such as `[table 5 rows]` and hit the
enter key, you'll see something like this, which shows all the `:`
commands. If you hit `esc` it will take you to the previous screen.

![image](https://user-images.githubusercontent.com/343840/205090155-3558a14b-87b7-4072-8dfb-dc8cc2ef4943.png)

If you then type `:try` you'll get this type of window where you can
type in the top portion and see results in the bottom.

![image](https://user-images.githubusercontent.com/343840/205089185-3c065551-0792-43d6-a13c-a52762856209.png)

The `:nu` command is interesting because you can type pipelines like
`:nu ls | sort-by type size` or another pipeline of your choosing such
as `:nu sys` and that will show the table that looks like this, which
we're calling "table mode".

![image](https://user-images.githubusercontent.com/343840/205090809-e686ff0f-6d0b-4347-8ed0-8c59adfbd741.png)

If you hit the `t` key it will now transpose the view to look like this.

![image](https://user-images.githubusercontent.com/343840/205090948-a834d7f2-1713-4dfe-92fe-5432f287df3d.png)

In table mode or transposed table mode you can use the `i` key to
inspect any collapsed field like `{record 8 fields}`, `[table 16 rows]`,
`[list x]`, etc.

One of the original benefits was that when you're in a view that has a
lot of columns, `explore` gives you the ability to scroll left, right,
up, and down.

`explore` is also smart enough to know when you're in table mode versus
preview mode. If you do `open Cargo.toml | explore` you get this.

![image](https://user-images.githubusercontent.com/343840/205091822-cac79130-3a52-4ca8-9210-eba5be30ed58.png)

If you type `open --raw Cargo.toml | explore` you get this where you can
scroll left, right, up, down. This is called preview mode.

![image](https://user-images.githubusercontent.com/343840/205091990-69455191-ab78-4fea-a961-feafafc16d70.png)

When you're in table mode, you can also type `:preview`. So, with `open
--raw Cargo.toml | explore`, if you type `:preview`, it will look like
this.

![image](https://user-images.githubusercontent.com/343840/205092569-436aa55a-0474-48d5-ab71-baddb1f43027.png)

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-12-01 09:32:10 -06:00
e92678ea2c Add a deprecation note for removed build-string (#7307)
Build string was removed for 0.72 by #7144

Adds a deprecation message
2022-12-01 16:26:59 +01:00
1f175d4c98 Add natural logarithm (#7258)
- `math ln`
2022-12-01 15:58:05 +01:00
4d6ccf2540 Add inverse hyperbolic functions (#7258)
- `math arcsinh`
- `math arccosh`
- `math arctanh`
2022-12-01 15:58:05 +01:00
aa6c3936d2 Add inverse for trigonometric functions (#7258)
- `math arcsin`
- `math arccos`
- `math arctan`

Again include `--degrees` or `-d` for convenient output in degrees
2022-12-01 15:58:05 +01:00
4f05994b36 Add basic hyperbolic functions (#7258)
`math sinh`
`math cosh`
`math tanh`
2022-12-01 15:58:05 +01:00
b27d6b2cb1 Add basic trigonometric functions (#7258)
`math sin`
`math cos`
`math tan`

Support degrees with the flag `--degrees`/`-d`
2022-12-01 15:58:05 +01:00
64f226f7da Add math pi and math e constants (#7258)
Currently implemented as commands

Work towards #7073

Add them to the example test harness together with `math round`
2022-12-01 15:58:05 +01:00
5c1606ed82 Add -n flag to sort (formerly only available on sort-by) (#7293)
# Description

* `-n`, `--natural` flag from `sort-by` is now on the plain `sort`.
* The `-i`, `-n` and `-r` flags now work with single record sorting
(formerly they didn't)

# 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-01 07:11:30 -06:00
11977759ce fix cal input_output_types signature (#7306)
# Description

This is one of many commands that needs the `input_output_types()` part
of the signature filled in so that `$nu.scope.commands` shows the
signature properly which leads to the documentation being updated
properly.

TIL that when commands like `cal` don't have Example tests that can
easily be expressed and require the use of `None` results that we need
to use this as part of the signature.

```rust
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
```

Related to https://github.com/nushell/nushell/issues/7287

# 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.
2022-12-01 07:10:49 -06:00
bc3dc98b34 add -f, --force for save command (#7262)
# Description

Closes: #6920 

# User-Facing Changes

```
❯ "asdf" | save dump.rdb
Error:
  × Destination file already exists
   ╭─[entry #21:1:1]
 1 │ "asdf" | save dump.rdb
   ·               ────┬───
   ·                   ╰── Destination file '/tmp/dump.rdb' already exists
   ╰────
  help: you can use -f, --force to force overwriting the destination
```
# 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-01 06:26:17 -06:00
6fadc72553 Remove unused dev-dependencies (#7285)
Warning: the `nu-json` crate seems to be undertested as the main test
code is commented out. Thus the unused dependencies there were just
commented out
2022-12-01 11:53:24 +01:00
a9e6b1ec6b Add did-you-mean suggestions for bitwise ops (#7252)
Continues work from #7251 to include the bitwise operations.
Covers the C style conventions as well as the typo `bits-*` instead of
`bit-*`
2022-12-01 11:34:41 +01:00
cbc7b94b02 Remove inactive actions-rs/toolchain@v1.0.6 for release workflow (#7302)
# Description

Remove inactive actions-rs/toolchain@v1.0.6 for release workflow,
https://github.com/actions-rs/toolchain is inactive for more than two
years, and have lots of unfixed warnings:
https://github.com/actions-rs/toolchain/issues?q=is%3Aissue+is%3Aopen+warning

After this PR:

Workflow running result:
https://github.com/hustcer/nu-release/actions/runs/3590194180
Release Test: https://github.com/hustcer/nu-release/releases/tag/v0.72.7
2022-12-01 16:30:25 +08:00
45c66e2090 Update release script to nu v0.71 and use ubuntu-20.04 to build nu binary (#7290)
# Description

1. Update nu to v0.71 for release script
2. Remove the usage of `set-output` see:
https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
3. Use `ubuntu-20.04` instead of `ubuntu-latest` to fix #7282 

To check the workflow running result see:
https://github.com/hustcer/nu-release/actions/runs/3588720720/jobs/6040412953

# 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-01 10:44:21 +08:00
11b2423544 add comments to release-pkg for manual running (#7277)
# Description

This PR is just some comments in the release-pkg.nu script. We had to
figure out how to run it manually so I thought it would be good to
document those requirements just in case we need to do it again
sometime.

# User-Facing Changes

N/A

# 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-11-30 20:18:28 -06:00
1f9907d2ff Fix failing test after #7051 (#7299)
Numerics as record names have to be quoted
Changed to string
2022-12-01 00:48:02 +01:00
fd503fceaf allow tables in ++ operator (#7051)
This PR closes #6916, which now allows table/table operations on the
`++` operator.
```
[[a b]; [1 2]] ++ [[a b]; [1 3]]
╭───┬───┬───╮
│ # │ a │ b │
├───┼───┼───┤
│ 0 │ 1 │ 2 │
│ 1 │ 1 │ 3 │
╰───┴───┴───╯
```
2022-12-01 00:21:59 +01:00
b7e5790cd1 fix dfr datetime conversion (#7264)
# Description

Closes #7257

This fixes an issue where dataframes would always try to convert
datetimes with milliseconds. Since the timeunit is passed in, I make use
of it and try to choose the appropriate divisor.


# 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.
2022-11-30 17:10:28 -06:00
3caab5de36 bump to dev release v0.72.1 (#7281)
# Description

Just bumping to the next dev version. Submitting as a draft until we're
ready.

# 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.
2022-11-30 16:06:22 -06:00
fa97e819eb Suggest using float on Value::Int overflow (#7253)
# Description

Suggest that floats support a larger range of values but warn about the
loss in precision.


![image](https://user-images.githubusercontent.com/15833959/204114339-c987cd47-f035-4c01-853f-e9a00441bf49.png)


(Doesn't apply to the types with associated units)

# Tests + Formatting

(-)
2022-11-29 13:30:02 -08:00
bdc4bf97a7 Upgrade windows and trash crates (#7259)
This upgrades the `windows` and `trash` crates so we are only compiling
1 version of the `windows` crate ([I recently upgraded the version used
in `trash`](https://github.com/Byron/trash-rs/pull/58)).

I was hoping that this would lead to some decent compile time
improvements, but unfortunately it did not. Compiling 1 version of
`windows` with all the features unified is about as slow as compiling 2
versions with distinct feature sets. Still, might as well upgrade.
2022-11-29 13:05:32 -08:00
JT
cfb0f3961b Simple README updates (#7279)
# 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-11-30 08:10:11 +13:00
JT
8fa965118c Update release.yml 2022-11-30 06:35:16 +13:00
JT
9c800bcb2c bump to 0.72 (#7272)
# 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-11-29 20:17:23 +13:00
ea4d8c5f49 update release-pkg.nu to include more recent less version (#7265)
# Description

This PR upgrades the Windows less version from v590 to v608

# 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.
2022-11-29 15:21:00 +13:00
5d2abdd1c3 add a more verbose description of operators (#7263)
# Description

I was thinking that it may be helpful to have a more verbose description
of our operators. Please let me know if you have better wording.

Also, while not strictly considered an operator, i added `not` to avoid
some confusion.
<img width="1574" alt="Screenshot 2022-11-28 at 7 57 30 PM"
src="https://user-images.githubusercontent.com/343840/204419666-7c4dbb43-26f5-4cd5-9a4e-a1555a9e700f.png">


# User-Facing Changes

Adds description column

# 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-11-29 15:20:17 +13:00
7d5333db3b Clean up .sh scripts with shellcheck (#7261)
Ran [`shellcheck`](https://github.com/koalaman/shellcheck) on our 3
shell scripts and fixed the warnings. This caught that the scripts were
[broken because of their
shebang](https://www.shellcheck.net/wiki/SC3030):

```
〉./uninstall-all.sh

----------------------------------------------
Uninstall nu and all plugins from cargo/bin...
----------------------------------------------
./uninstall-all.sh: 8: Syntax error: "(" unexpected
```

```
〉shellcheck *.sh

In build-all-maclin.sh line 8:
NU_PLUGINS=(
           ^-- SC3030 (warning): In POSIX sh, arrays are undefined.


In build-all-maclin.sh line 18:
for plugin in "${NU_PLUGINS[@]}"
               ^--------------^ SC3054 (warning): In POSIX sh, array references are undefined.


In build-all-maclin.sh line 20:
    echo '' && cd crates/$plugin
               ^---------------^ SC2164 (warning): Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
                         ^-----^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean:
    echo '' && cd crates/"$plugin" || exit


In install-all.sh line 14:
NU_PLUGINS=(
           ^-- SC3030 (warning): In POSIX sh, arrays are undefined.


In install-all.sh line 22:
for plugin in "${NU_PLUGINS[@]}"
               ^--------------^ SC3054 (warning): In POSIX sh, array references are undefined.


In install-all.sh line 28:
    cd crates/$plugin && cargo install --path . && cd ../../
              ^-----^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean:
    cd crates/"$plugin" && cargo install --path . && cd ../../


In uninstall-all.sh line 8:
NU_PLUGINS=(
           ^-- SC3030 (warning): In POSIX sh, arrays are undefined.


In uninstall-all.sh line 16:
for plugin in "${NU_PLUGINS[@]}"
               ^--------------^ SC3054 (warning): In POSIX sh, array references are undefined.


In uninstall-all.sh line 18:
    cargo uninstall $plugin
                    ^-----^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean:
    cargo uninstall "$plugin"

For more information:
  https://www.shellcheck.net/wiki/SC2164 -- Use 'cd ... || exit' or 'cd ... |...
  https://www.shellcheck.net/wiki/SC3030 -- In POSIX sh, arrays are undefined.
  https://www.shellcheck.net/wiki/SC3054 -- In POSIX sh, array references are...
  ```
 
To fix SC2164  I used `set -euo pipefail` as per this Julia Evans suggestion:
  
![image](https://user-images.githubusercontent.com/26268125/204181003-22283dcb-924d-4c0d-91f6-1ea635dbf0fc.png)
2022-11-27 22:13:06 -05:00
2a8a628b72 add help operators command (#7254)
# Description

This PR adds a new command called `help operators`. The intention is to
make nushell's operators more discoverable.

Operations are evaluated in the precedence order (from highest to
lowest).

<img width="737" alt="Screenshot 2022-11-26 at 7 23 15 PM"
src="https://user-images.githubusercontent.com/343840/204115311-56765517-c36d-44d5-b303-43ffc0e980f6.png">

# 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.
2022-11-27 21:03:17 +13:00
c4d2b787aa Add did-you-mean suggestions for operators (#7251)
# Description

Adds a `ParseError::UnkownOperator` and covers `^`,`pow`,`is`,`===`,`%`,
and `contains` as likely operators based on other languages. Provides
suggestion for the user to find the nu language feature they are looking
for.


![image](https://user-images.githubusercontent.com/15833959/204108373-d1165988-ad87-4a2e-bd81-b67a44072571.png)


# Tests + Formatting

(-)
2022-11-27 10:59:43 +13:00
a4e11726cf pin to rust v1.65 (#7249)
# Description

This fixes the compilation problems with `aarch64-apple-darwin` on rust
1.64 as well as the `zstd` problems. We recently found out that `zstd`
pinned to 1.65.



# User-Facing Changes

N/A

# 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-11-27 09:26:54 +13:00
9850fbd77d chore: chrono_update (#7132)
chrono version update

# Description

upgrade chrono to 0.4.23

# Major Changes

If you're considering making any major change to nushell, before
starting work on it, seek feedback from regular contributors and get
approval for the idea from the core team either on
[Discord](https://discordapp.com/invite/NtAbbGn) or [GitHub
issue](https://github.com/nushell/nushell/issues/new/choose).
Making sure we're all on board with the change saves everybody's time.
Thanks!

# Tests + Formatting

Make sure you've done the following, if applicable:

- Add tests that cover your changes (either in the command examples, the
crate/tests folder, or in the /tests folder)
- Try to think about corner cases and various ways how your changes
could break. Cover those in the tests

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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass

# After Submitting

* Help us keep the docs up to date: If your PR affects the user
experience of Nushell (adding/removing a command, changing an
input/output type, etc.), make sure the changes are reflected in the
documentation (https://github.com/nushell/nushell.github.io) after the
PR is merged.

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-11-27 07:19:02 +13:00
2ccb91dc6a Add logical xor operator (#7242)
We already have the binary `bit-xor` and the shortcircuiting logical
`or`(`||`) and `and`(`&&`).
This introduces `xor` as a compact form for both brevity and clarity.
You can express the operation through `not`/`and`/`or` with a slight
risk of introducing bugs through typos.

Operator precedence

`and` > `xor` > `or`

Added logic and precedence tests.
2022-11-26 17:02:37 +01:00
3e76ed9122 add into record command (#7225)
# Description

This command converts things into records.
<img width="466" alt="Screenshot 2022-11-24 at 2 10 54 PM"
src="https://user-images.githubusercontent.com/343840/203858104-0e4445da-9c37-4c7c-97ec-68ec3515bc4b.png">

<img width="716" alt="Screenshot 2022-11-24 at 5 04 11 PM"
src="https://user-images.githubusercontent.com/343840/203872621-48cab199-ba57-44fe-8f36-9e1469b9c4ef.png">



It also converts dates into record but I couldn't get the test harness
to accept an example.

Thanks to @WindSoilder for writing the "hard" parts of this. :)


_(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.

Co-authored-by: WindSoilder <WindSoilder@outlook.com>
2022-11-26 09:00:47 -06:00
JT
2223fd663a Clean up keyword lines in help (#7243)
# Description

This makes the help messages cleaner for keyword-style arguments.

Before:
```
  (optional) else_expression <Keyword([101, 108, 115, 101], Expression)>: expression or block to run if check fails
```

Now:
```
  (optional) "else" + <expression>: expression or block to run if check fails
```


# User-Facing Changes

Changes how help is printed, so we use slightly different shape names

# 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-11-26 20:16:39 +13:00
3f960012cd Revert "remove zstd warning message" (#7235)
This reverts commit ee81030600.


# 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-11-24 20:01:22 -08:00
0b094e2bf2 remove zstd warning message (#7232)
# Description

This removes the warning message we were getting on compilation...

```rust
warning: /Users/ma/j/tmp17/nushell/crates/nu-command/Cargo.toml: version requirement `=2.0.1+zstd.1.5.2` for dependency `zstd-sys` includes semver metadata which will be ignored, removing the metadata is recommended to avoid confusion
```

https://github.com/nushell/nushell/pull/7227

a slight modification of this PR

_(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-11-24 18:39:32 -08:00
2388e1e80b Reorder export-env eval and allow reloading an overlay (#7231)
# Description

This PR is a response to the issues raised in
https://github.com/nushell/nushell/pull/7087. It consists of two
changes:
* `export-env`, when evaluated in `overlay use`, will see the original
environment. Previously, it would see the environment from previous
overlay activation.
* Added a new `--reload` flag that reloads the overlay. Custom
definitions will be kept but the original definitions and environment
will be reloaded.

This enables a pattern when an overlay is supposed to shadow an existing
environment variable, such as `PROMPT_COMMAND`, but `overlay use` would
keep loading the value from the first activation. You can easily test it
by defining a module
```
module prompt {
    export-env {
        let-env PROMPT_COMMAND = (date now | into string)
    }
}
```
Calling `overlay use prompt` for the first time changes the prompt to
the current time, however, subsequent calls of `overlay use` won't
change the time. That's because overlays, once activated, store their
state so they can be hidden and restored at later time. To force-reload
the environment, use the new flag: Calling `overlay use --reload prompt`
repeatedly now updates the prompt with the current time each time.

# User-Facing Changes

* When calling `overlay use`, if the module has an `export-env` block,
the block will see the environment as it is _before_ the overlay is
activated. Previously, it was _after_.
* A new `overlay use --reload` flag.

# 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-11-24 23:45:24 +01:00
JT
62e34b69b3 New commands: break, continue, return, and loop (#7230)
# Description

This adds `break`, `continue`, `return`, and `loop`.

* `break` - breaks out a loop
* `continue` - continues a loop at the next iteration
* `return` - early return from a function call
* `loop` - loop forever (until the loop hits a break)

Examples:
```
for i in 1..10 {
    if $i == 5 {
       continue
    } 
    print $i
}
```

```
for i in 1..10 {
    if $i == 5 {
        break
    } 
    print $i
}
```

```
def foo [x] {
    if true {
        return 2
    }
    $x
}
foo 100
```

```
loop { print "hello, forever" }
```

```
[1, 2, 3, 4, 5] | each {|x| 
    if $x > 3 { break }
    $x
}
```

# User-Facing Changes

Adds the above commands.

# 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-11-25 09:39:16 +13:00
fd68767216 pin to a version of zstd that doesn't break dataframe compilation (#7227)
# Description

The `zstd` team released a version that breaks dataframe compilation.
This change pins to `zstd-sys = "=2.0.1+zstd.1.5.2"` in order to prevent
the required `+nightly` build flag.

_(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-11-25 08:43:23 +13:00
JT
93202d4529 Remove And and Or pipeline elements (#7229)
# Description

Since we're not implementing `&&` or `||`, let's remove their pipeline
elements.

# User-Facing Changes

Nothing user facing. These were not yet implemented.

# 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-11-25 07:06:12 +13:00
ed1f0eb231 Make catch block a closure w/ access to error (#7228)
A small follow-up to #7221. This changes the `catch` block from a block
to a closure, so that it can access the error returned from the `try`
block. This helps with a common scenario: "the `try` block failed, and I
want to log why it failed."

### Example


![image](https://user-images.githubusercontent.com/26268125/203841966-f1f8f102-fd73-41e6-83bc-bf69ed436fa8.png)

### Future Work

Nu's closure syntax is a little awkward here; it might be nicer to allow
something like `catch err { print $err }`. We discussed this on Discord
and it will require special parser code similar to what's already done
for `for`.

I'm not feeling confident enough in my parser knowledge to make that
change; I will spend some more time looking at the `for` code but I
doubt I will be able to implement anything in the next few days.
Volunteers welcome.
2022-11-25 07:02:20 +13:00
JT
04612809ab Add try/catch functionality (#7221)
# Description

This adds `try` (with an optional `catch` piece). Much like other
languages, `try` will try to run a block. If the block fails to run
successfully, the optional `catch` block will run if it is available.

# User-Facing Changes

This adds the `try` 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-11-24 17:52:11 +13:00
JT
8cca447e8c A set of fixes for stderr redirect (#7219)
# Description

This is a set of fixes to `err>` to make it work a bit more predictably.

I've also revised the tests, which accidentally tested the wrong thing
for redirection, but should be more correct now.

# 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-11-24 16:58:15 +13:00
651e86a3c0 uniq -i does not convert to lowercase (#7192) (#7209)
# Description
`uniq -i` does not convert output strings to lowercase.

Also, `uniq -i` did not ignore case in strings below the first level of
Tables and Records. Now all strings case are ignored for all children
Values for tables, Records, and List.

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


# Tests + Formatting
About the issue https://github.com/nushell/nushell/issues/7192, the
output will be:
```
〉[AAA BBB CCC] | uniq -i
╭───┬─────╮
│ 0 │ AAA │
│ 1 │ BBB │
│ 2 │ CCC │
╰───┴─────╯
```

About ignoring case for all children string, I expect this to be true:
```
([[origin, people];
    [World, (
        [[name, meal];
            ['Geremias', {plate: 'bitoque', carbs: 100}]
        ]
    )],
    [World, (
        [[name, meal];
            ['Martin', {plate: 'bitoque', carbs: 100}]
        ]
    )],
    [World, (
        [[name, meal];
            ['Geremias', {plate: 'Bitoque', carbs: 100}]
        ]
    )],
] | uniq -i
) == ([[origin, people];
    [World, (
        [[name, meal];
            ['Geremias', {plate: 'bitoque', carbs: 100}]
        ]
    )],
    [World, (
        [[name, meal];
            ['Martin', {plate: 'bitoque', carbs: 100}]
        ]
    )]
])
```
2022-11-23 15:46:20 -08:00
c3c3481ef5 fix color_config crashing on nonstring data (#7215)
This PR closses issue #7155, where now providing a non valid color will
just ignore it and use the default.

Making the same changes as the original issue just starts nushell
without crashing.
2022-11-23 23:32:25 +01:00
0732d8bbba Remove samples/wasm folder (#7214)
# Description

This folder doesn't seem to get used in active testing and the state of
the code seems to be close to an example stub for a general wasm
project.
2022-11-23 10:06:07 -08:00
bdca31cc2d Rename dataframe describe to summary so that the normal describe isn't overloaded (#7176)
This closes #6770.
2022-11-23 17:58:28 +01:00
ed7aea8dd3 Add binstall metadata (#7212)
# Description

To make `cargo binstall nu` works out of the box, `Cargo.toml` needs to
be slightly modified, likely because the URL to the download does not
exactly match.

# User-Facing Changes

None

# Tests + Formatting

Tested by cherry-picking this commit on top of the 0.71.0 tag, running
`cargo package --allow-dirty --no-verify`, then:

```
~
❯ cargo binstall --manifest-path ~/src/github/nushell/nushell/Cargo.toml nu --force
 INFO resolve: Resolving package: 'nu'
 WARN resolve: The package will be downloaded from github.com
 INFO resolve: This will install the following binaries:
 INFO resolve:   - nu (nu -> /home/michel/.cargo/bin/nu-v0.71.0)
 INFO resolve: And create (or update) the following symlinks:
 INFO resolve:   - nu (/home/michel/.cargo/bin/nu -> nu-v0.71.0)
Do you wish to continue? yes/[no]
? yes
 INFO install: Installing binaries...
 INFO Done in 8.538106765s
```

`--force` was needed because I've previously tried this to manually test
the `pkg-url`:

```
cargo binstall \
        --pkg-url="{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }" \
        --pkg-fmt="tgz" nu
```

Fixes #7201.

Signed-off-by: Michel Alexandre Salim <michel@michel-slm.name>

Signed-off-by: Michel Alexandre Salim <michel@michel-slm.name>
2022-11-23 08:46:06 -08:00
e813e44501 Fix fetch/post not erroring on 4xx and 5xx statuses (#7213)
# Description

Closes #6803.

You can look at the code and see this was always supposed to work this
way, but was broken due to 1 line (per file).

# 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-11-23 08:43:12 -08:00
f46c45343a uniq code refactoring (#7188)
# Description

While trying to add a new `uniq-by` command I refactored the `uniq`
command code to understand it and try to reuse. I think this is more
compact and easier to understand.
The part that I think it's a little confusing in this refactor is the
conditions inside `.filters()`, for example: `!flag_show_repeated ||
(value.1 > 1)`. I could use `if (flag_show_repeated) {value.1 > 1} else
{true}` but it is more verbose, what do you think?

PS: Not sure if you like this kind of PR, sorry if not.

# Tests + Formatting

I also added a test where the `uniq` has a table as input.
2022-11-23 11:18:13 +01:00
b12ffb8888 Fix sort-by, path join and size error arrows (#7199)
# Description
BEFORE:
```
〉ls | size
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #22:1:1]
 1 │ ls | size
   ·      ──┬─
   ·        │╰── value originates from here
   ·        ╰── expected: string
   ╰────

〉ls | sort-by SIZE
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #17:1:1]
 1 │ ls | sort-by SIZE
   ·      ───┬───
   ·         │╰── value originates here
   ·         ╰── cannot find column
   ╰────

〉[4kb] | path join 'b'
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #6:1:1]
 1 │ [4kb] | path join 'b'
   · ──┬──
   ·   │╰── value originates from here
   ·   ╰── expected: string or record
   ╰────
```
AFTER:
```
〉ls | size
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #1:1:1]
 1 │ ls | size
   · ─┬   ──┬─
   ·  │     ╰── expected: string
   ·  ╰── value originates from here
   ╰────

〉ls | get 0 | sort-by SIZE
Error: nu:🐚:column_not_found (link)

  × Cannot find column
   ╭─[entry #2:1:1]
 1 │ ls | get 0 | sort-by SIZE
   · ─┬           ───┬───
   ·  │              ╰── cannot find column 'SIZE'
   ·  ╰── value originates here
   ╰────

〉[4kb] | path join 'b'
Error: nu:🐚:pipeline_mismatch (link)

  × Pipeline mismatch.
   ╭─[entry #1:1:1]
 1 │ [4kb] | path join 'b'
   · ──┬──   ────┬────
   ·   │         ╰── expected: string or record
   ·   ╰── value originates from here
   ╰────

```

(Hey, anyone noticed that there's TWO wordings of "value originates from
here" in this codebase………?)

# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-23 19:22:23 +13:00
cf96677c78 Error on negative argument of first (#7186)
Fixes a two's complement underflow/overflow when given a negative arg.

Breaking change as it is throwing an error instead of most likely
returning most of the output.

Same behavior as #7184


# Tests + Formatting

+ 1 failure test

# 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-11-23 17:04:28 +13:00
ce03d8eb12 Error on negative argument to last (#7184)
# Description

- Error on negative argument to `last`
- Add test for negative value in last

Follow-up for #7178

# User-Facing Changes

Breaking change:

even before #7178 `last` returned an empty `list<any>` when given
negative indices.
Now this is an
[error](https://docs.rs/nu-protocol/latest/nu_protocol/enum.ShellError.html#variant.NeedsPositiveValue)

Note:
In #7136 we are considering supporting negative indexing

# Tests + Formatting

+ 1 failure test

# 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-11-23 17:04:04 +13:00
21dedef7f6 remove block input support in merge (#7177)
# Description

Closes: #6937

# User-Facing Changes

N/A

# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-23 17:01:27 +13:00
da7f77867a Fixed json parsing (#7175)
# Description

I noticed that some json values are not parsed at the top level, for
example: `null`, `true`, `false`. Although this is a valid json.
```
> "null" | from json
Error:
  × Error while parsing JSON text
   ╭─[entry #12:1:1]
 1 │ "null" | from json
   ·          ────┬────
   ·              ╰── error parsing JSON text
   ╰────

Error:
  × Error while parsing JSON text
   ╭────
 1 │ null
   ╰────
```

I tried to fix it and it seems to work fine.

# User-Facing Changes

It should give fewer errors.

# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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: sholderbach <sholderbach@users.noreply.github.com>
2022-11-23 17:00:00 +13:00
3415594877 Bump minimatch from 3.0.4 to 3.1.2 in /samples/wasm (#7181)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to
3.1.2.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="699c459443"><code>699c459</code></a>
3.1.2</li>
<li><a
href="2f2b5ff1bb"><code>2f2b5ff</code></a>
fix: trim pattern</li>
<li><a
href="25d7c0d09c"><code>25d7c0d</code></a>
3.1.1</li>
<li><a
href="55dda291df"><code>55dda29</code></a>
fix: treat nocase:true as always having magic</li>
<li><a
href="5e1fb8dd2b"><code>5e1fb8d</code></a>
3.1.0</li>
<li><a
href="f8145c54f3"><code>f8145c5</code></a>
Add 'allowWindowsEscape' option</li>
<li><a
href="570e8b1aef"><code>570e8b1</code></a>
add publishConfig for v3 publishes</li>
<li><a
href="5b7cd3372b"><code>5b7cd33</code></a>
3.0.6</li>
<li><a
href="20b4b56283"><code>20b4b56</code></a>
[fix] revert all breaking syntax changes</li>
<li><a
href="2ff038852e"><code>2ff0388</code></a>
document, expose, and test 'partial:true' option</li>
<li>Additional commits viewable in <a
href="https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=minimatch&package-manager=npm_and_yarn&previous-version=3.0.4&new-version=3.1.2)](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>
2022-11-23 16:57:55 +13:00
0c38729735 Apply clippy fix (#7193)
# Description

rust 1.65.0 has been released for a while, this pr applies lint
suggestions from rust 1.65.0.

# User-Facing Changes

N/A

# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-23 16:57:27 +13:00
a0b3a48e8b Fix mv error message issues (arrows, Windows paths) (#7197)
# Description

BEFORE (notice Windows paths look wrong):
```
〉mv 8 9
Error:
  × Destination file already exists
   ╭─[entry #22:1:1]
 1 │ mv 8 9
   ·      ┬
   ·      ╰── you can use -f, --force to force overwriting the destination
   ╰────

〉mv d1 tmp
Error:
  × Can't move "C:\\Users\\Leon\\TODO\\d1" to "C:\\Users\\Leon\\TODO\\tmp\\d1"
   ╭─[entry #19:1:1]
 1 │ mv d1 tmp
   ·       ─┬─
   ·        ╰── Directory not empty
   ╰────

```
AFTER (full paths are now included in the arrows' messages to make lines
like `mv $foo` entirely unambiguous):
```
〉mv 8 9
Error:
  × Destination file already exists
   ╭─[entry #4:1:1]
 1 │ mv 8 9
   ·      ┬
   ·      ╰── Destination file 'C:\Users\Leon\TODO\tmp\9' already exists
   ╰────
  help: you can use -f, --force to force overwriting the destination

〉mv d1 tmp
Error:
  × Can't move 'C:\Users\Leon\TODO\d1' to 'C:\Users\Leon\TODO\tmp\d1'
   ╭─[entry #3:1:1]
 1 │ mv d1 tmp
   ·       ─┬─
   ·        ╰── Directory 'C:\Users\Leon\TODO\tmp' is not empty
   ╰────

```
# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-23 16:55:13 +13:00
b662c2eb96 Make external command substitution works friendly(like fish shell, trailing ending newlines) (#7156)
# Description

As title, when execute external sub command, auto-trimming end
new-lines, like how fish shell does.

And if the command is executed directly like: `cat tmp`, the result
won't change.

Fixes: #6816
Fixes: #3980


Note that although nushell works correctly by directly replace output of
external command to variable(or other places like string interpolation),
it's not friendly to user, and users almost want to use `str trim` to
trim trailing newline, I think that's why fish shell do this
automatically.

If the pr is ok, as a result, no more `str trim -r` is required when
user is writing scripts which using external commands.

# User-Facing Changes
Before:
<img width="523" alt="img"
src="https://user-images.githubusercontent.com/22256154/202468810-86b04dbb-c147-459a-96a5-e0095eeaab3d.png">

After:
<img width="505" alt="img"
src="https://user-images.githubusercontent.com/22256154/202468599-7b537488-3d6b-458e-9d75-d85780826db0.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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-23 16:51:57 +13:00
JT
8cda641350 Don't redirect stdout when only redirecting stderr (#7206)
# Description

Spotted by @WindSoilder - don't redirect stdout if the user requests
`err>`.

# 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-11-23 15:18:34 +13:00
efdfeac55e Feature cleanup (#7182)
Following up on #7180 with some feature cleanup:

- Move the `database` feature from `plugin` to `default`
- Rename the `database` feature to `sqlite`
- Remove `--features=extra` from a lot of scripts etc. 
- No need to specify this, the `extra` feature is now the same as the
default feature set
- Remove the now-redundant 2nd Ubuntu test run
2022-11-22 16:58:11 -08:00
e0577e15f2 Restore original do -i behavior and add flags to break down shell vs program errors (#7122)
Closes https://github.com/nushell/nushell/issues/7076, fixes
https://github.com/nushell/nushell/issues/6956

cc @WindSoilder @fdncred

Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
2022-11-22 15:58:36 -06:00
bb0b0870ea Change all --insensitive flags to --ignore-case (#7198)
# Description

Support for this breaking change was raised in #7191. This affects
`sort`, `sort-by`, `str contains` and `find`. `--ignore-case` is used by
a few POSIX programs such as `less` and `grep`, as well as a few other
popular utils like `tree` and `wget`. Since long names aren't especially
popular (existing primarily for self-documentation purposes), I consider
this on the shallow end of the compat-break scale.

Note that the `-i` short flag is not affected.
 
# 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 --features=extra -- -D warnings -D
clippy::unwrap_used -A clippy::needless_collect` to check that you're
using the standard code style
- `cargo test --workspace --features=extra` 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-11-22 15:38:30 -06:00
JT
74a73f9838 Stdout/Stderr redirection (#7185)
This adds new pipeline connectors called out> and err> which redirect either stdout or stderr to a file. You can also use out+err> (or err+out>) to redirect both streams into a file.
2022-11-23 07:26:13 +13:00
c9f9078726 Fix while ctrlc behavior (#7195)
Currently while didn't respect ctrl-c, and thus non-terminating loops
couldn't be interrupted. This patch fixes this.
2022-11-22 15:47:12 +01:00
eb875ea949 Fix glob error arrows (#7194) 2022-11-22 14:23:01 +01:00
JT
b4a0e4c0dc Move dataframe out of extra (#7180) 2022-11-22 06:24:25 +13:00
88a0705df1 Fix last memory use (#7178)
Currently `last n` memory use is O(input), while it should be O(n). This
patch replaces code collecting all of last's input into a Vec<_> with
collecting into a bounded VecDeque<_>. UI/UX remain are unchanged.
2022-11-22 06:19:31 +13:00
7bcd96fc65 Remove erroneous test (#7179) 2022-11-21 17:04:36 +01:00
833825ae9a Allow iteration blocks to have an optional extra index parameter (alternative to -n flags) (#6994)
Alters `all`, `any`, `each while`, `each`, `insert`, `par-each`, `reduce`, `update`, `upsert` and `where`,
so that their blocks take an optional parameter containing the index.
2022-11-21 14:35:11 +01:00
899383c30c feat: Use Raw Text to save if pipeline data is ExternalStream (#7082)
if not value type or Value as String in nushell, save will use raw type
2022-11-20 19:32:15 -06:00
d9d6cea5a9 Make json require string and pass around metadata (#7010)
* Make json require string and pass around metadata

The json deserializer was accepting any inputs by coercing non-strings
into strings. As an example, if the input was `[1, 2]` the coercion
would turn into `[12]` and deserialize as a list containing number
twelve instead of a list of two numbers, one and two. This could lead
to silent data corruption.

Aside from that pipeline metadata wasn't passed aroud.

This commit fixes the type issue by adding a strict conversion
function that errors if the input type is not a string or external
stream. It then uses this function instead of the original
`collect_string()`. In addition, this function returns the pipeline
metadata so it can be passed along.

* Make other formats require string

The problem with json coercing non-string types to string was present in
all other text formats. This reuses the `collect_string_strict` function
to fix them.

* `IntoPipelineData` cleanup

The method `into_pipeline_data_with_metadata` can now be conveniently
used.
2022-11-20 17:06:09 -08:00
d01ccd5a54 add signature information when get help on one command (#7079)
* add signature information when help on one command

* tell user that one command support operated on cell paths

Also, make type output to be more friendly, like `record<>` should just be `record`

And the same to `table<>`, which should be `table`

* simplify code

* don't show signatures for parser keyword

* update comment

* output arg syntax shape as type, so it's the same as describe command

* fix string when no positional args

* update signature body

* update

* add help signature test

* fix arg output format for composed data type like list or record

* fix clippy

* add comment
2022-11-20 07:22:42 -06:00
JT
a896892ac9 Add auto-expanding table view to default config (#7172) 2022-11-20 20:52:38 +13:00
d89d1894d0 Add missing legacy support for config.table_index_mode. (#7170) 2022-11-20 00:46:13 -05:00
587536ddcc Edit rm help messages (#7165)
* Edit `rm` help messages

* Restore accidental missing changes
2022-11-19 10:33:30 -08:00
ced5e1065f new command url parse (#6854) and url subcommands tests (#7124)
*code refactor from PR tips & clippy fixes

*added username, password, and fragment

*commands `url host`, `url scheme`, `url query`, and `url path` removed

*tests refactoring - avoid formatted output
2022-11-19 10:14:29 -08:00
7479173811 Grouped config commands better (closes #6911) (#6983)
* Grouped config commands better

* Tweaked test slightly

* Fix merge conflict(?)

* Remove recently-added test case

* Revert rm.always_trash default

* Untweak rm help messages

* Formatting

* Remove example

* Add deprecation warning

* Remove deprecation timeline

Not sure we want to commit to a specific timeline just yet

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>
2022-11-19 09:52:09 -08:00
4b83a2d27a Improve CantFindColumn and ColumnAlreadyExists errors (#7164)
* Improve CantFindColumn and ColumnAlreadyExists errors

* Update tests
2022-11-19 09:35:55 -08:00
41f72b1236 Friendly error message for missing plugin executable (#7163) 2022-11-19 12:12:18 +01:00
c98a6705e6 Fix needs_quotes() in to nuon (closes #6989) (#7056)
* to nuon: fix needs_quotes()

Also, null now serialises as null instead of $nothing.

* Clippy

* Add missing quote

* Remove two unnecessary characters

* Add short datetime tests

* Make regex simplificatified

* Alphabetise 'use' statements

* Improve perf by putting case-insensitive cases in regex

* Fix 1 test
2022-11-19 12:09:39 +01:00
JT
6454bf69aa Parser refactoring for improving pipelines (#7162)
* Remove lite_parse file

* Add LiteElement to represent different pipeline elem types

* Add PipelineElement to Pipelines

* Remove lite_parse specific tests
2022-11-19 10:46:48 +13:00
bd30ea723e Consistent wrap (#7159)
* Consistent wrap

* Signature fix
2022-11-19 10:39:40 +13:00
2dd4cb9f7d Improve "Cannot convert argument to string" msg (#7161) 2022-11-18 21:33:01 +01:00
1784b4bf50 fix #7145 (#7148)
* fix #7145

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Improve fix

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-11-17 07:51:04 -06:00
8e4b85e29b update default_config.nu with display_output (#7146)
Added a default display_output hook to help people know about this feature.
2022-11-16 19:08:09 -05:00
7098e56ccf Remove build-string command (#7144) 2022-11-16 09:37:52 -08:00
02ad491dea [WIP] table: Change Record view in expand-mode (#6885)
* table: Change Record view in expand-mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix width issue

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Remove debug println!

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Update logic

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Improve the logic via a wrapping

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* `table -e` spread table to the whole width

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* fix CI

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fixing tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix coloring issues

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Don't expand when can

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Change the logic

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix cargo fmt

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-11-16 08:03:56 -06:00
708fee535c fix: ls not show pattern error (#7143)
Log: fix pattern error show in ls command
2022-11-16 09:15:19 +01:00
7636cc7fe4 avoid test failure caused by locale thousand separator (#7142)
Co-authored-by: Ricardo Monteiro <ricardo.monteiro@getmanta.com>
2022-11-16 11:15:15 +13:00
f856e64fb3 to html --list now returns a table (#7080)
* `to html --list` now returns a table

* Re-add screenshots link
2022-11-15 11:12:56 -06:00
a783a084d4 Update PR template; Add some tests instructions (#7135) 2022-11-15 00:43:15 +01:00
81b12d02ec Change parser cwd when running a file (#7134)
* Change parser cwd when running a file

* Add test

* Add missing file
2022-11-15 00:05:27 +01:00
336df6c65e Return errors on unexpected inputs to take and first (#7123)
* Fix `take` behaviour for unexpected input types

* Fix `first` behaviour for unexpected input types

* Fix copy paste mistake
2022-11-13 15:15:27 -08:00
35f9299fc6 fix ansi --osc parameter adding extra semi-colon (#7113) 2022-11-12 23:27:58 +01:00
649c8319e6 Add input-output types to $nu.scope.commands (#7105)
* Add input and output types to $nu.scope.commands

This commit changes the schema: instead of

command.signature: table

we now have

command.signatures: list<table>

with one signature for every input-output type pair.

* Represent signatures as a map from input_type to signature

* Sort signature entries

* Drop command name from signature tables

* Don't use "rest" as name of rest parameter; use empty string instead

* Bug fix: was creating records with repeated keys

E.g.
$nu.scope.commands | where name == 'hash sha256' | get signatures.0 | table -e
$nu.scope.commands | where name == 'transpose' | get signatures.0 | table -e
2022-11-12 14:26:20 -08:00
99cf5871aa Try --locked install in virtualenv CI tests (#7117)
Currently we see CI failures due to a `chrono` upgrade with
deprecations.
Also on every new reedline release we also suffer from regular compile
problems.
2022-11-12 18:53:57 +01:00
da04e9d801 Remove accidental strip-ansi-escapes after #6938 (#7115) 2022-11-12 17:12:40 +01:00
2db2d98ef0 Add missing dependency to Cargo.lock (#7114) 2022-11-12 16:13:42 +01:00
817eacccd8 removes unused features. (#6938)
* removes unused features.

* Adds back multithreading feature to sysinfo.

* Adds back alloc for percent-encoding

* Adds updated lock file.

* Missed one sysinfo.

* `indexmap` just defaults

* Revert `miette``default-features=false`

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-11-12 18:44:56 +13:00
ce6d3c6eb2 Refactor creation of $nu.scope in eval.rs (#7104)
The function was ~400 lines long and hence very hard to work with.
2022-11-11 23:20:28 +01:00
ef32e1ce1a reset stack size to 10mb vs 2gb (#7103) 2022-11-11 15:22:26 -06:00
JT
c1105e945e Add additional assignment operators (#7102) 2022-11-12 07:50:43 +13:00
JT
69b089845c Add support for while loops (#7101) 2022-11-12 07:21:45 +13:00
75556f6c5f fix(#7097): let-env should not be able to set PWD (#7100) 2022-11-12 05:45:51 +13:00
b650d1ef79 Remove --separator from seq date (#7096) 2022-11-11 20:16:44 +13:00
JT
099b571e8f All field assignment into the env variable (#7099) 2022-11-11 20:16:07 +13:00
cb926f7b49 Better error message when rm can't find files (#7098) 2022-11-10 23:05:09 -08:00
JT
13515c5eb0 Limited mutable variables (#7089)
This adds support for (limited) mutable variables. Mutable variables are created with mut much the same way immutable variables are made with let.

Mutable variables allow mutation via the assignment operator (=).

❯ mut x = 100
❯ $x = 200
❯ print $x
200

Mutable variables are limited in that they're only tended to be used in the local code block. Trying to capture a local variable will result in an error:

❯ mut x = 123; {|| $x }
Error: nu::parser::expected_keyword (link)

  × Capture of mutable variable.

The intent of this limitation is to reduce some of the issues with mutable variables in general: namely they make code that's harder to reason about. By reducing the scope that a mutable variable can be used it, we can help create local reasoning about them.

Mutation can occur with fields as well, as in this case:

❯ mut y = {abc: 123}
❯ $y.abc = 456
❯ $y

On a historical note: mutable variables are something that we resisted for quite a long time, leaning as much as we could on the functional style of pipelines and dataflow. That said, we've watched folks struggle to work with reduce as an approximation for patterns that would be trivial to express with local mutation. With that in mind, we're leaning towards the happy path.
2022-11-11 19:51:08 +13:00
JT
58d960d914 Update README re: typechecking (#7094) 2022-11-11 15:16:10 +13:00
JT
4a83bb6c93 Fix environment conversions (#7092) 2022-11-11 13:13:07 +13:00
3e56e81d06 fix plugin detection in help commands (#7088) 2022-11-10 16:12:09 -06:00
312e9bf5d6 fix overflow on negative bytes (#7070) 2022-11-10 22:33:15 +01:00
JT
18d7e64660 Convert 'for' to a statement (#7086) 2022-11-11 09:05:34 +13:00
f1118020a1 add commented out mold linker usage (#7081) 2022-11-10 08:41:06 -06:00
JT
63433f1bc8 Split blocks and closures (#7075)
* Split closures and blocks

* Tests mostly working

* finish last fixes, passes all tests

* fmt
2022-11-10 21:21:49 +13:00
921a66554e Replace all instances of 'column path' in help messages with 'cell path' (#7063)
* Rewrite all 'column path' instances to 'cell path'

* Minor tweak
2022-11-09 21:49:11 -08:00
bb0d08a721 Fix command_type classification (#7074)
- Custom commands are true for builtin and custom
- Add classification as external command
- Specify wildcard in keyword: keyword is true for builtin and keyword
2022-11-09 19:09:33 -08:00
fe14e52e77 Collapse some help commands columns into a single column (#7052) 2022-11-09 17:44:32 -08:00
24d72ca43c Simplify seq char (#7054)
* Simplify `seq char`

* Fix input/output tests
2022-11-09 17:06:47 -08:00
457f7889df use path.try_exist() to fix silent errors (#7069) 2022-11-09 16:54:43 -08:00
7b0c0692dc Type validation for headers command (#6918) (#7047)
cargo clippy lints

tests

format

Co-authored-by: Ricardo Monteiro <ricardo.monteiro@getmanta.com>
2022-11-09 16:43:24 -08:00
c4cb3a77cb command open returns error when does not have parameters (#7048) (#7058)
test

Co-authored-by: Ricardo Monteiro <ricardo.monteiro@getmanta.com>
2022-11-10 00:25:32 +01:00
aed8d3800b Fix CI failures after PR merge conflicts (#7072) 2022-11-10 00:24:57 +01:00
53a9264b67 return value::int instead of value::record in history session (#7049)
* return value::int instead of value::record

* clippy
2022-11-10 11:20:52 +13:00
e18fb13616 Make seq output type consistent (#7045) 2022-11-10 11:19:02 +13:00
2201bd9b09 Require column name(s) in sort-by (#7041) 2022-11-10 11:16:51 +13:00
da8f6c5682 Require input for date format (#7043) 2022-11-10 11:16:14 +13:00
14d7ba5cc9 Remove --predicate flag from find (#7042) 2022-11-10 11:15:17 +13:00
5ee096232c Remove sqlparser SQLite commands (#7040) 2022-11-10 11:14:48 +13:00
c259ef41bd update polar to 0.25 (#6988) 2022-11-10 11:07:38 +13:00
2c238aea6a Fixed $in in where blocks (#6976) 2022-11-10 11:05:15 +13:00
c600c1ebe7 Fix ignore-errors for select (#6896)
* Fix ignore-errors for select

* fix Value::List match

* fix invalid rows

* add tests

* fix ListStream match

* add one more test for ListStream

* add more tests

* tweak words
2022-11-10 10:57:44 +13:00
df94052180 Declare input and output types of commands (#6796)
* Add failing test that list of ints and floats is List<Number>

* Start defining subtype relation

* Make it possible to declare input and output types for commands

- Enforce them in tests

* Declare input and output types of commands

* Add formatted signatures to `help commands` table

* Revert SyntaxShape::Table -> Type::Table change

* Revert unnecessary derive(Hash) on SyntaxShape

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-11-10 10:55:05 +13:00
JT
f878276de7 Turn off foreground processes on macOS (#7068)
* Turn off foreground processes on macOS

* fmt
2022-11-10 07:39:09 +13:00
cd89304706 Add help warnings for path exists and path type regarding usage (#7062)
* Add help warnings to `path exists` and `path type`

* Correction
2022-11-09 13:41:55 +01:00
2b9f258126 bump to dev release 0.71.1 (#7064) 2022-11-09 13:18:34 +01:00
85587c0c2a make take behave like first (#6893)
* fix take_1 behavior

* fix test case

* simplify code

* reverse back for first command

* fix example

* make arg required

* add test

* fix test message
2022-11-09 11:32:16 +01:00
JT
6cc4ef6c70 bump to 0.71, use 1.63 toolchain (#7061) 2022-11-09 06:54:00 +13:00
JT
517173bb8c Update rust-toolchain.toml 2022-11-09 06:06:33 +13:00
JT
e415be6c0e Revert back to 1.63 because of macOS build issues 2022-11-09 06:05:53 +13:00
59332562bb Update contributing guide and PR template (#7008)
* Update contributing guide

* Refactor pull request template

* Reword PR template a bit

* Update CONTRIBUTING.md

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>

* Reformulate

* Make "Before Submitting" a top-level header

* Add review requirement to After Submitting

* Reformulate

* Update .github/pull_request_template.md

Co-authored-by: Dan Davison <dandavison7@gmail.com>

* Reformulate contributing guide

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>
Co-authored-by: Dan Davison <dandavison7@gmail.com>
2022-11-07 22:57:29 +01:00
3d8d7787de Pin reedline to 0.14.0 release (#7050)
See release notes:
https://github.com/nushell/reedline/releases/tag/v0.14.0
2022-11-07 21:31:15 +01:00
611fe41788 Make the example names unique across workspace (#7046)
Avoids name collision in the target directory when running test
compilation.

cc @rgwood
2022-11-07 09:00:21 +01:00
a6118eed8d Revert "Fix for escaping backslashes in interpolated strings (fixes #6737) (#7020)" (#7038)
This reverts commit d4798d6ee1.
2022-11-06 16:17:00 -06:00
d4798d6ee1 Fix for escaping backslashes in interpolated strings (fixes #6737) (#7020)
Co-authored-by: Gavin Foley <gavinmfoley@gmail.com>
2022-11-07 08:57:28 +13:00
5ea245badf Make example binaries proper cargo examples (#7019)
Should not be built by default with `cargo build`
Instead are compiled with `cargo test` to avoid bitrot
Run with `cargo run -p ... --example ...`
2022-11-06 11:39:27 -08:00
5ee7847035 Add accidentally missing tests of some command examples (#7035)
* Add missing tests of examples

* Fix broken tests due to externals not being in working set

* Comment out `where` test due to bug
2022-11-06 09:53:25 -08:00
8a812cf03c added some search-terms to the platform category (#7021)
* added some search-terms to the `platform` category

* removed the redundant `sleep` search-term

* removed the redundant `gradients` search-term
2022-11-06 18:11:04 +01:00
9a1cedfd08 Add expected result to test (#7031) 2022-11-06 18:09:56 +01:00
766d1ef374 Update reedline (#7023)
Fixes #6991
2022-11-06 09:37:36 +01:00
beec658872 New "display_output" hook. (#6915)
* New "display_output" hook.

* Fix unrelated "clippy" complaint in nu-tables crate.

* Fix code-formattng and style issues in "display_output" hook

* Enhance eval_hook to return PipelineData.

This allows a hook (including display_output) to return a value.

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-11-06 13:46:40 +13:00
b90d701f89 Rename column name from command to name for consistency (#7007) 2022-11-05 10:46:30 +13:00
be5d71ea47 Run a round of clippy --fix to fix a ton of lints (#7006)
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>

Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
2022-11-04 15:11:17 -05:00
f1bde69131 Fix panic when encountering ENOTTY. (#7001) 2022-11-05 09:06:04 +13:00
36ae384fb3 Improve do command docs (#6975) 2022-11-05 07:50:56 +13:00
2c4048eb43 Refactor ansi stripping into nu-utils functions (#6966)
Allows use of slightly optimized variants that check if they have to use
the heavier vte parser. Tries to avoid unnnecessary allocations. Initial
performance characteristics proven out in #4378.

Also reduces boilerplate with right-ward drift.
2022-11-05 07:49:45 +13:00
b9195c2668 fix: fixcd (#6799)
* fix: fixcd

try to fix

Log: try to fix the bug with can enter a permisson error fold

* change wording

* fat

* fmt

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-11-05 07:38:39 +13:00
bb968304da bump rust-toolchain to 1.64 (#7005)
* bump rust-toolchain to 1.64

* 1.64 clippy
2022-11-04 10:27:23 -05:00
ca9bf19041 highlight term on PipelineData::Value() (#6997) 2022-11-04 08:42:16 -05:00
JT
ecfee4c542 Remove unnecessary clone in par-each (#6995)
* Remove unnecessary clone in par-each

* clippy
2022-11-04 18:07:28 +13:00
acb34561eb category tweak (#6982) 2022-11-02 12:17:17 -05:00
43aec8cdbe Fix $in in blocks given to any and all (#6951)
* Fix $in in blocks given to `any` and `all` (closes #6917)

* Fix help message typos

* Fix tests ($in doesn't work in examples?!)

* Fix formatting
2022-11-01 11:36:54 -07:00
e46d610f77 Refactor: finish refactor on commands which take optional cell paths. (#6961)
* refactor on conversions module

* finish refactor on strings command

* simplify code

* rename from ArgumentsCp to CellPathOnlyArgs

* fmt code

* refactor on hash relative commands
2022-11-01 12:40:11 +01:00
1d95861a09 Remove inadvertent dep on original ansi_term (#6965)
Is default feature in `lscolors`. Not needed for function as we use
crossterm backend in this case.
2022-11-01 12:27:20 +13:00
f48de73236 change str distance to output value::int (#6963)
* change str distance to output value::int

* update the test
2022-10-31 10:28:04 -05:00
0b4daa66b0 tweak upsert help text (#6962)
* tweak upsert help text

* more tweaks
2022-10-31 08:18:11 -05:00
412952182f Update reedline to latest dev (#6953)
- Reedline now properly supports the `sqlite-dynlib` feature flag
- Reorganized examples allow removal of `gethostname` as reedline
dev-dependency
2022-10-30 22:08:07 +01:00
014d36b17a Use nt-api 4 on Windows (#6949)
* Bump nushell-sytem dep to ntapi 0.4

0.3.7 trigger a warning about code being incompatible
with future rust versions. This is resolved in 0.4
https://github.com/MSxDOS/ntapi/issues/11

* Upgrade Cargo.lock for ntapi 0.4
2022-10-30 14:29:41 +01:00
f44f3a8af1 Fix double cache read in CI (#6948) 2022-10-30 08:24:10 +01:00
457514590d Refactor: introduce general operate commands to reduce duplicate code (#6879)
* make format filesize more flexible

* make code simpler

* finish refactor on bytes commands

* finish refactor on str commands

* fimplify code

* rename from column_paths to cell_paths
2022-10-29 16:29:46 -05:00
843d8c2242 Make default for mv safer, require -f to overwrite (#6904)
* fix:  "saner" default for mv

fixes #6747

As highlighted in the issue, the default behavior of nu currently
is to overwrite the destination file without notice.
This is not a "standard" expectation users that want this behavior
can create a dedicated alias.

* fix: 📝 edit the comment

* fix:  updated the tests

* fix: 🚧 use --force for case test
2022-10-29 22:16:29 +02:00
ce4ae00a6f Remove unused dependencies (#6945)
* Remove unused dependencies

Inspired by #6938 ran `cargo +nightly udeps --features extra`.
Removes 2 crates and should remove an unnecessary intra-workspace
dependency which might open up further opportunities for compilation.

* Make windows-only dependency conditional in toml

`omnipath` is only used on Windows and already behind a `#[cfg]` block
in the code. Made the dependency in `Cargo.toml` conditional as well.

* Make `nu-pretty-hex` example a proper example

This allows making `rand` a dev-dependency in this crate.
2022-10-29 21:19:12 +02:00
4f7f6a2932 Friendly error message for access beyond end (#6944)
Adds `ShellError::AccessEmptyContent`
2022-10-29 19:47:50 +02:00
7039602e4d Move nu-test-support into dev deps on nu-command (#6940)
This reduces the number of dependencies to build for `cargo build` or
`cargo run` by around 19.

Will not speed up CI as we need to `cargo test` but should help for
`cargo install` or users just building from source.
Time saved on my machine ~0.8 secs so likely unnoticable in noise.

Larger future goal is reducing longer dependency chains to allow more
parallel compilation.
2022-10-29 19:39:27 +02:00
acb7aff6fb Fix feature flag for open test (#6935) 2022-10-28 20:25:19 -07:00
8940ee6c3f enable ability to upsert into a list like update (#6932) 2022-10-28 18:13:14 -05:00
3b26b4355e Fix bug with alias handling when searching for matching brackets (#6931)
* [4325] - fix slicing

* [4325] - fix style
2022-10-28 16:21:24 -05:00
b56e603c58 Update PR template to mention user-facing changes (#6923)
* Update PR template to mention user-facing changes

* remove checkboxes
2022-10-28 09:14:08 -07:00
66c2a36123 table: Show truncated record differently (#6884)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-28 14:00:10 +02:00
8838815737 Update nix crate to 0.25 and narrow features (#6924)
Avoids compiling the crate twice due to incompatible versions from
dependencies. This avoids binary bloat before linking as well.
Narrow our feature selection to the used modules/functions to save
compile time. On my machine reduces `nix` crate compile time from 
around 9 secs to 0.9 secs.
2022-10-28 01:07:13 +02:00
834522d002 Fix each while behavior when printing. (#6897)
Fixes #6895

Warning: `Iterator::map_while` does not return a `FusedIterator`!
Depending on the consuming adaptor or code (e.g. for loop) the iteration
may be stopped but this is not guaranteed.

Adds a test example but Examples handle iterators like
`FusedIterator` and thus don't catch the regression

Cleanup the message on another test
2022-10-27 23:45:45 +02:00
f281cd5aa3 Update merge to also take single records (#6919) 2022-10-27 09:00:26 -07:00
5add5cbd12 Further edits to help messages (#6913) 2022-10-26 09:36:42 -07:00
902aad6016 fix description of build-string's second example (#6912) 2022-10-26 09:36:31 -05:00
13f87857cf docs: 📝 add "map" to each's search terms (#6903) 2022-10-25 21:24:27 +02:00
e0cc2c9112 Make get 1 error message better (#6892) 2022-10-24 18:22:57 -07:00
92ab8b831b Reduce required dependencies for diagnostics (#6648)
Disable backtrace on miette
- gimli crate requires several seconds
Disable diagnostics on wax
- depends on an outdated miette version

Builds fine, no observable loss in diagnostics quality of life

Removes 10 crates that have to be compiled.
2022-10-24 21:42:32 +02:00
6a7a60429f Remove unnecessary #[allow(...)] annotations (#6870)
* Remove unnecessary `#[allow]` annots

Reduce the number of lint exceptions that are not necessary with the
current state of the code (or more recent toolchain)

* Remove dead code from `FileStructure` in nu-command

* Replace `allow(unused)` with relevant feature switch

* Deal with `needless_collect` with annotations

* Change hack for needless_collect in `from json`

This change obviates the need for `allow(needless_collect)`
Removes a pessimistic allocation for empty strings, but increases
allocation size to `Value`

Probably not really worth it.

* Revert "Deal with `needless_collect` with annotations"

This reverts commit 05aca98445.

The previous state seems to better from a performance perspective as a
`Vec<String>` is lighter weight than `Vec<Value>`
2022-10-24 20:12:16 +02:00
79fd7d54b2 Wrap open parse errors from from commands (#6877)
* Wrap `open` parse errors from `from` commands

Minimal fix for #6843

This propagates the underlying errors from the called `from` commands
and adds a top-level error with the full path and the understood file
extension and resulting called command.

* Repoint inner span for `from ...` to `open`

* Add actionable message: refer to help or use --raw
2022-10-24 20:09:19 +02:00
ebca840d91 Add support to render right prompt on last line of the prompt (#6781)
* Add support to render right prompt on last line of the prompt

* reset reedline to main branch

* update reedline to fix right prompt to be rendered correctly

* reset reedline to main branch again
2022-10-23 16:18:26 +02:00
17b2bcc125 Support range in str substring (#6867) 2022-10-23 11:42:17 +02:00
24a98f8999 Mildly edited a small handful of help messages (#6868)
* Edited a handful of help messages

* Remove line break as instructed by clippy
2022-10-23 02:02:52 -04:00
e49b359848 Bumps Windows 0.37 -> 0.42. (#6865) 2022-10-22 17:59:44 -07:00
c9fb381d69 feat: coredump (#6791)
fix issue in https://github.com/nushell/nushell/issues/5903
return Error to pipeline_data, if match error, then return error
directly
2022-10-22 20:24:58 +02:00
8224ec49bc Highlight matching brackets / parentheses (#6655)
* [4325] - wip

* [4325] - hightlight only matched symbol

* [4325] - cleanup

* [4325] - match bracket while typing

* [4325] - fix clippy

* [4325] - add bracket highlight configuration

* [4325] - fix working with non-ascii
2022-10-22 11:55:45 -05:00
fe7e87ee02 Fixes for ps on Linux (#6858)
* Fix ps returning nothing when process dies

* Fix ps CPU calcs on Linux, remove unused thread code
2022-10-22 11:54:46 -05:00
a3dce8ff19 table -e Fix stackoverflow (cause endless empty list) (#6847)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-22 11:52:32 -05:00
89f3cbf318 Try not to use verbatim paths for UNC shares (#6824) 2022-10-22 11:51:52 -05:00
3f555a6836 add more helpful error for calling a decl that exists in a module (#6752)
* add more helpful error for calling a decl that exists in a module

* accord to suggestions

* make error more helpful
2022-10-22 11:41:31 -05:00
4fdf5c663c Prepend directory to the binary tarball (#6701)
* Prepend directory to the binary tarball

According to https://www.gnu.org/software/tar/manual/html_section/transform.html, use
`--transform` parameter to prepend directory to each file name.

Closes #6676

* Don't depend on GNU tar
2022-10-22 11:39:11 -05:00
1ec41a0ab4 Expose reedline EditCommand::Complete command (#6863)
This should have been done in 5eee33c7e4
2022-10-22 11:32:07 -05:00
ab0a6b6ca6 path: fix error message (#6860)
Closes #6819
2022-10-22 06:42:46 -05:00
e3bf6fdfc0 Print command help in base from+to commands (#6856) 2022-10-21 10:08:57 -07:00
46c0d29c08 table/ Fix paging indexing (#6850)
* table/ Fix paging indexing

close #6840

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add test for pagging with row_overlapping

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-21 18:02:25 +02:00
76ccd5668a Remove perf flag to streamline logging configuration (#6834) 2022-10-21 10:20:21 -05:00
c6436eb32f rm: don't update target_exists every time in the loop (#6837) 2022-10-21 07:42:29 -07:00
b2c29117d9 table -e align key to 2nd line (#6842)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-21 06:29:55 -05:00
ffb1dfb012 Update ci workflow actions, fix #6713 (#6841)
* Update ci workflow actions, fix #6713

* Upgrade actions/setup-python to v4
2022-10-21 15:25:02 +08:00
88c6fa9933 Update reedline to fix history search filtering (#6835)
https://github.com/nushell/nushell/pull/6802#issuecomment-1285826499
2022-10-21 09:04:54 +02:00
60df45a390 Add missing shape_directory to default_config.nu (#6836)
Closes #6832
2022-10-20 21:25:09 -07:00
10aa86272b Allow captured stderr saving to file (#6793)
* support redirect stderr to file

* fix test

* fix test

* fix test
2022-10-20 07:56:44 -05:00
d37e6ba3b5 nu-table: Check perf improvements (#6710)
* nu-table: Checkout to test vte parsing

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Bump tabled version

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-20 07:52:15 -05:00
5eee33c7e4 Tab inline completion (#6802)
* Make Tab insert (partial) completion instead of select next menu item

* Use reedline feature branch

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-10-20 23:39:48 +13:00
5e748ae8fc make ++ append lists (#6766)
* make `++` append lists

* fmt

* fix for database
2022-10-20 23:28:18 +13:00
50e53e788a Simplify and reduce allocations in pipeline data loop (#6790) 2022-10-20 23:22:07 +13:00
7336e1df1a rm: fix error span when targets doesn't exists (#6815)
Closes #6810
2022-10-20 10:00:25 +02:00
a724a8fe7d bump to dev version 0.70.1 (#6814) 2022-10-20 18:04:10 +13:00
JT
c731a4e275 Update Cargo.toml 2022-10-19 11:44:04 +13:00
JT
9ef65dcd69 Bump to 0.70 (#6800) 2022-10-19 07:13:36 +13:00
JT
f99c002426 Fix let-env in banner (#6795) 2022-10-18 22:42:00 +13:00
f0420c5a6c Pin reedline to the 0.13.0 release (#6789)
See the release notes:

https://github.com/nushell/reedline/releases/tag/v0.13.0
2022-10-17 23:45:28 +02:00
46eec5e3a2 Tolerate more tty acquisition failures in non-interactive mode, fixes #6719 (#6779) 2022-10-17 21:08:25 +02:00
378248341e Update README.md (#6782)
Fixed a very small inconsistency.
2022-10-17 06:23:11 -05:00
803f9d4daf Upgrade reedline to latest dev version (#6778)
* Reorder conditional deps for readability

* Pull reedline from the most recent main branch

* Map 'Submit' and 'SubmitOrNewline' events
  Introduced by nushell/reedline#490
2022-10-16 23:51:15 +02:00
ce809881eb Rename query dfr -> query df (#6777) 2022-10-16 21:04:48 +02:00
1a99893e2d Add documentation requirement to PR template (#6749) 2022-10-16 18:19:54 +03:00
ec8e57cde9 Add search terms to roll commands (#6761) 2022-10-16 13:04:22 +02:00
a498234f1d fix stdout hangged on (#6715) 2022-10-15 14:29:29 -05:00
de77cb0cc4 add filesize_metric comment (#6760) 2022-10-15 14:26:18 -05:00
7d5d53cf85 Add search terms to arg dataframe commands (#6724)
* added search terms for arg prefixed dataframe commands

* remove search terms that already produce results
2022-10-15 12:49:09 -05:00
1572808adb Filter out empty glob patterns to "glob" command (#6707)
* Filter out empty glob patterns

An empty argument to the "glob" command will now produce an empty result.
Working towards nushell/nushell#6653.

* Run `cargo fmt --all`

Just autoformatted the repo so that CI passes and we have a consistent code
format across modules.

* Treat empty glob argument as error

The glob command will now report an empty string argument as an error instead
of silently ignoring it.

See https://github.com/nushell/nushell/pull/6707#discussion_r993345013.

* Add tests for glob command

Two small tests for the glob command, one to check that the empty string errors
it, and another to sanity check the '*' glob, have been added.

* Rename glob sanity check star test

Co-authored-by: Kyle Anderson <kyle.anderson@uwaterloo.ca>
2022-10-15 18:00:38 +02:00
9d77e3fc7c Improve erroring of config nu and config env (#6730)
* improve errors for `config nu` and `config env`

* fix tests
2022-10-15 08:28:54 -05:00
e22f2e9f13 window --remainder (#6738)
* Implement window remainder, and save allocation

* Fallible memory reservation
2022-10-15 08:06:54 -05:00
4ffa4ac42a Delete out.log (#6731) 2022-10-15 07:37:57 -05:00
da6f548dfd Remove unnecessary clone (#6729) 2022-10-14 17:13:24 -05:00
JT
7532991544 Allow auto-cd to work with backticks (#6728) 2022-10-15 10:37:31 +13:00
b7f47317c2 Fix quadratic time complexity with large strides (#6727) 2022-10-14 15:17:47 -05:00
5849e4f6e3 let alias list aliases (#6717)
* let `alias` list aliases

* fix highlighting

* Update parse_keywords.rs
2022-10-14 21:51:44 +02:00
868d94f573 Add search terms for export commands (#6722)
Contributes to https://github.com/nushell/nushell/issues/5093
2022-10-14 12:02:22 -05:00
1344ae3a65 add the ability to convert durations (#6723)
* add the ability to convert durations

* add conversion to floats if necessary
2022-10-14 11:46:48 -05:00
804b155035 Add search terms for uppercase (#6720)
* Add search terms for uppercase

* Add search terms

* Add search terms

* Change to parse

* Add search terms for from

* Add search terms for to

* Remove duplicate function

* Remove duplication of search terms

* Remove search term
2022-10-14 05:36:31 -05:00
9446e3960b Fix invalid variable name in input command docs (#6716) 2022-10-13 12:42:24 -05:00
90ba39184a allow for $in to affect environment (#6649)
* allow for `$in` to affect environment

* fix for vars, overlays, env_hidden

* fmt

* carry over variables properly

* add test

* modify name, remove redundant

* fmt
2022-10-13 12:04:34 +03:00
d40a73aafe Backport fixes from nushell/nushell.github.io#633 (#6712)
Co-authored-by: Eric Hodel <drbrain@segment7.net>

Co-authored-by: Eric Hodel <drbrain@segment7.net>
2022-10-12 19:14:16 +02:00
5815f122ed avoid freeze when capturing external stderr (#6700)
* avoid freeze when capturing external stderr

* try replace from sh to bash

* change description

* fmt code
2022-10-12 08:41:20 -05:00
0bbb3a20df Fix ex. completion git push --force-with-lease (#6702)
The `--force-with-lease` flag was given as requiring an additional string which is not true.

Fixes #6644 

for this to take effect you need to update your `config.nu`
2022-10-11 12:41:50 +02:00
1998bce19f avoid freeze for table print (#6688)
* avoid freeze for table print

* make failed_with_proper_exit_code work again

* add test case for table

* fix un-used import on windows
2022-10-10 07:32:55 -05:00
2f1711f783 return gid and uid in numbers if name not found (#6684)
* return git and uid in numbers if name not found

* fmt
2022-10-10 14:29:16 +02:00
34c8b276ab Return Error on str replace RegEx parse fail (#6695) 2022-10-10 07:27:01 -05:00
fde56cfe99 upgrade num-format (#6694) 2022-10-10 06:25:57 -05:00
118033e4a5 don't attempt to eval and record down if the repl line is empty (#6674) 2022-10-08 16:38:35 -05:00
7910d20e50 add a new command to query the registry on windows (#6670)
* add a new command to query the registry on windows

* cross platform tweaks

* return nushell datatype

* change visibility of exec and registry commands
2022-10-07 13:54:36 -05:00
e1d5180e6d Update nushell version for release workflow (#6666) 2022-10-05 22:10:25 +08:00
79ce13abef To nuon escapes (#6660)
* Add tests for "to nuon" escaping handling

* Fix "to nuon" not escaping double quotations

* Fix "to nuon" double backslash

Fix value_to_string_without_quotes leaving escaped backslash in
non-quoted strings
2022-10-04 06:25:21 -05:00
5921c19bc0 WIP/ Checkout to new tabled (#6286)
* nu-table/ Use latest tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table/ Fix first column alignment

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix cargo clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix color issue

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix footer row

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Update

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table/ Update

* Use latest tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add optional -e, -c argument to `table` command for different view

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Update

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix cargo clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Add footer into -e/c mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Publish new expand mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add width ctrl for Expand mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Refactorings

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Refactorings

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Merge with main

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add record expand and fix empty list issue

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* refactoring

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-03 11:40:16 -05:00
e629ef203a nu-cli: external completer precedence before file (#6652) 2022-10-01 07:24:22 -05:00
5959d1366a Remove unnecessary flags from term size (#6651)
The columns and rows can be obtained individually using

(term size).columns
(term size).rows
2022-10-01 07:00:54 -05:00
530ff3893e Eval external command result immediately when using do command with -c (#6645)
* make capture error works better in do command

* remove into string test because we have no way to generate Value::Error for now
2022-09-30 07:14:02 -05:00
6f59167960 Make semicolon works better for internal commands (#6643)
* make semicolon works with some internal command like do

* refactor, make consume external result logic out of eval_external

* update comment
2022-09-30 07:13:46 -05:00
ca715bb929 tweak the banner message and make the time more accurate (#6641) 2022-09-29 14:07:32 -05:00
4af0a6a3fa Foreground process group management, again (#6584)
* Revert "Revert "Try again: in unix like system, set foreground process while running external command (#6273)" (#6542)"

This reverts commit 2bb367f570.

* Make foreground job control hopefully work correctly

These changes are mostly inspired by the glibc manual.

* Fix typo in external command description

* Only restore tty control to shell when no fg procs are left; reuse pgrp

* Rework terminal acquirement code to be like fish

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-09-29 13:37:48 -05:00
6486364610 changed the way durations and filesizes are parsed (#6640) 2022-09-29 13:24:17 -05:00
6aa8a0073b add better description to table_index_mode (#6637) 2022-09-29 06:26:01 -05:00
f5e1b08e6a ensure Operator::And errors out with incompatible types (#6638) 2022-09-29 06:17:21 -05:00
7b9ad9d2e5 Fix issue 6596 (#6603)
* Fix issue 6596

* add two unit tests

* fix pipe

* add cp test

* fix test on windows
2022-09-29 10:43:58 +02:00
1a3762b905 prevent alias name from being filesize or number (#6595)
* prevent alias name from being filesize or number

* add test

* fmt
2022-09-28 17:08:38 -05:00
32fbcf39cc make first behave same way as last: always return list when with number argument (#6616)
* make `first` behave same way as `last`

* better behaviour

* fix tests

* add tests
2022-09-28 17:08:17 -05:00
dd578926c3 add some float operations with filesize (#6618)
* add some float operations with filesize

* more changes

* update return values on filesize-filesize, duration-duration

* missed filesize in floordiv

* missed float * duration
2022-09-28 17:07:50 -05:00
5c99921e15 Table indexes (#6620)
* Table indexes

* Renamed to `show_table_indexes`

* Renamed to `table_index_mode`
2022-09-28 17:07:33 -05:00
d2e4f03d19 update and fix python plugin example (#6633)
* update and fix python plugin example

* update comment
2022-09-28 17:06:43 -05:00
23bba9935f bump to dev version 0.69.2 (#6635) 2022-09-28 17:06:21 -05:00
737 changed files with 38135 additions and 20069 deletions

View File

@ -12,3 +12,17 @@ rustflags = ["-C", "link-args=-stack:10000000", "-C", "target-feature=+crt-stati
# set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB) # set a 2 gb stack size (0x80000000 = 2147483648 bytes = 2 GB)
# [target.x86_64-apple-darwin] # [target.x86_64-apple-darwin]
# rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"] # rustflags = ["-C", "link-args=-Wl,-stack_size,0x80000000"]
# How to use mold in linux and mac
# [target.x86_64-unknown-linux-gnu]
# linker = "clang"
# rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]
# [target.x86_64-apple-darwin]
# linker = "clang"
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]
# [target.aarch64-apple-darwin]
# linker = "clang"
# rustflags = ["-C", "link-arg=-fuse-ld=mold"]

View File

@ -1,17 +1,24 @@
# Description # Description
(description of your pull request here) _(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_
# Tests _(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_
Make sure you've done the following: # User-Facing Changes
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder. _(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works. # Tests + Formatting
Don't forget to add tests that cover your changes.
Make sure you've run and fixed any issues with these commands: Make sure you've run and fixed any issues with these commands:
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass - `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.

View File

@ -11,7 +11,10 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] # Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
# revisiting this when 20.04 is closer to EOL (April 2025)
platform: [windows-latest, macos-latest, ubuntu-20.04]
rust: rust:
- stable - stable
@ -20,32 +23,16 @@ jobs:
NUSHELL_CARGO_TARGET: ci NUSHELL_CARGO_TARGET: ci
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Rust toolchain - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
# components: rustfmt, clippy
- uses: Swatinem/rust-cache@v1 - name: cargo fmt
with: run: cargo fmt --all -- --check
key: "v2" # increment this to bust the cache if needed
- name: Rustfmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Clippy - name: Clippy
uses: actions-rs/cargo@v1 run: cargo clippy --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
with:
command: clippy
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
nu-tests: nu-tests:
env: env:
@ -54,43 +41,32 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] platform: [windows-latest, macos-latest, ubuntu-20.04]
style: [extra, default] style: [default, dataframe]
rust: rust:
- stable - stable
include: include:
- style: extra
flags: "--features=extra"
- style: default - style: default
flags: "" flags: ""
- style: dataframe
flags: "--features=dataframe"
exclude: exclude:
# only test dataframes on Ubuntu (the fastest platform)
- platform: windows-latest - platform: windows-latest
style: default style: dataframe
- platform: macos-latest - platform: macos-latest
style: default style: dataframe
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Rust toolchain - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- uses: Swatinem/rust-cache@v1
with:
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
- name: Tests - name: Tests
uses: actions-rs/cargo@v1 run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
with:
command: test
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
python-virtualenv: python-virtualenv:
env: env:
@ -99,7 +75,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [ubuntu-latest, macos-latest, windows-latest] platform: [ubuntu-20.04, macos-latest, windows-latest]
rust: rust:
- stable - stable
py: py:
@ -108,28 +84,16 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Rust toolchain - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- uses: Swatinem/rust-cache@v1
with:
key: "2" # increment this to bust the cache if needed
- name: Install Nushell - name: Install Nushell
uses: actions-rs/cargo@v1 run: cargo install --locked --path=. --profile ci --no-default-features
with:
command: install
args: --path=. --profile ci --no-default-features
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v2 uses: actions/setup-python@v4
with: with:
python-version: "3.10" python-version: "3.10"
@ -152,31 +116,20 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
platform: [windows-latest, macos-latest, ubuntu-latest] platform: [windows-latest, macos-latest, ubuntu-20.04]
rust: rust:
- stable - stable
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup Rust toolchain - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
# makes ci use rust-toolchain.toml
# with:
# profile: minimal
# toolchain: ${{ matrix.rust }}
# override: true
- name: Clippy - name: Clippy
uses: actions-rs/cargo@v1 run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
with:
command: clippy
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
- name: Tests - name: Tests
uses: actions-rs/cargo@v1 run: cargo test --profile ci --package nu_plugin_*
with:
command: test
args: --profile ci --package nu_plugin_*

View File

@ -6,6 +6,21 @@
# REF: # REF:
# 1. https://github.com/volks73/cargo-wix # 1. https://github.com/volks73/cargo-wix
# Added 2022-11-29 when Windows packaging wouldn't work
# because softprops/action-gh-release was broken
# To run this manual for windows
# let-env TARGET = 'x86_64-pc-windows-msvc'
# let-env TARGET_RUSTFLAGS = ''
# let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
# Pass 1 let-env _EXTRA_ = 'bin'
# Pass 2 let-env _EXTRA_ = 'msi'
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
# make sure you have the wixtools installed https://wixtoolset.org/
# set os below like this because it's what github's runner is named
# let os = 'windows-latest'
# The main binary file to be released # The main binary file to be released
let bin = 'nu' let bin = 'nu'
let os = $env.OS let os = $env.OS
@ -16,8 +31,13 @@ let flags = $env.TARGET_RUSTFLAGS
let dist = $'($env.GITHUB_WORKSPACE)/output' let dist = $'($env.GITHUB_WORKSPACE)/output'
let version = (open Cargo.toml | get package.version) let version = (open Cargo.toml | get package.version)
$'Debugging info:'
print { version: $version, bin: $bin, os: $os, target: $target, src: $src, flags: $flags, dist: $dist }; hr-line -b
# $env # $env
let USE_UBUNTU = 'ubuntu-20.04'
$'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b $'(char nl)Packaging ($bin) v($version) for ($target) in ($src)...'; hr-line -b
if not ('Cargo.lock' | path exists) { cargo generate-lockfile } if not ('Cargo.lock' | path exists) { cargo generate-lockfile }
@ -26,8 +46,9 @@ $'Start building ($bin)...'; hr-line
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Build for Ubuntu and macOS # Build for Ubuntu and macOS
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
if $os in ['ubuntu-latest', 'macos-latest'] { if $os in [$USE_UBUNTU, 'macos-latest'] {
if $os == 'ubuntu-latest' { if $os == $USE_UBUNTU {
sudo apt update
sudo apt-get install libxcb-composite0-dev -y sudo apt-get install libxcb-composite0-dev -y
} }
if $target == 'aarch64-unknown-linux-gnu' { if $target == 'aarch64-unknown-linux-gnu' {
@ -38,10 +59,14 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc' let-env CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
cargo-build-nu $flags cargo-build-nu $flags
} else if $target == 'riscv64gc-unknown-linux-gnu' {
sudo apt-get install gcc-riscv64-linux-gnu -y
let-env CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER = 'riscv64-linux-gnu-gcc'
cargo-build-nu $flags
} else { } else {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?' # musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target # Actually just for x86_64-unknown-linux-musl target
if $os == 'ubuntu-latest' { sudo apt install musl-tools -y } if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
cargo-build-nu $flags cargo-build-nu $flags
} }
} }
@ -51,9 +76,9 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
if $os in ['windows-latest'] { if $os in ['windows-latest'] {
if ($flags | str trim | is-empty) { if ($flags | str trim | is-empty) {
cargo build --release --all --target $target --features=extra cargo build --release --all --target $target
} else { } else {
cargo build --release --all --target $target --features=extra $flags cargo build --release --all --target $target $flags
} }
} }
@ -76,9 +101,9 @@ cp -v README.release.txt $'($dist)/README.txt'
$'(char nl)Check binary release version detail:'; hr-line $'(char nl)Check binary release version detail:'; hr-line
let ver = if $os == 'windows-latest' { let ver = if $os == 'windows-latest' {
(do -i { ./output/nu.exe -c 'version' }) | str collect (do -i { ./output/nu.exe -c 'version' }) | str join
} else { } else {
(do -i { ./output/nu -c 'version' }) | str collect (do -i { ./output/nu -c 'version' }) | str join
} }
if ($ver | str trim | is-empty) { if ($ver | str trim | is-empty) {
$'(ansi r)Incompatible nu binary...(ansi reset)' $'(ansi r)Incompatible nu binary...(ansi reset)'
@ -88,21 +113,28 @@ if ($ver | str trim | is-empty) {
# Create a release archive and send it to output for the following steps # Create a release archive and send it to output for the following steps
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
cd $dist; $'(char nl)Creating release archive...'; hr-line cd $dist; $'(char nl)Creating release archive...'; hr-line
if $os in ['ubuntu-latest', 'macos-latest'] { if $os in [$USE_UBUNTU, 'macos-latest'] {
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls let files = (ls | get name)
let dest = $'($bin)-($version)-($target)'
let archive = $'($dist)/($dest).tar.gz'
let archive = $'($dist)/($bin)-($version)-($target).tar.gz' mkdir $dest
tar czf $archive * $files | each {|it| mv $it $dest } | ignore
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
tar -czf $archive $dest
print $'archive: ---> ($archive)'; ls $archive print $'archive: ---> ($archive)'; ls $archive
echo $'::set-output name=archive::($archive)' # REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
} else if $os == 'windows-latest' { } else if $os == 'windows-latest' {
let releaseStem = $'($bin)-($version)-($target)' let releaseStem = $'($bin)-($version)-($target)'
$'(char nl)Download less related stuffs...'; hr-line $'(char nl)Download less related stuffs...'; hr-line
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v590/less.exe -o less.exe aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
# Create Windows msi release package # Create Windows msi release package
@ -115,7 +147,8 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
cp -r $'($dist)/*' target/release/ cp -r $'($dist)/*' target/release/
cargo install cargo-wix --version 0.3.3 cargo install cargo-wix --version 0.3.3
cargo wix --no-build --nocapture --package nu --output $wixRelease cargo wix --no-build --nocapture --package nu --output $wixRelease
echo $'::set-output name=archive::($wixRelease)' print $'archive: ---> ($wixRelease)';
echo $"archive=($wixRelease)" | save --append $env.GITHUB_OUTPUT
} else { } else {
@ -125,16 +158,16 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
print $'archive: ---> ($archive)'; print $'archive: ---> ($archive)';
let pkg = (ls -f $archive | get name) let pkg = (ls -f $archive | get name)
if not ($pkg | is-empty) { if not ($pkg | is-empty) {
echo $'::set-output name=archive::($pkg | get 0)' echo $"archive=($pkg | get 0)" | save --append $env.GITHUB_OUTPUT
} }
} }
} }
def 'cargo-build-nu' [ options: string ] { def 'cargo-build-nu' [ options: string ] {
if ($options | str trim | is-empty) { if ($options | str trim | is-empty) {
cargo build --release --all --target $target --features=extra,static-link-openssl cargo build --release --all --target $target --features=static-link-openssl
} else { } else {
cargo build --release --all --target $target --features=extra,static-link-openssl $options cargo build --release --all --target $target --features=static-link-openssl $options
} }
} }

View File

@ -27,6 +27,7 @@ jobs:
- x86_64-unknown-linux-musl - x86_64-unknown-linux-musl
- aarch64-unknown-linux-gnu - aarch64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf - armv7-unknown-linux-gnueabihf
- riscv64gc-unknown-linux-gnu
extra: ['bin'] extra: ['bin']
include: include:
- target: aarch64-apple-darwin - target: aarch64-apple-darwin
@ -44,35 +45,37 @@ jobs:
os: windows-latest os: windows-latest
target_rustflags: '' target_rustflags: ''
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-latest os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-20.04
target_rustflags: '' target_rustflags: ''
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v3.0.2 - uses: actions/checkout@v3.1.0
- name: Install Rust Toolchain Components - name: Update Rust Toolchain Target
uses: actions-rs/toolchain@v1.0.6 run: |
with: echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
override: true
profile: minimal - name: Setup Rust toolchain and cache
toolchain: stable uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
target: ${{ matrix.target }}
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v2.1 uses: hustcer/setup-nu@v3
with: with:
version: 0.68.0 version: 0.72.1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -88,7 +91,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release # REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive - name: Publish Archive
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v0.1.13
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
draft: true draft: true

4
.gitignore vendored
View File

@ -22,6 +22,10 @@ debian/nu/
# VSCode's IDE items # VSCode's IDE items
.vscode/* .vscode/*
# Visual Studio Extension SourceGear Rust items
VSWorkspaceSettings.json
unstable_cargo_features.txt
# Helix configuration folder # Helix configuration folder
.helix/* .helix/*
.helix .helix

View File

@ -1,8 +1,23 @@
# Contributing # Contributing
Welcome to Nushell! Welcome to Nushell and thank you for considering contributing!
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)! ## Review Process
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
This saves both your and our time if we realize the change needs to go another direction before spending time on it.
So, please, reach out and tell us what you want to do.
This will significantly increase the chance of your PR being accepted.
The review process can be summarized as follows:
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
5. Then, core team members and other regular contributors will review the PR and suggest changes.
6. When we all agree, the PR will be merged.
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
8. Congratulate yourself, you just improved Nushell! :-)
## Developing ## Developing
@ -16,6 +31,18 @@ cd nushell
cargo build cargo build
``` ```
### Tests
It is a good practice to cover your changes with a test. Also, try to think about corner cases and various ways how your changes could break. Cover those in the tests as well.
Tests can be found in different places:
* `/tests`
* `src/tests`
* command examples
* crate-specific tests
The most comprehensive test suite we have is the `nu-test-support` crate. For testing specific features, such as running Nushell in a REPL mode, we have so called "testbins". For simple tests, you can find `run_test()` and `fail_test()` functions.
### Useful Commands ### Useful Commands
- Build and run Nushell: - Build and run Nushell:
@ -24,21 +51,21 @@ cargo build
cargo run cargo run
``` ```
- Build and run with extra features. Currently extra features include dataframes and sqlite database support. - Build and run with dataframe support.
```shell ```shell
cargo run --features=extra cargo run --features=dataframe
``` ```
- Run Clippy on Nushell: - Run Clippy on Nushell:
```shell ```shell
cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
``` ```
- Run all tests: - Run all tests:
```shell ```shell
cargo test --workspace --features=extra cargo test --workspace
``` ```
- Run all tests for a specific command - Run all tests for a specific command
@ -64,11 +91,11 @@ cargo build
- To view verbose logs when developing, enable the `trace` log level. - To view verbose logs when developing, enable the `trace` log level.
```shell ```shell
cargo run --release --features=extra -- --log-level trace cargo run --release -- --log-level trace
``` ```
- To redirect trace logs to a file, enable the `--log-target file` switch. - To redirect trace logs to a file, enable the `--log-target file` switch.
```shell ```shell
cargo run --release --features=extra -- --log-level trace --log-target file cargo run --release -- --log-level trace --log-target file
open $"($nu.temp-path)/nu-($nu.pid).log" open $"($nu.temp-path)/nu-($nu.pid).log"
``` ```

1429
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,13 +8,19 @@ exclude = ["images"]
homepage = "https://www.nushell.sh" homepage = "https://www.nushell.sh"
license = "MIT" license = "MIT"
name = "nu" name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell" repository = "https://github.com/nushell/nushell"
rust-version = "1.60" rust-version = "1.60"
version = "0.69.1" version = "0.73.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/{ version }/{ name }-{ version }-{ target }.{ archive-format }"
pkg-fmt = "tgz"
[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-fmt = "zip"
[workspace] [workspace]
members = [ members = [
"crates/nu-cli", "crates/nu-cli",
@ -33,27 +39,27 @@ members = [
] ]
[dependencies] [dependencies]
chrono = { version = "0.4.21", features = ["serde"] } chrono = { version = "0.4.23", features = ["serde"] }
crossterm = "0.24.0" crossterm = "0.24.0"
ctrlc = "3.2.1" ctrlc = "3.2.1"
log = "0.4" log = "0.4"
miette = "5.1.0" miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.69.1" } nu-cli = { path="./crates/nu-cli", version = "0.73.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.69.1" } nu-color-config = { path = "./crates/nu-color-config", version = "0.73.0" }
nu-command = { path="./crates/nu-command", version = "0.69.1" } nu-command = { path="./crates/nu-command", version = "0.73.0" }
nu-engine = { path="./crates/nu-engine", version = "0.69.1" } nu-engine = { path="./crates/nu-engine", version = "0.73.0" }
nu-json = { path="./crates/nu-json", version = "0.69.1" } nu-json = { path="./crates/nu-json", version = "0.73.0" }
nu-parser = { path="./crates/nu-parser", version = "0.69.1" } nu-parser = { path="./crates/nu-parser", version = "0.73.0" }
nu-path = { path="./crates/nu-path", version = "0.69.1" } nu-path = { path="./crates/nu-path", version = "0.73.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.69.1" } nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.73.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.69.1" } nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.73.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.69.1" } nu-protocol = { path = "./crates/nu-protocol", version = "0.73.0" }
nu-system = { path = "./crates/nu-system", version = "0.69.1" } nu-system = { path = "./crates/nu-system", version = "0.73.0" }
nu-table = { path = "./crates/nu-table", version = "0.69.1" } nu-table = { path = "./crates/nu-table", version = "0.73.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.69.1" } nu-term-grid = { path = "./crates/nu-term-grid", version = "0.73.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.69.1" } nu-utils = { path = "./crates/nu-utils", version = "0.73.0" }
reedline = { version = "0.12.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
rayon = "1.5.1" rayon = "1.5.1"
is_executable = "1.0.1" is_executable = "1.0.1"
@ -65,8 +71,16 @@ time = "0.3.12"
openssl = { version = "0.10.38", features = ["vendored"], optional = true } openssl = { version = "0.10.38", features = ["vendored"], optional = true }
signal-hook = { version = "0.3.14", default-features = false } signal-hook = { version = "0.3.14", default-features = false }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
atty = "0.2"
[dev-dependencies] [dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.69.1" } nu-test-support = { path="./crates/nu-test-support", version = "0.73.0" }
tempfile = "3.2.0" tempfile = "3.2.0"
assert_cmd = "2.0.2" assert_cmd = "2.0.2"
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
@ -75,15 +89,14 @@ hamcrest2 = "0.3.0"
rstest = {version = "0.15.0", default-features = false} rstest = {version = "0.15.0", default-features = false}
itertools = "0.10.3" itertools = "0.10.3"
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[features] [features]
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"] plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
extra = ["default", "dataframe", "database"] # extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
default = ["plugin", "which-support", "trash-support"] extra = ["default"]
default = ["plugin", "which-support", "trash-support", "sqlite"]
stable = ["default"] stable = ["default"]
wasi = [] wasi = []
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build # Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl"] static-link-openssl = ["dep:openssl"]
@ -96,8 +109,8 @@ trash-support = ["nu-command/trash-support"]
# Dataframe feature for nushell # Dataframe feature for nushell
dataframe = ["nu-command/dataframe"] dataframe = ["nu-command/dataframe"]
# Database commands for nushell # SQLite commands for nushell
database = ["nu-command/database"] sqlite = ["nu-command/sqlite"]
[profile.release] [profile.release]
opt-level = "s" # Optimize for size opt-level = "s" # Optimize for size
@ -122,3 +135,8 @@ debug = false
[[bin]] [[bin]]
name = "nu" name = "nu"
path = "src/main.rs" path = "src/main.rs"
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }

View File

@ -1,6 +1,6 @@
# Nushell <!-- omit in toc --> # Nushell <!-- omit in toc -->
[![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu) [![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu)
![Build Status](https://img.shields.io/github/workflow/status/nushell/nushell/continuous-integration) ![Build Status](https://img.shields.io/github/actions/workflow/status/nushell/nushell/ci.yml?branch=main)
[![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn) [![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn)
[![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363) [![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363)
[![@nu_shell](https://img.shields.io/badge/twitter-@nu_shell-1DA1F3?style=flat-square)](https://twitter.com/nu_shell) [![@nu_shell](https://img.shields.io/badge/twitter-@nu_shell-1DA1F3?style=flat-square)](https://twitter.com/nu_shell)
@ -71,7 +71,7 @@ Additionally, commands can output structured data (you can think of this as a th
Commands that work in the pipeline fit into one of three categories: Commands that work in the pipeline fit into one of three categories:
- Commands that produce a stream (e.g., `ls`) - Commands that produce a stream (e.g., `ls`)
- Commands that filter a stream (eg, `where type == "dir"`) - Commands that filter a stream (e.g., `where type == "dir"`)
- Commands that consume the output of the pipeline (e.g., `table`) - Commands that consume the output of the pipeline (e.g., `table`)
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right. Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
@ -126,12 +126,13 @@ For example, you can load a .toml file as structured data and explore it:
> open Cargo.toml > open Cargo.toml
╭──────────────────┬────────────────────╮ ╭──────────────────┬────────────────────╮
│ bin │ [table 1 row] │ bin │ [table 1 row]
│ dependencies │ {record 24 fields} │ dependencies │ {record 25 fields}
│ dev-dependencies │ {record 8 fields} │ dev-dependencies │ {record 8 fields}
│ features │ {record 10 fields} │ features │ {record 10 fields}
│ package │ {record 13 fields} │ package │ {record 13 fields}
│ patch │ {record 1 field}
│ profile │ {record 3 fields} │ profile │ {record 3 fields}
│ target │ {record 2 fields} │ target │ {record 3 fields}
│ workspace │ {record 1 field} │ workspace │ {record 1 field}
╰──────────────────┴────────────────────╯ ╰──────────────────┴────────────────────╯
``` ```
@ -149,11 +150,11 @@ We can pipe this into a command that gets the contents of one of the columns:
│ exclude │ [list 1 item] │ exclude │ [list 1 item]
│ homepage │ https://www.nushell.sh │ │ homepage │ https://www.nushell.sh │
│ license │ MIT │ │ license │ MIT │
│ metadata │ {record 1 field}
│ name │ nu │ │ name │ nu │
│ readme │ README.md │
│ repository │ https://github.com/nushell/nushell │ │ repository │ https://github.com/nushell/nushell │
│ rust-version │ 1.60 │ │ rust-version │ 1.60 │
│ version │ 0.63.1 │ version │ 0.72.0
╰───────────────┴────────────────────────────────────╯ ╰───────────────┴────────────────────────────────────╯
``` ```
@ -161,7 +162,7 @@ And if needed we can drill down further:
```shell ```shell
> open Cargo.toml | get package.version > open Cargo.toml | get package.version
0.63.1 0.72.0
``` ```
### Plugins ### Plugins
@ -206,7 +207,7 @@ Nu is under heavy development and will naturally change as it matures. The chart
| Functions | | | | X | | Functions and aliases are supported | | Functions | | | | X | | Functions and aliases are supported |
| Variables | | | | X | | Nu supports variables and environment variables | | Variables | | | | X | | Nu supports variables and environment variables |
| Completions | | | | X | | Completions for filepaths | | Completions | | | | X | | Completions for filepaths |
| Type-checking | | | X | | | Commands check basic types, but input/output isn't checked | | Type-checking | | | | x | | Commands check basic types, and input/output types |
## Officially Supported By ## Officially Supported By

View File

@ -1,7 +1,8 @@
#!/bin/sh #!/usr/bin/env bash
set -euo pipefail
echo "---------------------------------------------------------------" echo "---------------------------------------------------------------"
echo "Building nushell (nu) with --features=extra and all the plugins" echo "Building nushell (nu) with dataframes and all the plugins"
echo "---------------------------------------------------------------" echo "---------------------------------------------------------------"
echo "" echo ""
@ -14,10 +15,10 @@ NU_PLUGINS=(
) )
echo "Building nushell" echo "Building nushell"
cargo build --features=extra cargo build --features=dataframe
for plugin in "${NU_PLUGINS[@]}" for plugin in "${NU_PLUGINS[@]}"
do do
echo '' && cd crates/$plugin echo '' && cd crates/"$plugin"
echo "Building $plugin..." echo "Building $plugin..."
echo "-----------------------------" echo "-----------------------------"
cargo build && cd ../.. cargo build && cd ../..

View File

@ -1,11 +1,11 @@
@echo off @echo off
@echo ------------------------------------------------------------------- @echo -------------------------------------------------------------------
@echo Building nushell (nu.exe) with --features=extra and all the plugins @echo Building nushell (nu.exe) with dataframes and all the plugins
@echo ------------------------------------------------------------------- @echo -------------------------------------------------------------------
@echo. @echo.
echo Building nushell.exe echo Building nushell.exe
cargo build --features=extra cargo build cargo build --features=dataframe
@echo. @echo.
@cd crates\nu_plugin_example @cd crates\nu_plugin_example

View File

@ -1,10 +1,10 @@
echo '-------------------------------------------------------------------' echo '-------------------------------------------------------------------'
echo 'Building nushell (nu) with --features=extra and all the plugins' echo 'Building nushell (nu) with dataframes and all the plugins'
echo '-------------------------------------------------------------------' echo '-------------------------------------------------------------------'
echo $'(char nl)Building nushell' echo $'(char nl)Building nushell'
echo '----------------------------' echo '----------------------------'
cargo build --features=extra cargo build --features=dataframe
let plugins = [ let plugins = [
nu_plugin_inc, nu_plugin_inc,

View File

@ -5,34 +5,33 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cli" name = "nu-cli"
version = "0.69.1" version = "0.73.0"
[dev-dependencies] [dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.69.1" } nu-test-support = { path="../nu-test-support", version = "0.73.0" }
nu-command = { path = "../nu-command", version = "0.69.1" } nu-command = { path = "../nu-command", version = "0.73.0" }
rstest = {version = "0.15.0", default-features = false} rstest = {version = "0.15.0", default-features = false}
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.69.1" } nu-engine = { path = "../nu-engine", version = "0.73.0" }
nu-path = { path = "../nu-path", version = "0.69.1" } nu-path = { path = "../nu-path", version = "0.73.0" }
nu-parser = { path = "../nu-parser", version = "0.69.1" } nu-parser = { path = "../nu-parser", version = "0.73.0" }
nu-protocol = { path = "../nu-protocol", version = "0.69.1" } nu-protocol = { path = "../nu-protocol", version = "0.73.0" }
nu-utils = { path = "../nu-utils", version = "0.69.1" } nu-utils = { path = "../nu-utils", version = "0.73.0" }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.69.1" } nu-color-config = { path = "../nu-color-config", version = "0.73.0" }
reedline = { version = "0.12.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
atty = "0.2.14" atty = "0.2.14"
chrono = "0.4.21" chrono = { default-features = false, features = ["std"], version = "0.4.23" }
crossterm = "0.24.0" crossterm = "0.24.0"
fancy-regex = "0.10.0" fancy-regex = "0.10.0"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
is_executable = "1.0.1" is_executable = "1.0.1"
lazy_static = "1.4.0" once_cell = "1.16.0"
log = "0.4" log = "0.4"
miette = { version = "5.1.0", features = ["fancy"] } miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
percent-encoding = "2" percent-encoding = "2"
strip-ansi-escapes = "0.1.1"
sysinfo = "0.26.2" sysinfo = "0.26.2"
thiserror = "1.0.31" thiserror = "1.0.31"

View File

@ -15,7 +15,6 @@ pub fn evaluate_commands(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
input: PipelineData, input: PipelineData,
is_perf_true: bool,
table_mode: Option<Value>, table_mode: Option<Value>,
) -> Result<Option<i64>> { ) -> Result<Option<i64>> {
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
@ -68,9 +67,7 @@ pub fn evaluate_commands(
} }
}; };
if is_perf_true { info!("evaluate {}:{}:{}", file!(), line!(), column!());
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
Ok(exit_code) Ok(exit_code)
} }

View File

@ -60,7 +60,7 @@ impl CommandCompletion {
.matches_str(&x.to_string_lossy(), prefix)), .matches_str(&x.to_string_lossy(), prefix)),
Some(true) Some(true)
) )
&& is_executable::is_executable(&item.path()) && is_executable::is_executable(item.path())
{ {
if let Ok(name) = item.file_name().into_string() { if let Ok(name) = item.file_name().into_string() {
executables.push(name); executables.push(name);
@ -94,10 +94,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x.0).to_string(), value: String::from_utf8_lossy(&x.0).to_string(),
description: x.1, description: x.1,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -108,10 +105,7 @@ impl CommandCompletion {
value: String::from_utf8_lossy(&x).to_string(), value: String::from_utf8_lossy(&x).to_string(),
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
@ -128,15 +122,15 @@ impl CommandCompletion {
value: x, value: x,
description: None, description: None,
extra: None, extra: None,
span: reedline::Span { span: reedline::Span::new(span.start - offset, span.end - offset),
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: true, append_whitespace: true,
}); });
let results_strings: Vec<String> =
results.clone().into_iter().map(|x| x.value).collect();
for external in results_external { for external in results_external {
if results.contains(&external) { if results_strings.contains(&external.value) {
results.push(Suggestion { results.push(Suggestion {
value: format!("^{}", external.value), value: format!("^{}", external.value),
description: None, description: None,
@ -187,10 +181,7 @@ impl Completer for CommandCompletion {
let subcommands = if let Some(last) = last { let subcommands = if let Some(last) = last {
self.complete_commands( self.complete_commands(
working_set, working_set,
Span { Span::new(last.0.start, pos),
start: last.0.start,
end: pos,
},
offset, offset,
false, false,
options.match_algorithm, options.match_algorithm,

View File

@ -5,6 +5,7 @@ use crate::completions::{
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape}; use nu_parser::{flatten_expression, parse, FlatShape};
use nu_protocol::{ use nu_protocol::{
ast::PipelineElement,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
BlockId, PipelineData, Span, Value, BlockId, PipelineData, Span, Value,
}; };
@ -60,10 +61,10 @@ impl NuCompleter {
fn external_completion( fn external_completion(
&self, &self,
block_id: BlockId, block_id: BlockId,
spans: Vec<String>, spans: &[String],
offset: usize, offset: usize,
span: Span, span: Span,
) -> Vec<Suggestion> { ) -> Option<Vec<Suggestion>> {
let stack = self.stack.clone(); let stack = self.stack.clone();
let block = self.engine_state.get_block(block_id); let block = self.engine_state.get_block(block_id);
let mut callee_stack = stack.gather_captures(&block.captures); let mut callee_stack = stack.gather_captures(&block.captures);
@ -75,11 +76,8 @@ impl NuCompleter {
var_id, var_id,
Value::List { Value::List {
vals: spans vals: spans
.into_iter() .iter()
.map(|it| Value::String { .map(|it| Value::string(it, Span::unknown()))
val: it,
span: Span::unknown(),
})
.collect(), .collect(),
span: Span::unknown(), span: Span::unknown(),
}, },
@ -91,7 +89,7 @@ impl NuCompleter {
&self.engine_state, &self.engine_state,
&mut callee_stack, &mut callee_stack,
block, block,
PipelineData::new(span), PipelineData::empty(),
true, true,
true, true,
); );
@ -100,22 +98,16 @@ impl NuCompleter {
Ok(pd) => { Ok(pd) => {
let value = pd.into_value(span); let value = pd.into_value(span);
if let Value::List { vals, span: _ } = value { if let Value::List { vals, span: _ } = value {
let result = map_value_completions( let result =
vals.iter(), map_value_completions(vals.iter(), Span::new(span.start, span.end), offset);
Span {
start: span.start,
end: span.end,
},
offset,
);
return result; return Some(result);
} }
} }
Err(err) => println!("failed to eval completer block: {}", err), Err(err) => println!("failed to eval completer block: {}", err),
} }
vec![] None
} }
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> { fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
@ -123,141 +115,67 @@ impl NuCompleter {
let offset = working_set.next_span_start(); let offset = working_set.next_span_start();
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set); let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
let initial_line = line.to_string(); let initial_line = line.to_string();
new_line.push(b'a'); let alias_total_offset: usize = alias_offset.iter().sum();
new_line.insert(alias_total_offset + pos, b'a');
let pos = offset + pos; let pos = offset + pos;
let config = self.engine_state.get_config(); let config = self.engine_state.get_config();
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]); let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
for pipeline in output.pipelines.into_iter() { for pipeline in output.pipelines.into_iter() {
for expr in pipeline.expressions { for pipeline_element in pipeline.elements {
let flattened: Vec<_> = flatten_expression(&working_set, &expr); match pipeline_element {
let span_offset: usize = alias_offset.iter().sum(); PipelineElement::Expression(_, expr)
let mut spans: Vec<String> = vec![]; | PipelineElement::Redirection(_, _, expr)
| PipelineElement::And(_, expr)
| PipelineElement::Or(_, expr) => {
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum();
let mut spans: Vec<String> = vec![];
for (flat_idx, flat) in flattened.iter().enumerate() { for (flat_idx, flat) in flattened.iter().enumerate() {
// Read the current spam to string // Read the current spam to string
let current_span = working_set.get_span_contents(flat.0).to_vec(); let current_span = working_set.get_span_contents(flat.0).to_vec();
let current_span_str = String::from_utf8_lossy(&current_span); let current_span_str = String::from_utf8_lossy(&current_span);
// Skip the last 'a' as span item // Skip the last 'a' as span item
if flat_idx == flattened.len() - 1 { if flat_idx == flattened.len() - 1 {
let mut chars = current_span_str.chars(); let mut chars = current_span_str.chars();
chars.next_back(); chars.next_back();
let current_span_str = chars.as_str().to_owned(); let current_span_str = chars.as_str().to_owned();
spans.push(current_span_str.to_string()); spans.push(current_span_str.to_string());
} else { } else {
spans.push(current_span_str.to_string()); spans.push(current_span_str.to_string());
}
// Complete based on the last span
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
most_left_variable(flat_idx, &working_set, flattened.clone());
// Create a new span
let new_span = if flat_idx == 0 {
Span {
start: flat.0.start,
end: flat.0.end - 1 - span_offset,
}
} else {
Span {
start: flat.0.start - span_offset,
end: flat.0.end - 1 - span_offset,
}
};
// Parses the prefix
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
prefix.remove(pos - (flat.0.start - span_offset));
// Variables completion
if prefix.starts_with(b"$") || most_left_var.is_some() {
let mut completer = VariableCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
most_left_var.unwrap_or((vec![], vec![])),
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Flags completion
if prefix.starts_with(b"-") {
// Try to complete flag internally
let mut completer = FlagCompletion::new(expr.clone());
let result = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !result.is_empty() {
return result;
} }
// We got no results for internal completion // Complete based on the last span
// now we can check if external completer is set and use it if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
if let Some(block_id) = config.external_completer { // Context variables
return self.external_completion(block_id, spans, offset, new_span); let most_left_var =
} most_left_variable(flat_idx, &working_set, flattened.clone());
}
// specially check if it is currently empty - always complete commands // Create a new span
if flat_idx == 0 && working_set.get_span_contents(new_span).is_empty() { let new_span = if flat_idx == 0 {
let mut completer = CommandCompletion::new( Span::new(flat.0.start, flat.0.end - 1 - span_offset)
self.engine_state.clone(), } else {
&working_set, Span::new(
flattened.clone(), flat.0.start - span_offset,
// flat_idx, flat.0.end - 1 - span_offset,
FlatShape::String, )
true, };
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Completions that depends on the previous expression (e.g: use, source-env) // Parses the prefix. Completion should look up to the cursor position, not after.
if flat_idx > 0 { let mut prefix = working_set.get_span_contents(flat.0).to_vec();
if let Some(previous_expr) = flattened.get(flat_idx - 1) { let index = pos - (flat.0.start - span_offset);
// Read the content for the previous expression prefix.drain(index..);
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files // Variables completion
if prev_expr_str == b"use" || prev_expr_str == b"source-env" { if prefix.starts_with(b"$") || most_left_var.is_some() {
let mut completer = let mut completer = VariableCompletion::new(
DotNuCompletion::new(self.engine_state.clone()); self.engine_state.clone(),
self.stack.clone(),
return self.process_completion( most_left_var.unwrap_or((vec![], vec![])),
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
); );
} else if prev_expr_str == b"ls" {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion( return self.process_completion(
&mut completer, &mut completer,
@ -268,98 +186,190 @@ impl NuCompleter {
pos, pos,
); );
} }
// Flags completion
if prefix.starts_with(b"-") {
// Try to complete flag internally
let mut completer = FlagCompletion::new(expr.clone());
let result = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !result.is_empty() {
return result;
}
// We got no results for internal completion
// now we can check if external completer is set and use it
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self
.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
}
}
}
// specially check if it is currently empty - always complete commands
if flat_idx == 0
&& working_set.get_span_contents(new_span).is_empty()
{
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
FlatShape::String,
true,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
// Completions that depends on the previous expression (e.g: use, source-env)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
let prev_expr_str =
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
{
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
} else if prev_expr_str == b"ls" {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
}
}
// Match other types
match &flat.1 {
FlatShape::Custom(decl_id) => {
let mut completer = CustomCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
*decl_id,
initial_line,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Directory => {
let mut completer =
DirectoryCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
flat_shape => {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
flat_shape.clone(),
false,
);
let mut out: Vec<_> = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Try to complete using an external completer (if set)
if let Some(block_id) = config.external_completer {
if let Some(external_result) = self.external_completion(
block_id, &spans, offset, new_span,
) {
return external_result;
}
}
// Check for file completion
let mut completer =
FileCompletion::new(self.engine_state.clone());
out = self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
}
};
} }
} }
// Match other types
match &flat.1 {
FlatShape::Custom(decl_id) => {
let mut completer = CustomCompletion::new(
self.engine_state.clone(),
self.stack.clone(),
*decl_id,
initial_line,
);
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Directory => {
let mut completer =
DirectoryCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let mut completer = FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
}
flat_shape => {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
flat_shape.clone(),
false,
);
let mut out: Vec<_> = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Check for file completion
let mut completer = FileCompletion::new(self.engine_state.clone());
out = self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Try to complete using an exnternal compelter (if set)
if let Some(block_id) = config.external_completer {
return self
.external_completion(block_id, spans, offset, new_span);
}
}
};
} }
} }
} }

View File

@ -52,13 +52,13 @@ impl Completer for CustomCompletion {
head: span, head: span,
arguments: vec![ arguments: vec![
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::String, ty: Type::String,
expr: Expr::String(self.line.clone()), expr: Expr::String(self.line.clone()),
custom_completion: None, custom_completion: None,
}), }),
Argument::Positional(Expression { Argument::Positional(Expression {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
ty: Type::Int, ty: Type::Int,
expr: Expr::Int(line_pos as i64), expr: Expr::Int(line_pos as i64),
custom_completion: None, custom_completion: None,
@ -67,7 +67,7 @@ impl Completer for CustomCompletion {
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: true, redirect_stderr: true,
}, },
PipelineData::new(span), PipelineData::empty(),
); );
let mut custom_completion_options = None; let mut custom_completion_options = None;

View File

@ -136,8 +136,12 @@ pub fn directory_completion(
file_name.push(SEP); file_name.push(SEP);
} }
// Fix files or folders with quotes // Fix files or folders with quotes or hash
if path.contains('\'') || path.contains('"') || path.contains(' ') { if path.contains('\'')
|| path.contains('"')
|| path.contains(' ')
|| path.contains('#')
{
path = format!("`{}`", path); path = format!("`{}`", path);
} }

View File

@ -141,8 +141,12 @@ pub fn file_path_completion(
file_name.push(SEP); file_name.push(SEP);
} }
// Fix files or folders with quotes // Fix files or folders with quotes or hashes
if path.contains('\'') || path.contains('"') || path.contains(' ') { if path.contains('\'')
|| path.contains('"')
|| path.contains(' ')
|| path.contains('#')
{
path = format!("`{}`", path); path = format!("`{}`", path);
} }

View File

@ -111,10 +111,7 @@ impl Completer for VariableCompletion {
&self.engine_state, &self.engine_state,
&self.stack, &self.stack,
nu_protocol::NU_VARIABLE_ID, nu_protocol::NU_VARIABLE_ID,
nu_protocol::Span { nu_protocol::Span::new(current_span.start, current_span.end),
start: current_span.start,
end: current_span.end,
},
) { ) {
for suggestion in for suggestion in
nested_suggestions(nuval, self.var_context.1.clone(), current_span) nested_suggestions(nuval, self.var_context.1.clone(), current_span)
@ -134,13 +131,7 @@ impl Completer for VariableCompletion {
// Completion other variable types // Completion other variable types
if let Some(var_id) = var_id { if let Some(var_id) = var_id {
// Extract the variable value from the stack // Extract the variable value from the stack
let var = self.stack.get_var( let var = self.stack.get_var(var_id, Span::new(span.start, span.end));
var_id,
Span {
start: span.start,
end: span.end,
},
);
// If the value exists and it's of type Record // If the value exists and it's of type Record
if let Ok(value) = var { if let Ok(value) = var {
@ -281,7 +272,7 @@ fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
// Current sublevel value not found // Current sublevel value not found
return Value::Nothing { return Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}; };
} }
_ => return val, _ => return val,

View File

@ -8,7 +8,7 @@ use nu_path::canonicalize_with;
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_protocol::Spanned; use nu_protocol::Spanned;
use nu_protocol::{HistoryFileFormat, PipelineData, Span}; use nu_protocol::{HistoryFileFormat, PipelineData};
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -23,7 +23,6 @@ pub fn read_plugin_file(
stack: &mut Stack, stack: &mut Stack,
plugin_file: Option<Spanned<String>>, plugin_file: Option<Spanned<String>>,
storage_path: &str, storage_path: &str,
is_perf_true: bool,
) { ) {
// Reading signatures from signature file // Reading signatures from signature file
// The plugin.nu file stores the parsed signature collected from each registered plugin // The plugin.nu file stores the parsed signature collected from each registered plugin
@ -31,7 +30,7 @@ pub fn read_plugin_file(
let plugin_path = engine_state.plugin_signatures.clone(); let plugin_path = engine_state.plugin_signatures.clone();
if let Some(plugin_path) = plugin_path { if let Some(plugin_path) = plugin_path {
let plugin_filename = plugin_path.to_string_lossy().to_owned(); let plugin_filename = plugin_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&plugin_path) { if let Ok(contents) = std::fs::read(&plugin_path) {
eval_source( eval_source(
@ -39,14 +38,12 @@ pub fn read_plugin_file(
stack, stack,
&contents, &contents,
&plugin_filename, &plugin_filename,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
} }
} }
if is_perf_true { info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
}
} }
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -80,7 +77,7 @@ pub fn eval_config_contents(
stack: &mut Stack, stack: &mut Stack,
) { ) {
if config_path.exists() & config_path.is_file() { if config_path.exists() & config_path.is_file() {
let config_filename = config_path.to_string_lossy().to_owned(); let config_filename = config_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&config_path) { if let Ok(contents) = std::fs::read(&config_path) {
eval_source( eval_source(
@ -88,7 +85,7 @@ pub fn eval_config_contents(
stack, stack,
&contents, &contents,
&config_filename, &config_filename,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
// Merge the environment in case env vars changed in the config // Merge the environment in case env vars changed in the config

View File

@ -2,13 +2,13 @@ use crate::util::{eval_source, report_error};
use log::info; use log::info;
use log::trace; use log::trace;
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_engine::convert_env_values; use nu_engine::{convert_env_values, current_dir};
use nu_parser::parse; use nu_parser::parse;
use nu_protocol::Type; use nu_path::canonicalize_with;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, PipelineData, Span, Value, Config, PipelineData, ShellError, Span, Type, Value,
}; };
use nu_utils::stdout_write_all_and_flush; use nu_utils::stdout_write_all_and_flush;
@ -19,7 +19,6 @@ pub fn evaluate_file(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
input: PipelineData, input: PipelineData,
is_perf_true: bool,
) -> Result<()> { ) -> Result<()> {
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) { if let Some(e) = convert_env_values(engine_state, stack) {
@ -28,12 +27,75 @@ pub fn evaluate_file(
std::process::exit(1); std::process::exit(1);
} }
let file = std::fs::read(&path).into_diagnostic()?; let cwd = current_dir(engine_state, stack)?;
let file_path = {
match canonicalize_with(&path, &cwd) {
Ok(p) => p,
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::FileNotFoundCustom(
format!("Could not access file '{}': {:?}", path, e.to_string()),
Span::unknown(),
),
);
std::process::exit(1);
}
}
};
let file_path_str = match file_path.to_str() {
Some(s) => s,
None => {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::NonUtf8Custom(
format!(
"Input file name '{}' is not valid UTF8",
file_path.to_string_lossy()
),
Span::unknown(),
),
);
std::process::exit(1);
}
};
let file = match std::fs::read(&file_path).into_diagnostic() {
Ok(p) => p,
Err(e) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(
&working_set,
&ShellError::FileNotFoundCustom(
format!(
"Could not read file '{}': {:?}",
file_path_str,
e.to_string()
),
Span::unknown(),
),
);
std::process::exit(1);
}
};
engine_state.start_in_file(Some(file_path_str));
let mut parent = file_path.clone();
parent.pop();
stack.add_env_var(
"FILE_PWD".to_string(),
Value::string(parent.to_string_lossy(), Span::unknown()),
);
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
trace!("parsing file: {}", path); trace!("parsing file: {}", file_path_str);
let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]);
let _ = parse(&mut working_set, Some(&path), &file, false, &[]);
if working_set.find_decl(b"main", &Type::Any).is_some() { if working_set.find_decl(b"main", &Type::Any).is_some() {
let args = format!("main {}", args.join(" ")); let args = format!("main {}", args.join(" "));
@ -42,21 +104,19 @@ pub fn evaluate_file(
engine_state, engine_state,
stack, stack,
&file, &file,
&path, file_path_str,
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
) { ) {
std::process::exit(1); std::process::exit(1);
} }
if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) { if !eval_source(engine_state, stack, args.as_bytes(), "<commandline>", input) {
std::process::exit(1); std::process::exit(1);
} }
} else if !eval_source(engine_state, stack, &file, &path, input) { } else if !eval_source(engine_state, stack, &file, file_path_str, input) {
std::process::exit(1); std::process::exit(1);
} }
if is_perf_true { info!("evaluate {}:{}:{}", file!(), line!(), column!());
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
Ok(()) Ok(())
} }
@ -75,6 +135,14 @@ pub fn print_table_or_error(
// Change the engine_state config to use the passed in configuration // Change the engine_state config to use the passed in configuration
engine_state.set_config(config); engine_state.set_config(config);
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, error);
std::process::exit(1);
}
match engine_state.find_decl("table".as_bytes(), &[]) { match engine_state.find_decl("table".as_bytes(), &[]) {
Some(decl_id) => { Some(decl_id) => {
let command = engine_state.get_decl(decl_id); let command = engine_state.get_decl(decl_id);

View File

@ -372,7 +372,7 @@ impl DescriptionMenu {
let description = self let description = self
.get_value() .get_value()
.and_then(|suggestion| suggestion.description) .and_then(|suggestion| suggestion.description)
.unwrap_or_else(|| "".to_string()) .unwrap_or_default()
.lines() .lines()
.skip(self.skipped_rows) .skip(self.skipped_rows)
.take(self.working_details.description_rows) .take(self.working_details.description_rows)
@ -610,7 +610,7 @@ impl Menu for DescriptionMenu {
let description_rows = self let description_rows = self
.get_value() .get_value()
.and_then(|suggestion| suggestion.description) .and_then(|suggestion| suggestion.description)
.unwrap_or_else(|| "".to_string()) .unwrap_or_default()
.lines() .lines()
.count(); .count();

View File

@ -17,7 +17,7 @@ impl NuHelpCompleter {
//Vec<(Signature, Vec<Example>, bool, bool)> { //Vec<(Signature, Vec<Example>, bool, bool)> {
let mut commands = full_commands let mut commands = full_commands
.iter() .iter()
.filter(|(sig, _, _, _)| { .filter(|(sig, _, _, _, _)| {
sig.name.to_lowercase().contains(&line.to_lowercase()) sig.name.to_lowercase().contains(&line.to_lowercase())
|| sig.usage.to_lowercase().contains(&line.to_lowercase()) || sig.usage.to_lowercase().contains(&line.to_lowercase())
|| sig || sig
@ -31,7 +31,7 @@ impl NuHelpCompleter {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
commands.sort_by(|(a, _, _, _), (b, _, _, _)| { commands.sort_by(|(a, _, _, _, _), (b, _, _, _, _)| {
let a_distance = levenshtein_distance(line, &a.name); let a_distance = levenshtein_distance(line, &a.name);
let b_distance = levenshtein_distance(line, &b.name); let b_distance = levenshtein_distance(line, &b.name);
a_distance.cmp(&b_distance) a_distance.cmp(&b_distance)
@ -39,7 +39,7 @@ impl NuHelpCompleter {
commands commands
.into_iter() .into_iter()
.map(|(sig, examples, _, _)| { .map(|(sig, examples, _, _, _)| {
let mut long_desc = String::new(); let mut long_desc = String::new();
let usage = &sig.usage; let usage = &sig.usage;

View File

@ -42,20 +42,14 @@ impl Completer for NuMenuCompleter {
if let Some(buffer) = block.signature.get_positional(0) { if let Some(buffer) = block.signature.get_positional(0) {
if let Some(buffer_id) = &buffer.var_id { if let Some(buffer_id) = &buffer.var_id {
let line_buffer = Value::String { let line_buffer = Value::string(parsed.remainder, self.span);
val: parsed.remainder.to_string(),
span: self.span,
};
self.stack.add_var(*buffer_id, line_buffer); self.stack.add_var(*buffer_id, line_buffer);
} }
} }
if let Some(position) = block.signature.get_positional(1) { if let Some(position) = block.signature.get_positional(1) {
if let Some(position_id) = &position.var_id { if let Some(position_id) = &position.var_id {
let line_buffer = Value::Int { let line_buffer = Value::int(pos as i64, self.span);
val: pos as i64,
span: self.span,
};
self.stack.add_var(*position_id, line_buffer); self.stack.add_var(*position_id, line_buffer);
} }
} }

View File

@ -50,14 +50,13 @@ Since this command has no output, there is no point in piping it with other comm
let args: Vec<Value> = call.rest(engine_state, stack, 0)?; let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
let no_newline = call.has_flag("no-newline"); let no_newline = call.has_flag("no-newline");
let to_stderr = call.has_flag("stderr"); let to_stderr = call.has_flag("stderr");
let head = call.head;
for arg in args { for arg in args {
arg.into_pipeline_data() arg.into_pipeline_data()
.print(engine_state, stack, no_newline, to_stderr)?; .print(engine_state, stack, no_newline, to_stderr)?;
} }
Ok(PipelineData::new(head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -17,6 +17,7 @@ pub struct NushellPrompt {
default_vi_insert_prompt_indicator: Option<String>, default_vi_insert_prompt_indicator: Option<String>,
default_vi_normal_prompt_indicator: Option<String>, default_vi_normal_prompt_indicator: Option<String>,
default_multiline_indicator: Option<String>, default_multiline_indicator: Option<String>,
render_right_prompt_on_last_line: bool,
} }
impl Default for NushellPrompt { impl Default for NushellPrompt {
@ -34,6 +35,7 @@ impl NushellPrompt {
default_vi_insert_prompt_indicator: None, default_vi_insert_prompt_indicator: None,
default_vi_normal_prompt_indicator: None, default_vi_normal_prompt_indicator: None,
default_multiline_indicator: None, default_multiline_indicator: None,
render_right_prompt_on_last_line: false,
} }
} }
@ -41,8 +43,13 @@ impl NushellPrompt {
self.left_prompt_string = prompt_string; self.left_prompt_string = prompt_string;
} }
pub fn update_prompt_right(&mut self, prompt_string: Option<String>) { pub fn update_prompt_right(
&mut self,
prompt_string: Option<String>,
render_right_prompt_on_last_line: bool,
) {
self.right_prompt_string = prompt_string; self.right_prompt_string = prompt_string;
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
} }
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) { pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
@ -68,6 +75,7 @@ impl NushellPrompt {
prompt_indicator_string: Option<String>, prompt_indicator_string: Option<String>,
prompt_multiline_indicator_string: Option<String>, prompt_multiline_indicator_string: Option<String>,
prompt_vi: (Option<String>, Option<String>), prompt_vi: (Option<String>, Option<String>),
render_right_prompt_on_last_line: bool,
) { ) {
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi; let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
@ -78,6 +86,8 @@ impl NushellPrompt {
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string; self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string; self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
} }
fn default_wrapped_custom_string(&self, str: String) -> String { fn default_wrapped_custom_string(&self, str: String) -> String {
@ -162,4 +172,8 @@ impl Prompt for NushellPrompt {
prefix, history_search.term prefix, history_search.term
)) ))
} }
fn right_prompt_on_last_line(&self) -> bool {
self.render_right_prompt_on_last_line
}
} }

View File

@ -4,7 +4,7 @@ use log::info;
use nu_engine::eval_subexpression; use nu_engine::eval_subexpression;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, PipelineData, Span, Value, Config, PipelineData, Value,
}; };
use reedline::Prompt; use reedline::Prompt;
@ -25,12 +25,11 @@ fn get_prompt_string(
config: &Config, config: &Config,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
is_perf_true: bool,
) -> Option<String> { ) -> Option<String> {
stack stack
.get_env_var(engine_state, prompt) .get_env_var(engine_state, prompt)
.and_then(|v| match v { .and_then(|v| match v {
Value::Block { Value::Closure {
val: block_id, val: block_id,
captures, captures,
.. ..
@ -38,20 +37,34 @@ fn get_prompt_string(
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let mut stack = stack.captures_to_stack(&captures); let mut stack = stack.captures_to_stack(&captures);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt // Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val = eval_subexpression( let ret_val =
engine_state, eval_subexpression(engine_state, &mut stack, block, PipelineData::empty());
&mut stack, info!(
block, "get_prompt_string (block) {}:{}:{}",
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored file!(),
line!(),
column!()
); );
if is_perf_true {
info!( match ret_val {
"get_prompt_string (block) {}:{}:{}", Ok(ret_val) => Some(ret_val),
file!(), Err(err) => {
line!(), let working_set = StateWorkingSet::new(engine_state);
column!() report_error(&working_set, &err);
); None
}
} }
}
Value::Block { val: block_id, .. } => {
let block = engine_state.get_block(block_id);
// Use eval_subexpression to force a redirection of output, so we can use everything in prompt
let ret_val = eval_subexpression(engine_state, stack, block, PipelineData::empty());
info!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
column!()
);
match ret_val { match ret_val {
Ok(ret_val) => Some(ret_val), Ok(ret_val) => Some(ret_val),
@ -90,17 +103,10 @@ pub(crate) fn update_prompt<'prompt>(
engine_state: &EngineState, engine_state: &EngineState,
stack: &Stack, stack: &Stack,
nu_prompt: &'prompt mut NushellPrompt, nu_prompt: &'prompt mut NushellPrompt,
is_perf_true: bool,
) -> &'prompt dyn Prompt { ) -> &'prompt dyn Prompt {
let mut stack = stack.clone(); let mut stack = stack.clone();
let left_prompt_string = get_prompt_string( let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
PROMPT_COMMAND,
config,
engine_state,
&mut stack,
is_perf_true,
);
// Now that we have the prompt string lets ansify it. // Now that we have the prompt string lets ansify it.
// <133 A><prompt><133 B><command><133 C><command output> // <133 A><prompt><133 B><command><133 C><command output>
@ -116,45 +122,20 @@ pub(crate) fn update_prompt<'prompt>(
left_prompt_string left_prompt_string
}; };
let right_prompt_string = get_prompt_string( let right_prompt_string =
PROMPT_COMMAND_RIGHT, get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, &mut stack);
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_indicator_string = get_prompt_string( let prompt_indicator_string =
PROMPT_INDICATOR, get_prompt_string(PROMPT_INDICATOR, config, engine_state, &mut stack);
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_multiline_string = get_prompt_string( let prompt_multiline_string =
PROMPT_MULTILINE_INDICATOR, get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, &mut stack);
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_vi_insert_string = get_prompt_string( let prompt_vi_insert_string =
PROMPT_INDICATOR_VI_INSERT, get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, &mut stack);
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_vi_normal_string = get_prompt_string( let prompt_vi_normal_string =
PROMPT_INDICATOR_VI_NORMAL, get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, &mut stack);
config,
engine_state,
&mut stack,
is_perf_true,
);
// apply the other indicators // apply the other indicators
nu_prompt.update_all_prompt_strings( nu_prompt.update_all_prompt_strings(
@ -163,12 +144,11 @@ pub(crate) fn update_prompt<'prompt>(
prompt_indicator_string, prompt_indicator_string,
prompt_multiline_string, prompt_multiline_string,
(prompt_vi_insert_string, prompt_vi_normal_string), (prompt_vi_insert_string, prompt_vi_normal_string),
config.render_right_prompt_on_last_line,
); );
let ret_val = nu_prompt as &dyn Prompt; let ret_val = nu_prompt as &dyn Prompt;
if is_perf_true { info!("update_prompt {}:{}:{}", file!(), line!(), column!());
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
}
ret_val ret_val
} }

View File

@ -1,11 +1,11 @@
use super::DescriptionMenu; use super::DescriptionMenu;
use crate::{menus::NuMenuCompleter, NuHelpCompleter}; use crate::{menus::NuMenuCompleter, NuHelpCompleter};
use crossterm::event::{KeyCode, KeyModifiers}; use crossterm::event::{KeyCode, KeyModifiers};
use nu_color_config::lookup_ansi_color_style; use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_protocol::{ use nu_protocol::{
color_value_string, create_menus, create_menus,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData, extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
ShellError, Span, Value, ShellError, Span, Value,
@ -114,7 +114,7 @@ pub(crate) fn add_menus(
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?; let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
if let PipelineData::Value(value, None) = res { if let PipelineData::Value(value, None) = res {
for menu in create_menus(&value, config)? { for menu in create_menus(&value)? {
line_editor = line_editor =
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?; add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
} }
@ -159,14 +159,11 @@ macro_rules! add_style {
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => { ($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
$menu = match extract_value($name, $cols, $vals, $span) { $menu = match extract_value($name, $cols, $vals, $span) {
Ok(text) => { Ok(text) => {
let text = match text { let style = match text {
Value::String { val, .. } => val.clone(), Value::String { val, .. } => lookup_ansi_color_style(&val),
Value::Record { cols, vals, span } => { Value::Record { .. } => color_record_to_nustyle(&text),
color_value_string(span, cols, vals, $config).into_string("", $config) _ => lookup_ansi_color_style("green"),
}
_ => "green".to_string(),
}; };
let style = lookup_ansi_color_style(&text);
$f($menu, style) $f($menu, style)
} }
Err(_) => $menu, Err(_) => $menu,
@ -251,7 +248,7 @@ pub(crate) fn add_columnar_menu(
Value::Nothing { .. } => { Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu)))) Ok(line_editor.with_menu(ReedlineMenu::EngineCompleter(Box::new(columnar_menu))))
} }
Value::Block { Value::Closure {
val, val,
captures, captures,
span, span,
@ -337,7 +334,7 @@ pub(crate) fn add_list_menu(
Value::Nothing { .. } => { Value::Nothing { .. } => {
Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu)))) Ok(line_editor.with_menu(ReedlineMenu::HistoryMenu(Box::new(list_menu))))
} }
Value::Block { Value::Closure {
val, val,
captures, captures,
span, span,
@ -459,7 +456,7 @@ pub(crate) fn add_description_menu(
completer, completer,
})) }))
} }
Value::Block { Value::Closure {
val, val,
captures, captures,
span, span,
@ -477,7 +474,7 @@ pub(crate) fn add_description_menu(
})) }))
} }
_ => Err(ShellError::UnsupportedConfigValue( _ => Err(ShellError::UnsupportedConfigValue(
"block or omitted value".to_string(), "closure or omitted value".to_string(),
menu.source.into_abbreviated_string(config), menu.source.into_abbreviated_string(config),
menu.source.span()?, menu.source.span()?,
)), )),
@ -491,7 +488,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
KeyCode::Tab, KeyCode::Tab,
ReedlineEvent::UntilFound(vec![ ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("completion_menu".to_string()), ReedlineEvent::Menu("completion_menu".to_string()),
ReedlineEvent::MenuNext, ReedlineEvent::Edit(vec![EditCommand::Complete]),
]), ]),
); );
@ -822,6 +819,8 @@ fn event_from_record(
"ctrld" => ReedlineEvent::CtrlD, "ctrld" => ReedlineEvent::CtrlD,
"ctrlc" => ReedlineEvent::CtrlC, "ctrlc" => ReedlineEvent::CtrlC,
"enter" => ReedlineEvent::Enter, "enter" => ReedlineEvent::Enter,
"submit" => ReedlineEvent::Submit,
"submitornewline" => ReedlineEvent::SubmitOrNewline,
"esc" | "escape" => ReedlineEvent::Esc, "esc" | "escape" => ReedlineEvent::Esc,
"up" => ReedlineEvent::Up, "up" => ReedlineEvent::Up,
"down" => ReedlineEvent::Down, "down" => ReedlineEvent::Down,
@ -962,6 +961,7 @@ fn edit_from_record(
let char = extract_char(value, config)?; let char = extract_char(value, config)?;
EditCommand::MoveLeftBefore(char) EditCommand::MoveLeftBefore(char)
} }
"complete" => EditCommand::Complete,
e => { e => {
return Err(ShellError::UnsupportedConfigValue( return Err(ShellError::UnsupportedConfigValue(
"reedline EditCommand".to_string(), "reedline EditCommand".to_string(),
@ -990,10 +990,7 @@ mod test {
#[test] #[test]
fn test_send_event() { fn test_send_event() {
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::string("Enter", Span::test_data())];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap(); let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1013,10 +1010,7 @@ mod test {
#[test] #[test]
fn test_edit_event() { fn test_edit_event() {
let cols = vec!["edit".to_string()]; let cols = vec!["edit".to_string()];
let vals = vec![Value::String { let vals = vec![Value::string("Clear", Span::test_data())];
val: "Clear".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span).unwrap(); let b = EventType::try_from_columns(&cols, &vals, &span).unwrap();
@ -1040,14 +1034,8 @@ mod test {
fn test_send_menu() { fn test_send_menu() {
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::string("Menu", Span::test_data()),
val: "Menu".to_string(), Value::string("history_menu", Span::test_data()),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let span = Span::test_data(); let span = Span::test_data();
@ -1073,14 +1061,8 @@ mod test {
// Menu event // Menu event
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::string("Menu", Span::test_data()),
val: "Menu".to_string(), Value::string("history_menu", Span::test_data()),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let menu_event = Value::Record { let menu_event = Value::Record {
@ -1091,10 +1073,7 @@ mod test {
// Enter event // Enter event
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::string("Enter", Span::test_data())];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let enter_event = Value::Record { let enter_event = Value::Record {
cols, cols,
@ -1135,14 +1114,8 @@ mod test {
// Menu event // Menu event
let cols = vec!["send".to_string(), "name".to_string()]; let cols = vec!["send".to_string(), "name".to_string()];
let vals = vec![ let vals = vec![
Value::String { Value::string("Menu", Span::test_data()),
val: "Menu".to_string(), Value::string("history_menu", Span::test_data()),
span: Span::test_data(),
},
Value::String {
val: "history_menu".to_string(),
span: Span::test_data(),
},
]; ];
let menu_event = Value::Record { let menu_event = Value::Record {
@ -1153,10 +1126,7 @@ mod test {
// Enter event // Enter event
let cols = vec!["send".to_string()]; let cols = vec!["send".to_string()];
let vals = vec![Value::String { let vals = vec![Value::string("Enter", Span::test_data())];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let enter_event = Value::Record { let enter_event = Value::Record {
cols, cols,
@ -1184,10 +1154,7 @@ mod test {
#[test] #[test]
fn test_error() { fn test_error() {
let cols = vec!["not_exist".to_string()]; let cols = vec!["not_exist".to_string()];
let vals = vec![Value::String { let vals = vec![Value::string("Enter", Span::test_data())];
val: "Enter".to_string(),
span: Span::test_data(),
}];
let span = Span::test_data(); let span = Span::test_data();
let b = EventType::try_from_columns(&cols, &vals, &span); let b = EventType::try_from_columns(&cols, &vals, &span);

View File

@ -5,13 +5,11 @@ use crate::{
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new}, util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
NuHighlighter, NuValidator, NushellPrompt, NuHighlighter, NuValidator, NushellPrompt,
}; };
use fancy_regex::Regex;
use lazy_static::lazy_static;
use log::{info, trace, warn}; use log::{info, trace, warn};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_color_config::get_color_config; use nu_color_config::StyleComputer;
use nu_engine::{convert_env_values, eval_block}; use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
use nu_parser::{lex, parse}; use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{ use nu_protocol::{
ast::PathMember, ast::PathMember,
engine::{EngineState, ReplOperation, Stack, StateWorkingSet}, engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
@ -24,7 +22,6 @@ use std::{
sync::atomic::Ordering, sync::atomic::Ordering,
time::Instant, time::Instant,
}; };
use strip_ansi_escapes::strip;
use sysinfo::SystemExt; use sysinfo::SystemExt;
// According to Daniel Imms @Tyriar, we need to do these this way: // According to Daniel Imms @Tyriar, we need to do these this way:
@ -41,7 +38,6 @@ pub fn evaluate_repl(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
nushell_path: &str, nushell_path: &str,
is_perf_true: bool,
prerun_command: Option<Spanned<String>>, prerun_command: Option<Spanned<String>>,
) -> Result<()> { ) -> Result<()> {
use reedline::{FileBackedHistory, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
@ -51,7 +47,7 @@ pub fn evaluate_repl(
if !atty::is(atty::Stream::Stdin) { if !atty::is(atty::Stream::Stdin) {
return Err(std::io::Error::new( return Err(std::io::Error::new(
std::io::ErrorKind::NotFound, std::io::ErrorKind::NotFound,
"Nushell launched as interactive REPL but STDIN is not a TTY, either launch in a valid terminal or provide arguments to invoke a script!", "Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
)) ))
.into_diagnostic(); .into_diagnostic();
} }
@ -60,14 +56,12 @@ pub fn evaluate_repl(
let mut nu_prompt = NushellPrompt::new(); let mut nu_prompt = NushellPrompt::new();
if is_perf_true { info!(
info!( "translate environment vars {}:{}:{}",
"translate environment vars {}:{}:{}", file!(),
file!(), line!(),
line!(), column!()
column!() );
);
}
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) { if let Some(e) = convert_env_values(engine_state, stack) {
@ -78,32 +72,19 @@ pub fn evaluate_repl(
// seed env vars // seed env vars
stack.add_env_var( stack.add_env_var(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::string("0823", Span::unknown()),
val: "0823".to_string(),
span: Span { start: 0, end: 0 },
},
); );
stack.add_env_var( stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
"LAST_EXIT_CODE".into(),
Value::Int { info!(
val: 0, "load config initially {}:{}:{}",
span: Span { start: 0, end: 0 }, file!(),
}, line!(),
column!()
); );
if is_perf_true { info!("setup reedline {}:{}:{}", file!(), line!(), column!());
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
}
if is_perf_true {
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
}
let mut line_editor = Reedline::create(); let mut line_editor = Reedline::create();
@ -121,9 +102,7 @@ pub fn evaluate_repl(
engine_state.config.history_file_format, engine_state.config.history_file_format,
); );
if let Some(history_path) = history_path.as_deref() { if let Some(history_path) = history_path.as_deref() {
if is_perf_true { info!("setup history {}:{}:{}", file!(), line!(), column!());
info!("setup history {}:{}:{}", file!(), line!(), column!());
}
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format { let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
HistoryFileFormat::PlainText => Box::new( HistoryFileFormat::PlainText => Box::new(
@ -149,15 +128,7 @@ pub fn evaluate_repl(
if use_ansi { if use_ansi {
println!("{}", banner); println!("{}", banner);
} else { } else {
let stripped_string = { println!("{}", nu_utils::strip_ansi_string_likely(banner));
if let Ok(bytes) = strip(&banner) {
String::from_utf8_lossy(&bytes).to_string()
} else {
banner
}
};
println!("{}", stripped_string);
} }
} }
@ -167,20 +138,18 @@ pub fn evaluate_repl(
stack, stack,
s.item.as_bytes(), s.item.as_bytes(),
&format!("entry #{}", entry_num), &format!("entry #{}", entry_num),
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?; engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
} }
loop { loop {
if is_perf_true { info!(
info!( "load config each loop {}:{}:{}",
"load config each loop {}:{}:{}", file!(),
file!(), line!(),
line!(), column!()
column!() );
);
}
let cwd = get_guaranteed_cwd(engine_state, stack); let cwd = get_guaranteed_cwd(engine_state, stack);
@ -201,15 +170,9 @@ pub fn evaluate_repl(
let config = engine_state.get_config(); let config = engine_state.get_config();
if is_perf_true { info!("setup colors {}:{}:{}", file!(), line!(), column!());
info!("setup colors {}:{}:{}", file!(), line!(), column!());
}
let color_hm = get_color_config(config); info!("update reedline {}:{}:{}", file!(), line!(), column!());
if is_perf_true {
info!("update reedline {}:{}:{}", file!(), line!(), column!());
}
let engine_reference = std::sync::Arc::new(engine_state.clone()); let engine_reference = std::sync::Arc::new(engine_state.clone());
line_editor = line_editor line_editor = line_editor
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
@ -227,10 +190,14 @@ pub fn evaluate_repl(
.with_partial_completions(config.partial_completions) .with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring); .with_ansi_colors(config.use_ansi_coloring);
let style_computer = StyleComputer::from_config(engine_state, stack);
line_editor = if config.use_ansi_coloring { line_editor = if config.use_ansi_coloring {
line_editor.with_hinter(Box::new( line_editor.with_hinter(Box::new({
DefaultHinter::default().with_style(color_hm["hints"]), // As of Nov 2022, "hints" color_config closures only get `null` passed in.
)) let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
DefaultHinter::default().with_style(style)
}))
} else { } else {
line_editor.disable_hints() line_editor.disable_hints()
}; };
@ -266,18 +233,14 @@ pub fn evaluate_repl(
}; };
if config.sync_history_on_enter { if config.sync_history_on_enter {
if is_perf_true { info!("sync history {}:{}:{}", file!(), line!(), column!());
info!("sync history {}:{}:{}", file!(), line!(), column!());
}
if let Err(e) = line_editor.sync_history() { if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e); warn!("Failed to sync history: {}", e);
} }
} }
if is_perf_true { info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
}
// Changing the line editor based on the found keybindings // Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) { line_editor = match create_keybindings(config) {
@ -301,14 +264,12 @@ pub fn evaluate_repl(
} }
}; };
if is_perf_true { info!("prompt_update {}:{}:{}", file!(), line!(), column!());
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
}
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook // fire the "pre_prompt" hook
if let Some(hook) = config.hooks.pre_prompt.clone() { if let Some(hook) = config.hooks.pre_prompt.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) { if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
} }
@ -323,19 +284,16 @@ pub fn evaluate_repl(
} }
let config = engine_state.get_config(); let config = engine_state.get_config();
let prompt = let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
entry_num += 1; entry_num += 1;
if is_perf_true { info!(
info!( "finished setup, starting repl {}:{}:{}",
"finished setup, starting repl {}:{}:{}", file!(),
file!(), line!(),
line!(), column!()
column!() );
);
}
let input = line_editor.read_line(prompt); let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration; let shell_integration = config.shell_integration;
@ -367,7 +325,7 @@ pub fn evaluate_repl(
// Right before we start running the code the user gave us, // Right before we start running the code the user gave us,
// fire the "pre_execution" hook // fire the "pre_execution" hook
if let Some(hook) = config.hooks.pre_execution.clone() { if let Some(hook) = config.hooks.pre_execution.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) { if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
} }
@ -380,9 +338,13 @@ pub fn evaluate_repl(
let tokens = lex(s.as_bytes(), 0, &[], &[], false); let tokens = lex(s.as_bytes(), 0, &[], &[], false);
// Check if this is a single call to a directory, if so auto-cd // Check if this is a single call to a directory, if so auto-cd
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?; let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
let path = nu_path::expand_path_with(&s, &cwd);
let orig = s.clone(); let mut orig = s.clone();
if orig.starts_with('`') {
orig = trim_quotes_str(&orig).to_string()
}
let path = nu_path::expand_path_with(&orig, &cwd);
if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 { if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 {
// We have an auto-cd // We have an auto-cd
@ -404,7 +366,7 @@ pub fn evaluate_repl(
"OLDPWD".into(), "OLDPWD".into(),
Value::String { Value::String {
val: cwd.clone(), val: cwd.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -414,7 +376,7 @@ pub fn evaluate_repl(
"PWD".into(), "PWD".into(),
Value::String { Value::String {
val: path.clone(), val: path.clone(),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
let cwd = Value::String { val: cwd, span }; let cwd = Value::String { val: cwd, span };
@ -452,7 +414,7 @@ pub fn evaluate_repl(
span, span,
}, },
); );
} else { } else if !s.trim().is_empty() {
trace!("eval source: {}", s); trace!("eval source: {}", s);
eval_source( eval_source(
@ -460,7 +422,7 @@ pub fn evaluate_repl(
stack, stack,
s.as_bytes(), s.as_bytes(),
&format!("entry #{}", entry_num), &format!("entry #{}", entry_num),
PipelineData::new(Span::new(0, 0)), PipelineData::empty(),
); );
} }
let cmd_duration = start_time.elapsed(); let cmd_duration = start_time.elapsed();
@ -469,7 +431,7 @@ pub fn evaluate_repl(
"CMD_DURATION_MS".into(), "CMD_DURATION_MS".into(),
Value::String { Value::String {
val: format!("{}", cmd_duration.as_millis()), val: format!("{}", cmd_duration.as_millis()),
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
); );
@ -557,7 +519,7 @@ pub fn evaluate_repl(
Err(err) => { Err(err) => {
let message = err.to_string(); let message = err.to_string();
if !message.contains("duration") { if !message.contains("duration") {
println!("Error: {:?}", err); eprintln!("Error: {:?}", err);
// TODO: Identify possible error cases where a hard failure is preferable // TODO: Identify possible error cases where a hard failure is preferable
// Ignoring and reporting could hide bigger problems // Ignoring and reporting could hide bigger problems
// e.g. https://github.com/nushell/nushell/issues/6452 // e.g. https://github.com/nushell/nushell/issues/6452
@ -578,7 +540,7 @@ fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
engine_state, engine_state,
stack, stack,
None, None,
"(date now) - ('05/10/2019' | into datetime)", "(date now) - ('2019-05-10 09:59:12-0700' | into datetime)",
) { ) {
Ok(Value::Duration { val, .. }) => format_duration(val), Ok(Value::Duration { val, .. }) => format_duration(val),
_ => "".to_string(), _ => "".to_string(),
@ -595,13 +557,13 @@ Our {}GitHub{} repository is at {}https://github.com/nushell/nushell{}
Our {}Documentation{} is located at {}http://nushell.sh{} Our {}Documentation{} is located at {}http://nushell.sh{}
{}Tweet{} us at {}@nu_shell{} {}Tweet{} us at {}@nu_shell{}
{}Nushell{} has been around for: It's been this long since {}Nushell{}'s first commit:
{} {}
{}You can disable this banner using the {}config nu{}{} command {}You can disable this banner using the {}config nu{}{} command
to modify the config.nu file and setting show_banner to false. to modify the config.nu file and setting show_banner to false.
let-env config {{ let-env config = {{
show_banner: false show_banner: false
... ...
}}{} }}{}
@ -666,7 +628,7 @@ pub fn eval_string_with_input(
let input_as_pipeline_data = match input { let input_as_pipeline_data = match input {
Some(input) => PipelineData::Value(input, None), Some(input) => PipelineData::Value(input, None),
None => PipelineData::new(Span::test_data()), None => PipelineData::empty(),
}; };
eval_block( eval_block(
@ -715,6 +677,7 @@ pub fn eval_env_change_hook(
eval_hook( eval_hook(
engine_state, engine_state,
stack, stack,
None,
vec![("$before".into(), before), ("$after".into(), after.clone())], vec![("$before".into(), before), ("$after".into(), after.clone())],
hook_value, hook_value,
)?; )?;
@ -740,15 +703,24 @@ pub fn eval_env_change_hook(
pub fn eval_hook( pub fn eval_hook(
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
input: Option<PipelineData>,
arguments: Vec<(String, Value)>, arguments: Vec<(String, Value)>,
value: &Value, value: &Value,
) -> Result<(), ShellError> { ) -> Result<PipelineData, ShellError> {
let value_span = value.span()?; let value_span = value.span()?;
// Hooks can optionally be a record in this form:
// {
// condition: {|before, after| ... } # block that evaluates to true/false
// code: # block or a string
// }
// The condition block will be run to check whether the main hook (in `code`) should be run.
// If it returns true (the default if a condition block is not specified), the hook should be run.
let condition_path = PathMember::String { let condition_path = PathMember::String {
val: "condition".to_string(), val: "condition".to_string(),
span: value_span, span: value_span,
}; };
let mut output = PipelineData::empty();
let code_path = PathMember::String { let code_path = PathMember::String {
val: "code".to_string(), val: "code".to_string(),
@ -758,7 +730,7 @@ pub fn eval_hook(
match value { match value {
Value::List { vals, .. } => { Value::List { vals, .. } => {
for val in vals { for val in vals {
eval_hook(engine_state, stack, arguments.clone(), val)? eval_hook(engine_state, stack, None, arguments.clone(), val)?;
} }
} }
Value::Record { .. } => { Value::Record { .. } => {
@ -769,24 +741,33 @@ pub fn eval_hook(
val: block_id, val: block_id,
span: block_span, span: block_span,
.. ..
}
| Value::Closure {
val: block_id,
span: block_span,
..
} => { } => {
match run_hook_block( match run_hook_block(
engine_state, engine_state,
stack, stack,
block_id, block_id,
None,
arguments.clone(), arguments.clone(),
block_span, block_span,
) { ) {
Ok(value) => match value { Ok(pipeline_data) => {
Value::Bool { val, .. } => val, if let PipelineData::Value(Value::Bool { val, .. }, ..) =
other => { pipeline_data
{
val
} else {
return Err(ShellError::UnsupportedConfigValue( return Err(ShellError::UnsupportedConfigValue(
"boolean output".to_string(), "boolean output".to_string(),
format!("{}", other.get_type()), "other PipelineData variant".to_string(),
other.span()?, block_span,
)); ));
} }
}, }
Err(err) => { Err(err) => {
return Err(err); return Err(err);
} }
@ -821,6 +802,7 @@ pub fn eval_hook(
name.as_bytes().to_vec(), name.as_bytes().to_vec(),
val.span()?, val.span()?,
Type::Any, Type::Any,
false,
); );
vars.push((var_id, val)); vars.push((var_id, val));
@ -842,7 +824,7 @@ pub fn eval_hook(
}; };
engine_state.merge_delta(delta)?; engine_state.merge_delta(delta)?;
let input = PipelineData::new(value_span); let input = PipelineData::empty();
let var_ids: Vec<VarId> = vars let var_ids: Vec<VarId> = vars
.into_iter() .into_iter()
@ -853,7 +835,9 @@ pub fn eval_hook(
.collect(); .collect();
match eval_block(engine_state, stack, &block, input, false, false) { match eval_block(engine_state, stack, &block, input, false, false) {
Ok(_) => {} Ok(pipeline_data) => {
output = pipeline_data;
}
Err(err) => { Err(err) => {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
@ -868,7 +852,28 @@ pub fn eval_hook(
span: block_span, span: block_span,
.. ..
} => { } => {
run_hook_block(engine_state, stack, block_id, arguments, block_span)?; run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
} }
other => { other => {
return Err(ShellError::UnsupportedConfigValue( return Err(ShellError::UnsupportedConfigValue(
@ -885,7 +890,28 @@ pub fn eval_hook(
span: block_span, span: block_span,
.. ..
} => { } => {
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?; output = run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?;
}
Value::Closure {
val: block_id,
span: block_span,
..
} => {
output = run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?;
} }
other => { other => {
return Err(ShellError::UnsupportedConfigValue( return Err(ShellError::UnsupportedConfigValue(
@ -899,19 +925,20 @@ pub fn eval_hook(
let cwd = get_guaranteed_cwd(engine_state, stack); let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?; engine_state.merge_env(stack, cwd)?;
Ok(()) Ok(output)
} }
pub fn run_hook_block( fn run_hook_block(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
block_id: BlockId, block_id: BlockId,
optional_input: Option<PipelineData>,
arguments: Vec<(String, Value)>, arguments: Vec<(String, Value)>,
span: Span, span: Span,
) -> Result<Value, ShellError> { ) -> Result<PipelineData, ShellError> {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let input = PipelineData::new(span); let input = optional_input.unwrap_or_else(PipelineData::empty);
let mut callee_stack = stack.gather_captures(&block.captures); let mut callee_stack = stack.gather_captures(&block.captures);
@ -930,29 +957,30 @@ pub fn run_hook_block(
} }
} }
match eval_block(engine_state, &mut callee_stack, block, input, false, false) { match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)
Ok(pipeline_data) => match pipeline_data.into_value(span) { {
Value::Error { error } => Err(error), Ok(pipeline_data) => {
val => { if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
// If all went fine, preserve the environment of the called block return Err(error);
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(val)
} }
},
// If all went fine, preserve the environment of the called block
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(pipeline_data)
}
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
@ -964,7 +992,7 @@ fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
return Err(ShellError::GenericError( return Err(ShellError::GenericError(
"Error writing ansi sequence".into(), "Error writing ansi sequence".into(),
err.to_string(), err.to_string(),
Some(Span { start: 0, end: 0 }), Some(Span::unknown()),
None, None,
Vec::new(), Vec::new(),
)); ));
@ -974,18 +1002,19 @@ fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
ShellError::GenericError( ShellError::GenericError(
"Error flushing stdio".into(), "Error flushing stdio".into(),
e.to_string(), e.to_string(),
Some(Span { start: 0, end: 0 }), Some(Span::unknown()),
None, None,
Vec::new(), Vec::new(),
) )
}) })
} }
lazy_static! { // Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo'
// Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo' #[cfg(windows)]
static ref DRIVE_PATH_REGEX: Regex = static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =
Regex::new(r"^[a-zA-Z]:[/\\]?").expect("Internal error: regex creation"); once_cell::sync::Lazy::new(|| {
} fancy_regex::Regex::new(r"^[a-zA-Z]:[/\\]?").expect("Internal error: regex creation")
});
// A best-effort "does this string look kinda like a path?" function to determine whether to auto-cd // A best-effort "does this string look kinda like a path?" function to determine whether to auto-cd
fn looks_like_path(orig: &str) -> bool { fn looks_like_path(orig: &str) -> bool {

View File

@ -1,9 +1,10 @@
use log::trace; use log::trace;
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_color_config::get_shape_color; use nu_color_config::{get_matching_brackets_style, get_shape_color};
use nu_parser::{flatten_block, parse, FlatShape}; use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
use nu_protocol::engine::{EngineState, StateWorkingSet}; use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::Config; use nu_protocol::{Config, Span};
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
pub struct NuHighlighter { pub struct NuHighlighter {
@ -15,10 +16,12 @@ impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> StyledText { fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line); trace!("highlighting: {}", line);
let (shapes, global_span_offset) = { let mut working_set = StateWorkingSet::new(&self.engine_state);
let mut working_set = StateWorkingSet::new(&self.engine_state); let block = {
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]); let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
block
};
let (shapes, global_span_offset) = {
let shapes = flatten_block(&working_set, &block); let shapes = flatten_block(&working_set, &block);
(shapes, self.engine_state.next_span_start()) (shapes, self.engine_state.next_span_start())
}; };
@ -26,6 +29,15 @@ impl Highlighter for NuHighlighter {
let mut output = StyledText::default(); let mut output = StyledText::default();
let mut last_seen_span = global_span_offset; let mut last_seen_span = global_span_offset;
let global_cursor_offset = _cursor + global_span_offset;
let matching_brackets_pos = find_matching_brackets(
line,
&working_set,
&block,
global_span_offset,
global_cursor_offset,
);
for shape in &shapes { for shape in &shapes {
if shape.0.end <= last_seen_span if shape.0.end <= last_seen_span
|| last_seen_span < global_span_offset || last_seen_span < global_span_offset
@ -44,166 +56,75 @@ impl Highlighter for NuHighlighter {
let next_token = line let next_token = line
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)] [(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string(); .to_string();
macro_rules! add_colored_token_with_bracket_highlight {
($shape:expr, $span:expr, $text:expr) => {{
let spans = split_span_by_highlight_positions(
line,
&$span,
&matching_brackets_pos,
global_span_offset,
);
spans.iter().for_each(|(part, highlight)| {
let start = part.start - $span.start;
let end = part.end - $span.start;
let text = (&next_token[start..end]).to_string();
let mut style = get_shape_color($shape.to_string(), &self.config);
if *highlight {
style = get_matching_brackets_style(style, &self.config);
}
output.push((style, text));
});
}};
}
macro_rules! add_colored_token {
($shape:expr, $text:expr) => {
output.push((get_shape_color($shape.to_string(), &self.config), $text))
};
}
match shape.1 { match shape.1 {
FlatShape::Garbage => output.push(( FlatShape::Garbage => add_colored_token!(shape.1, next_token),
// nushell Garbage FlatShape::Nothing => add_colored_token!(shape.1, next_token),
get_shape_color(shape.1.to_string(), &self.config), FlatShape::Binary => add_colored_token!(shape.1, next_token),
next_token, FlatShape::Bool => add_colored_token!(shape.1, next_token),
)), FlatShape::Int => add_colored_token!(shape.1, next_token),
FlatShape::Nothing => output.push(( FlatShape::Float => add_colored_token!(shape.1, next_token),
// nushell Nothing FlatShape::Range => add_colored_token!(shape.1, next_token),
get_shape_color(shape.1.to_string(), &self.config), FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
next_token, FlatShape::External => add_colored_token!(shape.1, next_token),
)), FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
FlatShape::Binary => { FlatShape::Literal => add_colored_token!(shape.1, next_token),
// nushell ? FlatShape::Operator => add_colored_token!(shape.1, next_token),
output.push(( FlatShape::Signature => add_colored_token!(shape.1, next_token),
get_shape_color(shape.1.to_string(), &self.config), FlatShape::String => add_colored_token!(shape.1, next_token),
next_token, FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
)) FlatShape::DateTime => add_colored_token!(shape.1, next_token),
}
FlatShape::Bool => {
// nushell ?
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Int => {
// nushell Int
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Float => {
// nushell Decimal
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Range => output.push((
// nushell DotDot ?
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::InternalCall => output.push((
// nushell InternalCommand
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::External => {
// nushell ExternalCommand
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::ExternalArg => {
// nushell ExternalWord
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Literal => {
// nushell ?
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Operator => output.push((
// nushell Operator
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Signature => output.push((
// nushell ?
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::String => {
// nushell String
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::StringInterpolation => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::DateTime => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::List => { FlatShape::List => {
// nushell ??? add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
} }
FlatShape::Table => { FlatShape::Table => {
// nushell ??? add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
} }
FlatShape::Record => { FlatShape::Record => {
// nushell ??? add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
} }
FlatShape::Block => { FlatShape::Block => {
// nushell ??? add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
} }
FlatShape::Filepath => output.push((
// nushell Path FlatShape::Filepath => add_colored_token!(shape.1, next_token),
get_shape_color(shape.1.to_string(), &self.config), FlatShape::Directory => add_colored_token!(shape.1, next_token),
next_token, FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
)), FlatShape::Variable => add_colored_token!(shape.1, next_token),
FlatShape::Directory => output.push(( FlatShape::Flag => add_colored_token!(shape.1, next_token),
// nushell Directory FlatShape::Pipe => add_colored_token!(shape.1, next_token),
get_shape_color(shape.1.to_string(), &self.config), FlatShape::And => add_colored_token!(shape.1, next_token),
next_token, FlatShape::Or => add_colored_token!(shape.1, next_token),
)), FlatShape::Redirection => add_colored_token!(shape.1, next_token),
FlatShape::GlobPattern => output.push(( FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
// nushell GlobPattern
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Variable => output.push((
// nushell Variable
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Flag => {
// nushell Flag
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Custom(..) => output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
} }
last_seen_span = shape.0.end; last_seen_span = shape.0.end;
} }
@ -216,3 +137,299 @@ impl Highlighter for NuHighlighter {
output output
} }
} }
fn split_span_by_highlight_positions(
line: &str,
span: &Span,
highlight_positions: &Vec<usize>,
global_span_offset: usize,
) -> Vec<(Span, bool)> {
let mut start = span.start;
let mut result: Vec<(Span, bool)> = Vec::new();
for pos in highlight_positions {
if start <= *pos && pos < &span.end {
if start < *pos {
result.push((Span::new(start, *pos), false));
}
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
let end = span_str
.chars()
.next()
.map(|c| pos + get_char_length(c))
.unwrap_or(pos + 1);
result.push((Span::new(*pos, end), true));
start = end;
}
}
if start < span.end {
result.push((Span::new(start, span.end), false));
}
result
}
fn find_matching_brackets(
line: &str,
working_set: &StateWorkingSet,
block: &Block,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Vec<usize> {
const BRACKETS: &str = "{}[]()";
// calculate first bracket position
let global_end_offset = line.len() + global_span_offset;
let global_bracket_pos =
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
// cursor is at the end of a non-empty string -- find block end at the previous position
if let Some(last_char) = line.chars().last() {
global_cursor_offset - get_char_length(last_char)
} else {
global_cursor_offset
}
} else {
// cursor is in the middle of a string -- find block end at the current position
global_cursor_offset
};
// check that position contains bracket
let match_idx = global_bracket_pos - global_span_offset;
if match_idx >= line.len()
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
{
return Vec::new();
}
// find matching bracket by finding matching block end
let matching_block_end = find_matching_block_end_in_block(
line,
working_set,
block,
global_span_offset,
global_bracket_pos,
);
if let Some(pos) = matching_block_end {
let matching_idx = pos - global_span_offset;
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
return if global_bracket_pos < pos {
vec![global_bracket_pos, pos]
} else {
vec![pos, global_bracket_pos]
};
}
}
Vec::new()
}
fn find_matching_block_end_in_block(
line: &str,
working_set: &StateWorkingSet,
block: &Block,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Option<usize> {
for p in &block.pipelines {
for e in &p.elements {
match e {
PipelineElement::Expression(_, e)
| PipelineElement::Redirection(_, _, e)
| PipelineElement::And(_, e)
| PipelineElement::Or(_, e) => {
if e.span.contains(global_cursor_offset) {
if let Some(pos) = find_matching_block_end_in_expr(
line,
working_set,
e,
global_span_offset,
global_cursor_offset,
) {
return Some(pos);
}
}
}
}
}
}
None
}
fn find_matching_block_end_in_expr(
line: &str,
working_set: &StateWorkingSet,
expression: &Expression,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Option<usize> {
macro_rules! find_in_expr_or_continue {
($inner_expr:ident) => {
if let Some(pos) = find_matching_block_end_in_expr(
line,
working_set,
$inner_expr,
global_span_offset,
global_cursor_offset,
) {
return Some(pos);
}
};
}
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
{
let expr_first = expression.span.start;
let span_str = &line
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
let expr_last = span_str
.chars()
.last()
.map(|c| expression.span.end - get_char_length(c))
.unwrap_or(expression.span.start);
return match &expression.expr {
Expr::Bool(_) => None,
Expr::Int(_) => None,
Expr::Float(_) => None,
Expr::Binary(_) => None,
Expr::Range(..) => None,
Expr::Var(_) => None,
Expr::VarDecl(_) => None,
Expr::ExternalCall(..) => None,
Expr::Operator(_) => None,
Expr::UnaryNot(_) => None,
Expr::Keyword(..) => None,
Expr::ValueWithUnit(..) => None,
Expr::DateTime(_) => None,
Expr::Filepath(_) => None,
Expr::Directory(_) => None,
Expr::GlobPattern(_) => None,
Expr::String(_) => None,
Expr::CellPath(_) => None,
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
Expr::Signature(_) => None,
Expr::Nothing => None,
Expr::Garbage => None,
Expr::Table(hdr, rows) => {
if expr_last == global_cursor_offset {
// cursor is at table end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at table start
Some(expr_last)
} else {
// cursor is inside table
for inner_expr in hdr {
find_in_expr_or_continue!(inner_expr);
}
for row in rows {
for inner_expr in row {
find_in_expr_or_continue!(inner_expr);
}
}
None
}
}
Expr::Record(exprs) => {
if expr_last == global_cursor_offset {
// cursor is at record end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at record start
Some(expr_last)
} else {
// cursor is inside record
for (k, v) in exprs {
find_in_expr_or_continue!(k);
find_in_expr_or_continue!(v);
}
None
}
}
Expr::Call(call) => {
for arg in &call.arguments {
let opt_expr = match arg {
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr),
};
if let Some(inner_expr) = opt_expr {
find_in_expr_or_continue!(inner_expr);
}
}
None
}
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
line,
working_set,
&b.head,
global_span_offset,
global_cursor_offset,
),
Expr::BinaryOp(lhs, op, rhs) => {
find_in_expr_or_continue!(lhs);
find_in_expr_or_continue!(op);
find_in_expr_or_continue!(rhs);
None
}
Expr::Block(block_id)
| Expr::Closure(block_id)
| Expr::RowCondition(block_id)
| Expr::Subexpression(block_id) => {
if expr_last == global_cursor_offset {
// cursor is at block end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at block start
Some(expr_last)
} else {
// cursor is inside block
let nested_block = working_set.get_block(*block_id);
find_matching_block_end_in_block(
line,
working_set,
nested_block,
global_span_offset,
global_cursor_offset,
)
}
}
Expr::StringInterpolation(inner_expr) => {
for inner_expr in inner_expr {
find_in_expr_or_continue!(inner_expr);
}
None
}
Expr::List(inner_expr) => {
if expr_last == global_cursor_offset {
// cursor is at list end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at list start
Some(expr_last)
} else {
// cursor is inside list
for inner_expr in inner_expr {
find_in_expr_or_continue!(inner_expr);
}
None
}
}
};
}
None
}
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
s[index..].chars().next()
}
fn get_char_length(c: char) -> usize {
c.to_string().len()
}

View File

@ -1,11 +1,11 @@
use log::trace; use crate::repl::eval_hook;
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents}; use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
use nu_protocol::engine::StateWorkingSet; use nu_protocol::engine::StateWorkingSet;
use nu_protocol::CliError; use nu_protocol::CliError;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, ShellError, Span, Value, print_if_stream, PipelineData, ShellError, Span, Value,
}; };
#[cfg(windows)] #[cfg(windows)]
use nu_utils::enable_vt_processing; use nu_utils::enable_vt_processing;
@ -204,8 +204,6 @@ pub fn eval_source(
fname: &str, fname: &str,
input: PipelineData, input: PipelineData,
) -> bool { ) -> bool {
trace!("eval_source");
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse( let (output, err) = parse(
@ -231,23 +229,41 @@ pub fn eval_source(
} }
match eval_block(engine_state, stack, &block, input, false, false) { match eval_block(engine_state, stack, &block, input, false, false) {
Ok(mut pipeline_data) => { Ok(pipeline_data) => {
if let PipelineData::ExternalStream { exit_code, .. } = &mut pipeline_data { let config = engine_state.get_config();
if let Some(exit_code) = exit_code.take().and_then(|it| it.last()) { let result;
stack.add_env_var("LAST_EXIT_CODE".to_string(), exit_code); if let PipelineData::ExternalStream {
} else { stdout: stream,
set_last_exit_code(stack, 0); stderr: stderr_stream,
exit_code,
..
} = pipeline_data
{
result = print_if_stream(stream, stderr_stream, false, exit_code);
} else if let Some(hook) = config.hooks.display_output.clone() {
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
Err(err) => {
result = Err(err);
}
Ok(val) => {
result = val.print(engine_state, stack, false, false);
}
} }
} else { } else {
set_last_exit_code(stack, 0); result = pipeline_data.print(engine_state, stack, true, false);
} }
if let Err(err) = pipeline_data.print(engine_state, stack, false, false) { match result {
let working_set = StateWorkingSet::new(engine_state); Err(err) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err); report_error(&working_set, &err);
return false; return false;
}
Ok(exit_code) => {
set_last_exit_code(stack, exit_code);
}
} }
// reset vt processing, aka ansi because illbehaved externals can break it // reset vt processing, aka ansi because illbehaved externals can break it
@ -273,10 +289,7 @@ pub fn eval_source(
fn set_last_exit_code(stack: &mut Stack, exit_code: i64) { fn set_last_exit_code(stack: &mut Stack, exit_code: i64) {
stack.add_env_var( stack.add_env_var(
"LAST_EXIT_CODE".to_string(), "LAST_EXIT_CODE".to_string(),
Value::Int { Value::int(exit_code, Span::unknown()),
val: exit_code,
span: Span { start: 0, end: 0 },
},
); );
} }

View File

@ -5,7 +5,7 @@ use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet; use nu_protocol::engine::StateWorkingSet;
use reedline::{Completer, Suggestion}; use reedline::{Completer, Suggestion};
use rstest::{fixture, rstest}; use rstest::{fixture, rstest};
use support::{file, folder, match_suggestions, new_engine}; use support::{completions_helpers::new_quote_engine, file, folder, match_suggestions, new_engine};
#[fixture] #[fixture]
fn completer() -> NuCompleter { fn completer() -> NuCompleter {
@ -63,7 +63,7 @@ fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter
#[rstest] #[rstest]
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) { fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 9); let suggestions = completer_strings.complete("my-c ", 4);
let expected: Vec<String> = vec!["my-command".into()]; let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(expected, suggestions); match_suggestions(expected, suggestions);
} }
@ -411,6 +411,24 @@ fn command_watch_with_filecompletion() {
match_suggestions(expected_paths, suggestions) match_suggestions(expected_paths, suggestions)
} }
#[test]
fn file_completion_quoted() {
let (_, _, engine, stack) = new_quote_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "open ";
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test] #[test]
fn flag_completions() { fn flag_completions() {
// Create a new engine // Create a new engine
@ -672,3 +690,63 @@ fn unknown_command_completion() {
match_suggestions(expected_paths, suggestions) match_suggestions(expected_paths, suggestions)
} }
#[rstest]
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c | ls", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h | ls", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[test]
fn filecompletions_triggers_after_cursor() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("cp test_c", 3);
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions);
}

View File

@ -33,20 +33,53 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
"PWD".to_string(), "PWD".to_string(),
Value::String { Value::String {
val: dir_str.clone(), val: dir_str.clone(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0,
end: dir_str.len(),
},
}, },
); );
stack.add_env_var( stack.add_env_var(
"TEST".to_string(), "TEST".to_string(),
Value::String { Value::String {
val: "NUSHELL".to_string(), val: "NUSHELL".to_string(),
span: nu_protocol::Span { span: nu_protocol::Span::new(0, dir_str.len()),
start: 0, },
end: dir_str.len(), );
},
// Merge environment into the permanent state
let merge_result = engine_state.merge_env(&mut stack, &dir);
assert!(merge_result.is_ok());
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
let mut dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
dir_str.push(SEP);
// Create a new engine with default context
let mut engine_state = create_default_context();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var(
"PWD".to_string(),
Value::String {
val: dir_str.clone(),
span: nu_protocol::Span::new(0, dir_str.len()),
},
);
stack.add_env_var(
"TEST".to_string(),
Value::String {
val: "NUSHELL".to_string(),
span: nu_protocol::Span::new(0, dir_str.len()),
}, },
); );
@ -112,7 +145,7 @@ pub fn merge_input(
&block, &block,
PipelineData::Value( PipelineData::Value(
Value::Nothing { Value::Nothing {
span: Span { start: 0, end: 0 }, span: Span::unknown(),
}, },
None None
), ),

View File

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

View File

@ -1,418 +1,89 @@
use crate::nu_style::{color_from_hex, color_string_to_nustyle}; use crate::{
use nu_ansi_term::{Color, Style}; nu_style::{color_from_hex, lookup_style},
use nu_protocol::Config; parse_nustyle, NuStyle,
use nu_table::{Alignment, TextStyle}; };
use nu_ansi_term::Style;
use nu_protocol::Value;
use std::collections::HashMap; use std::collections::HashMap;
pub fn lookup_ansi_color_style(s: &str) -> Style { pub fn lookup_ansi_color_style(s: &str) -> Style {
if s.starts_with('#') { if s.starts_with('#') {
match color_from_hex(s) { color_from_hex(s)
Ok(c) => match c { .ok()
Some(c) => c.normal(), .and_then(|c| c.map(|c| c.normal()))
None => Style::default(), .unwrap_or_default()
},
Err(_) => Style::default(),
}
} else if s.starts_with('{') { } else if s.starts_with('{') {
color_string_to_nustyle(s.to_string()) color_string_to_nustyle(s.to_string())
} else { } else {
match s { lookup_style(s)
"g" | "green" => Color::Green.normal(),
"gb" | "green_bold" => Color::Green.bold(),
"gu" | "green_underline" => Color::Green.underline(),
"gi" | "green_italic" => Color::Green.italic(),
"gd" | "green_dimmed" => Color::Green.dimmed(),
"gr" | "green_reverse" => Color::Green.reverse(),
"gbl" | "green_blink" => Color::Green.blink(),
"gst" | "green_strike" => Color::Green.strikethrough(),
"lg" | "light_green" => Color::LightGreen.normal(),
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
"r" | "red" => Color::Red.normal(),
"rb" | "red_bold" => Color::Red.bold(),
"ru" | "red_underline" => Color::Red.underline(),
"ri" | "red_italic" => Color::Red.italic(),
"rd" | "red_dimmed" => Color::Red.dimmed(),
"rr" | "red_reverse" => Color::Red.reverse(),
"rbl" | "red_blink" => Color::Red.blink(),
"rst" | "red_strike" => Color::Red.strikethrough(),
"lr" | "light_red" => Color::LightRed.normal(),
"lrb" | "light_red_bold" => Color::LightRed.bold(),
"lru" | "light_red_underline" => Color::LightRed.underline(),
"lri" | "light_red_italic" => Color::LightRed.italic(),
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
"u" | "blue" => Color::Blue.normal(),
"ub" | "blue_bold" => Color::Blue.bold(),
"uu" | "blue_underline" => Color::Blue.underline(),
"ui" | "blue_italic" => Color::Blue.italic(),
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
"ur" | "blue_reverse" => Color::Blue.reverse(),
"ubl" | "blue_blink" => Color::Blue.blink(),
"ust" | "blue_strike" => Color::Blue.strikethrough(),
"lu" | "light_blue" => Color::LightBlue.normal(),
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
"b" | "black" => Color::Black.normal(),
"bb" | "black_bold" => Color::Black.bold(),
"bu" | "black_underline" => Color::Black.underline(),
"bi" | "black_italic" => Color::Black.italic(),
"bd" | "black_dimmed" => Color::Black.dimmed(),
"br" | "black_reverse" => Color::Black.reverse(),
"bbl" | "black_blink" => Color::Black.blink(),
"bst" | "black_strike" => Color::Black.strikethrough(),
"ligr" | "light_gray" => Color::LightGray.normal(),
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
"y" | "yellow" => Color::Yellow.normal(),
"yb" | "yellow_bold" => Color::Yellow.bold(),
"yu" | "yellow_underline" => Color::Yellow.underline(),
"yi" | "yellow_italic" => Color::Yellow.italic(),
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
"ybl" | "yellow_blink" => Color::Yellow.blink(),
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
"ly" | "light_yellow" => Color::LightYellow.normal(),
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
"p" | "purple" => Color::Purple.normal(),
"pb" | "purple_bold" => Color::Purple.bold(),
"pu" | "purple_underline" => Color::Purple.underline(),
"pi" | "purple_italic" => Color::Purple.italic(),
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
"pr" | "purple_reverse" => Color::Purple.reverse(),
"pbl" | "purple_blink" => Color::Purple.blink(),
"pst" | "purple_strike" => Color::Purple.strikethrough(),
"lp" | "light_purple" => Color::LightPurple.normal(),
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
"c" | "cyan" => Color::Cyan.normal(),
"cb" | "cyan_bold" => Color::Cyan.bold(),
"cu" | "cyan_underline" => Color::Cyan.underline(),
"ci" | "cyan_italic" => Color::Cyan.italic(),
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
"cbl" | "cyan_blink" => Color::Cyan.blink(),
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
"lc" | "light_cyan" => Color::LightCyan.normal(),
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
"w" | "white" => Color::White.normal(),
"wb" | "white_bold" => Color::White.bold(),
"wu" | "white_underline" => Color::White.underline(),
"wi" | "white_italic" => Color::White.italic(),
"wd" | "white_dimmed" => Color::White.dimmed(),
"wr" | "white_reverse" => Color::White.reverse(),
"wbl" | "white_blink" => Color::White.blink(),
"wst" | "white_strike" => Color::White.strikethrough(),
"dgr" | "dark_gray" => Color::DarkGray.normal(),
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
"def" | "default" => Color::Default.normal(),
"defb" | "default_bold" => Color::Default.bold(),
"defu" | "default_underline" => Color::Default.underline(),
"defi" | "default_italic" => Color::Default.italic(),
"defd" | "default_dimmed" => Color::Default.dimmed(),
"defr" | "default_reverse" => Color::Default.reverse(),
_ => Color::White.normal(),
}
} }
} }
fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) { pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style> {
// eprintln!("key: {}, val: {}", &key, &val);
let color = lookup_ansi_color_style(val);
if let Some(v) = hm.get_mut(key) {
*v = color;
} else {
hm.insert(key.to_string(), color);
}
}
pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
let config = config;
// create the hashmap
let mut hm: HashMap<String, Style> = HashMap::new(); let mut hm: HashMap<String, Style> = HashMap::new();
// set some defaults
// hm.insert("primitive_line".to_string(), Color::White.normal());
// hm.insert("primitive_pattern".to_string(), Color::White.normal());
// hm.insert("primitive_path".to_string(), Color::White.normal());
hm.insert("separator".to_string(), Color::White.normal());
hm.insert(
"leading_trailing_space_bg".to_string(),
Style::default().on(Color::Rgb(128, 128, 128)),
);
hm.insert("header".to_string(), Color::Green.bold());
hm.insert("empty".to_string(), Color::Blue.normal());
hm.insert("bool".to_string(), Color::White.normal());
hm.insert("int".to_string(), Color::White.normal());
hm.insert("filesize".to_string(), Color::White.normal());
hm.insert("duration".to_string(), Color::White.normal());
hm.insert("date".to_string(), Color::White.normal());
hm.insert("range".to_string(), Color::White.normal());
hm.insert("float".to_string(), Color::White.normal());
hm.insert("string".to_string(), Color::White.normal());
hm.insert("nothing".to_string(), Color::White.normal());
hm.insert("binary".to_string(), Color::White.normal());
hm.insert("cellpath".to_string(), Color::White.normal());
hm.insert("row_index".to_string(), Color::Green.bold());
hm.insert("record".to_string(), Color::White.normal());
hm.insert("list".to_string(), Color::White.normal());
hm.insert("block".to_string(), Color::White.normal());
hm.insert("hints".to_string(), Color::DarkGray.normal());
for (key, value) in &config.color_config { for (key, value) in colors {
let value = value parse_map_entry(&mut hm, key, value);
.as_string()
.expect("the only values for config color must be strings");
update_hashmap(key, &value, &mut hm);
// eprintln!(
// "config: {}:{}\t\t\thashmap: {}:{:?}",
// &key, &value, &key, &hm[key]
// );
} }
hm hm
} }
// This function will assign a text style to a primitive, or really any string that's fn parse_map_entry(hm: &mut HashMap<String, Style>, key: &str, value: &Value) {
// in the hashmap. The hashmap actually contains the style to be applied. let value = match value {
pub fn style_primitive(primitive: &str, color_hm: &HashMap<String, Style>) -> TextStyle { Value::String { val, .. } => Some(lookup_ansi_color_style(val)),
match primitive { Value::Record { cols, vals, .. } => get_style_from_value(cols, vals).map(parse_nustyle),
"bool" => { _ => None,
let style = color_hm.get(primitive); };
match style { if let Some(value) = value {
Some(s) => TextStyle::with_style(Alignment::Left, *s), hm.entry(key.to_owned()).or_insert(value);
None => TextStyle::basic_left(),
}
}
"int" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"filesize" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"duration" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"date" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"range" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"float" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::basic_right(),
}
}
"string" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"nothing" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
// not sure what to do with error
// "error" => {}
"binary" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"cellpath" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
"row_index" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Right, *s),
None => TextStyle::new()
.alignment(Alignment::Right)
.fg(Color::Green)
.bold(Some(true)),
}
}
"record" | "list" | "block" => {
let style = color_hm.get(primitive);
match style {
Some(s) => TextStyle::with_style(Alignment::Left, *s),
None => TextStyle::basic_left(),
}
}
// types in nushell but not in engine-q
// "Line" => {
// let style = color_hm.get("Primitive::Line");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "GlobPattern" => {
// let style = color_hm.get("Primitive::GlobPattern");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "FilePath" => {
// let style = color_hm.get("Primitive::FilePath");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "BeginningOfStream" => {
// let style = color_hm.get("Primitive::BeginningOfStream");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
// "EndOfStream" => {
// let style = color_hm.get("Primitive::EndOfStream");
// match style {
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
// None => TextStyle::basic_left(),
// }
// }
_ => TextStyle::basic_left(),
} }
} }
#[test] fn get_style_from_value(cols: &[String], vals: &[Value]) -> Option<NuStyle> {
fn test_hm() { let mut was_set = false;
use nu_ansi_term::{Color, Style}; let mut style = NuStyle::from(Style::default());
for (col, val) in cols.iter().zip(vals) {
match col.as_str() {
"bg" => {
if let Value::String { val, .. } = val {
style.bg = Some(val.clone());
was_set = true;
}
}
"fg" => {
if let Value::String { val, .. } = val {
style.fg = Some(val.clone());
was_set = true;
}
}
"attr" => {
if let Value::String { val, .. } = val {
style.attr = Some(val.clone());
was_set = true;
}
}
_ => (),
}
}
let mut hm: HashMap<String, Style> = HashMap::new(); if was_set {
hm.insert("primitive_int".to_string(), Color::White.normal()); Some(style)
hm.insert("primitive_decimal".to_string(), Color::White.normal()); } else {
hm.insert("primitive_filesize".to_string(), Color::White.normal()); None
hm.insert("primitive_string".to_string(), Color::White.normal()); }
hm.insert("primitive_line".to_string(), Color::White.normal()); }
hm.insert("primitive_columnpath".to_string(), Color::White.normal());
hm.insert("primitive_pattern".to_string(), Color::White.normal()); fn color_string_to_nustyle(color_string: String) -> Style {
hm.insert("primitive_boolean".to_string(), Color::White.normal()); // eprintln!("color_string: {}", &color_string);
hm.insert("primitive_date".to_string(), Color::White.normal()); if color_string.is_empty() {
hm.insert("primitive_duration".to_string(), Color::White.normal()); return Style::default();
hm.insert("primitive_range".to_string(), Color::White.normal()); }
hm.insert("primitive_path".to_string(), Color::White.normal());
hm.insert("primitive_binary".to_string(), Color::White.normal()); let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
hm.insert("separator".to_string(), Color::White.normal()); Ok(s) => s,
hm.insert("header_align".to_string(), Color::Green.bold()); Err(_) => return Style::default(),
hm.insert("header".to_string(), Color::Green.bold()); };
hm.insert("header_style".to_string(), Style::default());
hm.insert("row_index".to_string(), Color::Green.bold()); parse_nustyle(nu_style)
hm.insert(
"leading_trailing_space_bg".to_string(),
Style::default().on(Color::Rgb(128, 128, 128)),
);
update_hashmap("primitive_int", "green", &mut hm);
assert_eq!(hm["primitive_int"], Color::Green.normal());
} }

View File

@ -1,7 +1,13 @@
mod color_config; mod color_config;
mod matching_brackets_style;
mod nu_style; mod nu_style;
mod shape_color; mod shape_color;
mod style_computer;
mod text_style;
pub use color_config::*; pub use color_config::*;
pub use matching_brackets_style::*;
pub use nu_style::*; pub use nu_style::*;
pub use shape_color::*; pub use shape_color::*;
pub use style_computer::*;
pub use text_style::*;

View File

@ -0,0 +1,30 @@
use crate::color_config::lookup_ansi_color_style;
use nu_ansi_term::Style;
use nu_protocol::Config;
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
Some(int_color) => match int_color.as_string() {
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
Err(_) => default_style,
},
None => default_style,
}
}
fn merge_styles(base: Style, extra: Style) -> Style {
Style {
foreground: extra.foreground.or(base.foreground),
background: extra.background.or(base.background),
is_bold: extra.is_bold || base.is_bold,
is_dimmed: extra.is_dimmed || base.is_dimmed,
is_italic: extra.is_italic || base.is_italic,
is_underline: extra.is_underline || base.is_underline,
is_blink: extra.is_blink || base.is_blink,
is_reverse: extra.is_reverse || base.is_reverse,
is_hidden: extra.is_hidden || base.is_hidden,
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
}
}

View File

@ -1,88 +1,113 @@
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use serde::Deserialize; use nu_protocol::Value;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, PartialEq, Eq, Debug)] #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
pub struct NuStyle { pub struct NuStyle {
pub fg: Option<String>, pub fg: Option<String>,
pub bg: Option<String>, pub bg: Option<String>,
pub attr: Option<String>, pub attr: Option<String>,
} }
pub fn parse_nustyle(nu_style: NuStyle) -> Style { impl From<Style> for NuStyle {
// get the nu_ansi_term::Color foreground color fn from(s: Style) -> Self {
let fg_color = match nu_style.fg { Self {
Some(fg) => color_from_hex(&fg).unwrap_or_default(), bg: s.background.and_then(color_to_string),
_ => None, fg: s.foreground.and_then(color_to_string),
}; attr: style_get_attr(s),
// get the nu_ansi_term::Color background color
let bg_color = match nu_style.bg {
Some(bg) => color_from_hex(&bg).unwrap_or_default(),
_ => None,
};
// get the attributes
let color_attr = match nu_style.attr {
Some(attr) => attr,
_ => "".to_string(),
};
// setup the attributes available in nu_ansi_term::Style
let mut bold = false;
let mut dimmed = false;
let mut italic = false;
let mut underline = false;
let mut blink = false;
let mut reverse = false;
let mut hidden = false;
let mut strikethrough = false;
// since we can combine styles like bold-italic, iterate through the chars
// and set the bools for later use in the nu_ansi_term::Style application
for ch in color_attr.to_lowercase().chars() {
match ch {
'l' => blink = true,
'b' => bold = true,
'd' => dimmed = true,
'h' => hidden = true,
'i' => italic = true,
'r' => reverse = true,
's' => strikethrough = true,
'u' => underline = true,
'n' => (),
_ => (),
} }
} }
// here's where we build the nu_ansi_term::Style
Style {
foreground: fg_color,
background: bg_color,
is_blink: blink,
is_bold: bold,
is_dimmed: dimmed,
is_hidden: hidden,
is_italic: italic,
is_reverse: reverse,
is_strikethrough: strikethrough,
is_underline: underline,
}
} }
pub fn color_string_to_nustyle(color_string: String) -> Style { fn style_get_attr(s: Style) -> Option<String> {
// eprintln!("color_string: {}", &color_string); macro_rules! check {
if color_string.chars().count() < 1 { ($attrs:expr, $b:expr, $c:expr) => {
Style::default() if $b {
} else { $attrs.push($c);
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) { }
Ok(s) => s,
Err(_) => NuStyle {
fg: None,
bg: None,
attr: None,
},
}; };
parse_nustyle(nu_style)
} }
let mut attrs = String::new();
check!(attrs, s.is_blink, 'l');
check!(attrs, s.is_bold, 'b');
check!(attrs, s.is_dimmed, 'd');
check!(attrs, s.is_reverse, 'r');
check!(attrs, s.is_strikethrough, 's');
check!(attrs, s.is_underline, 'u');
if attrs.is_empty() {
None
} else {
Some(attrs)
}
}
fn color_to_string(color: Color) -> Option<String> {
match color {
Color::Black => Some(String::from("black")),
Color::DarkGray => Some(String::from("dark_gray")),
Color::Red => Some(String::from("red")),
Color::LightRed => Some(String::from("light_red")),
Color::Green => Some(String::from("green")),
Color::LightGreen => Some(String::from("light_green")),
Color::Yellow => Some(String::from("yellow")),
Color::LightYellow => Some(String::from("light_yellow")),
Color::Blue => Some(String::from("blue")),
Color::LightBlue => Some(String::from("light_blue")),
Color::Purple => Some(String::from("purple")),
Color::LightPurple => Some(String::from("light_purple")),
Color::Magenta => Some(String::from("magenta")),
Color::LightMagenta => Some(String::from("light_magenta")),
Color::Cyan => Some(String::from("cyan")),
Color::LightCyan => Some(String::from("light_cyan")),
Color::White => Some(String::from("white")),
Color::LightGray => Some(String::from("light_gray")),
Color::Default => Some(String::from("default")),
Color::Rgb(r, g, b) => Some(format!("#{:X}{:X}{:X}", r, g, b)),
Color::Fixed(_) => None,
}
}
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
let mut style = Style {
foreground: nu_style.fg.and_then(|fg| lookup_color_str(&fg)),
background: nu_style.bg.and_then(|bg| lookup_color_str(&bg)),
..Default::default()
};
if let Some(attrs) = nu_style.attr {
fill_modifiers(&attrs, &mut style)
}
style
}
// Converts the color_config records, { fg, bg, attr }, into a Style.
pub fn color_record_to_nustyle(value: &Value) -> Style {
let mut fg = None;
let mut bg = None;
let mut attr = None;
let v = value.as_record();
if let Ok((cols, inner_vals)) = v {
for (k, v) in cols.iter().zip(inner_vals) {
// Because config already type-checked the color_config records, this doesn't bother giving errors
// if there are unrecognised keys or bad values.
if let Ok(v) = v.as_string() {
match k.as_str() {
"fg" => fg = Some(v),
"bg" => bg = Some(v),
"attr" => attr = Some(v),
_ => (),
}
}
}
}
parse_nustyle(NuStyle { fg, bg, attr })
} }
pub fn color_from_hex( pub fn color_from_hex(
@ -101,3 +126,471 @@ pub fn color_from_hex(
))) )))
} }
} }
pub fn lookup_style(s: &str) -> Style {
match s {
"g" | "green" => Color::Green.normal(),
"gb" | "green_bold" => Color::Green.bold(),
"gu" | "green_underline" => Color::Green.underline(),
"gi" | "green_italic" => Color::Green.italic(),
"gd" | "green_dimmed" => Color::Green.dimmed(),
"gr" | "green_reverse" => Color::Green.reverse(),
"gbl" | "green_blink" => Color::Green.blink(),
"gst" | "green_strike" => Color::Green.strikethrough(),
"lg" | "light_green" => Color::LightGreen.normal(),
"lgb" | "light_green_bold" => Color::LightGreen.bold(),
"lgu" | "light_green_underline" => Color::LightGreen.underline(),
"lgi" | "light_green_italic" => Color::LightGreen.italic(),
"lgd" | "light_green_dimmed" => Color::LightGreen.dimmed(),
"lgr" | "light_green_reverse" => Color::LightGreen.reverse(),
"lgbl" | "light_green_blink" => Color::LightGreen.blink(),
"lgst" | "light_green_strike" => Color::LightGreen.strikethrough(),
"r" | "red" => Color::Red.normal(),
"rb" | "red_bold" => Color::Red.bold(),
"ru" | "red_underline" => Color::Red.underline(),
"ri" | "red_italic" => Color::Red.italic(),
"rd" | "red_dimmed" => Color::Red.dimmed(),
"rr" | "red_reverse" => Color::Red.reverse(),
"rbl" | "red_blink" => Color::Red.blink(),
"rst" | "red_strike" => Color::Red.strikethrough(),
"lr" | "light_red" => Color::LightRed.normal(),
"lrb" | "light_red_bold" => Color::LightRed.bold(),
"lru" | "light_red_underline" => Color::LightRed.underline(),
"lri" | "light_red_italic" => Color::LightRed.italic(),
"lrd" | "light_red_dimmed" => Color::LightRed.dimmed(),
"lrr" | "light_red_reverse" => Color::LightRed.reverse(),
"lrbl" | "light_red_blink" => Color::LightRed.blink(),
"lrst" | "light_red_strike" => Color::LightRed.strikethrough(),
"u" | "blue" => Color::Blue.normal(),
"ub" | "blue_bold" => Color::Blue.bold(),
"uu" | "blue_underline" => Color::Blue.underline(),
"ui" | "blue_italic" => Color::Blue.italic(),
"ud" | "blue_dimmed" => Color::Blue.dimmed(),
"ur" | "blue_reverse" => Color::Blue.reverse(),
"ubl" | "blue_blink" => Color::Blue.blink(),
"ust" | "blue_strike" => Color::Blue.strikethrough(),
"lu" | "light_blue" => Color::LightBlue.normal(),
"lub" | "light_blue_bold" => Color::LightBlue.bold(),
"luu" | "light_blue_underline" => Color::LightBlue.underline(),
"lui" | "light_blue_italic" => Color::LightBlue.italic(),
"lud" | "light_blue_dimmed" => Color::LightBlue.dimmed(),
"lur" | "light_blue_reverse" => Color::LightBlue.reverse(),
"lubl" | "light_blue_blink" => Color::LightBlue.blink(),
"lust" | "light_blue_strike" => Color::LightBlue.strikethrough(),
"b" | "black" => Color::Black.normal(),
"bb" | "black_bold" => Color::Black.bold(),
"bu" | "black_underline" => Color::Black.underline(),
"bi" | "black_italic" => Color::Black.italic(),
"bd" | "black_dimmed" => Color::Black.dimmed(),
"br" | "black_reverse" => Color::Black.reverse(),
"bbl" | "black_blink" => Color::Black.blink(),
"bst" | "black_strike" => Color::Black.strikethrough(),
"ligr" | "light_gray" => Color::LightGray.normal(),
"ligrb" | "light_gray_bold" => Color::LightGray.bold(),
"ligru" | "light_gray_underline" => Color::LightGray.underline(),
"ligri" | "light_gray_italic" => Color::LightGray.italic(),
"ligrd" | "light_gray_dimmed" => Color::LightGray.dimmed(),
"ligrr" | "light_gray_reverse" => Color::LightGray.reverse(),
"ligrbl" | "light_gray_blink" => Color::LightGray.blink(),
"ligrst" | "light_gray_strike" => Color::LightGray.strikethrough(),
"y" | "yellow" => Color::Yellow.normal(),
"yb" | "yellow_bold" => Color::Yellow.bold(),
"yu" | "yellow_underline" => Color::Yellow.underline(),
"yi" | "yellow_italic" => Color::Yellow.italic(),
"yd" | "yellow_dimmed" => Color::Yellow.dimmed(),
"yr" | "yellow_reverse" => Color::Yellow.reverse(),
"ybl" | "yellow_blink" => Color::Yellow.blink(),
"yst" | "yellow_strike" => Color::Yellow.strikethrough(),
"ly" | "light_yellow" => Color::LightYellow.normal(),
"lyb" | "light_yellow_bold" => Color::LightYellow.bold(),
"lyu" | "light_yellow_underline" => Color::LightYellow.underline(),
"lyi" | "light_yellow_italic" => Color::LightYellow.italic(),
"lyd" | "light_yellow_dimmed" => Color::LightYellow.dimmed(),
"lyr" | "light_yellow_reverse" => Color::LightYellow.reverse(),
"lybl" | "light_yellow_blink" => Color::LightYellow.blink(),
"lyst" | "light_yellow_strike" => Color::LightYellow.strikethrough(),
"p" | "purple" => Color::Purple.normal(),
"pb" | "purple_bold" => Color::Purple.bold(),
"pu" | "purple_underline" => Color::Purple.underline(),
"pi" | "purple_italic" => Color::Purple.italic(),
"pd" | "purple_dimmed" => Color::Purple.dimmed(),
"pr" | "purple_reverse" => Color::Purple.reverse(),
"pbl" | "purple_blink" => Color::Purple.blink(),
"pst" | "purple_strike" => Color::Purple.strikethrough(),
"lp" | "light_purple" => Color::LightPurple.normal(),
"lpb" | "light_purple_bold" => Color::LightPurple.bold(),
"lpu" | "light_purple_underline" => Color::LightPurple.underline(),
"lpi" | "light_purple_italic" => Color::LightPurple.italic(),
"lpd" | "light_purple_dimmed" => Color::LightPurple.dimmed(),
"lpr" | "light_purple_reverse" => Color::LightPurple.reverse(),
"lpbl" | "light_purple_blink" => Color::LightPurple.blink(),
"lpst" | "light_purple_strike" => Color::LightPurple.strikethrough(),
"c" | "cyan" => Color::Cyan.normal(),
"cb" | "cyan_bold" => Color::Cyan.bold(),
"cu" | "cyan_underline" => Color::Cyan.underline(),
"ci" | "cyan_italic" => Color::Cyan.italic(),
"cd" | "cyan_dimmed" => Color::Cyan.dimmed(),
"cr" | "cyan_reverse" => Color::Cyan.reverse(),
"cbl" | "cyan_blink" => Color::Cyan.blink(),
"cst" | "cyan_strike" => Color::Cyan.strikethrough(),
"lc" | "light_cyan" => Color::LightCyan.normal(),
"lcb" | "light_cyan_bold" => Color::LightCyan.bold(),
"lcu" | "light_cyan_underline" => Color::LightCyan.underline(),
"lci" | "light_cyan_italic" => Color::LightCyan.italic(),
"lcd" | "light_cyan_dimmed" => Color::LightCyan.dimmed(),
"lcr" | "light_cyan_reverse" => Color::LightCyan.reverse(),
"lcbl" | "light_cyan_blink" => Color::LightCyan.blink(),
"lcst" | "light_cyan_strike" => Color::LightCyan.strikethrough(),
"w" | "white" => Color::White.normal(),
"wb" | "white_bold" => Color::White.bold(),
"wu" | "white_underline" => Color::White.underline(),
"wi" | "white_italic" => Color::White.italic(),
"wd" | "white_dimmed" => Color::White.dimmed(),
"wr" | "white_reverse" => Color::White.reverse(),
"wbl" | "white_blink" => Color::White.blink(),
"wst" | "white_strike" => Color::White.strikethrough(),
"dgr" | "dark_gray" => Color::DarkGray.normal(),
"dgrb" | "dark_gray_bold" => Color::DarkGray.bold(),
"dgru" | "dark_gray_underline" => Color::DarkGray.underline(),
"dgri" | "dark_gray_italic" => Color::DarkGray.italic(),
"dgrd" | "dark_gray_dimmed" => Color::DarkGray.dimmed(),
"dgrr" | "dark_gray_reverse" => Color::DarkGray.reverse(),
"dgrbl" | "dark_gray_blink" => Color::DarkGray.blink(),
"dgrst" | "dark_gray_strike" => Color::DarkGray.strikethrough(),
"def" | "default" => Color::Default.normal(),
"defb" | "default_bold" => Color::Default.bold(),
"defu" | "default_underline" => Color::Default.underline(),
"defi" | "default_italic" => Color::Default.italic(),
"defd" | "default_dimmed" => Color::Default.dimmed(),
"defr" | "default_reverse" => Color::Default.reverse(),
// Add xterm 256 colors adding an x prefix where the name conflicts
"xblack" | "xterm_black" => Color::Fixed(0).normal(),
"maroon" | "xterm_maroon" => Color::Fixed(1).normal(),
"xgreen" | "xterm_green" => Color::Fixed(2).normal(),
"olive" | "xterm_olive" => Color::Fixed(3).normal(),
"navy" | "xterm_navy" => Color::Fixed(4).normal(),
"xpurplea" | "xterm_purplea" => Color::Fixed(5).normal(),
"teal" | "xterm_teal" => Color::Fixed(6).normal(),
"silver" | "xterm_silver" => Color::Fixed(7).normal(),
"grey" | "xterm_grey" => Color::Fixed(8).normal(),
"xred" | "xterm_red" => Color::Fixed(9).normal(),
"lime" | "xterm_lime" => Color::Fixed(10).normal(),
"xyellow" | "xterm_yellow" => Color::Fixed(11).normal(),
"xblue" | "xterm_blue" => Color::Fixed(12).normal(),
"fuchsia" | "xterm_fuchsia" => Color::Fixed(13).normal(),
"aqua" | "xterm_aqua" => Color::Fixed(14).normal(),
"xwhite" | "xterm_white" => Color::Fixed(15).normal(),
"grey0" | "xterm_grey0" => Color::Fixed(16).normal(),
"navyblue" | "xterm_navyblue" => Color::Fixed(17).normal(),
"darkblue" | "xterm_darkblue" => Color::Fixed(18).normal(),
"blue3a" | "xterm_blue3a" => Color::Fixed(19).normal(),
"blue3b" | "xterm_blue3b" => Color::Fixed(20).normal(),
"blue1" | "xterm_blue1" => Color::Fixed(21).normal(),
"darkgreen" | "xterm_darkgreen" => Color::Fixed(22).normal(),
"deepskyblue4a" | "xterm_deepskyblue4a" => Color::Fixed(23).normal(),
"deepskyblue4b" | "xterm_deepskyblue4b" => Color::Fixed(24).normal(),
"deepskyblue4c" | "xterm_deepskyblue4c" => Color::Fixed(25).normal(),
"dodgerblue3" | "xterm_dodgerblue3" => Color::Fixed(26).normal(),
"dodgerblue2" | "xterm_dodgerblue2" => Color::Fixed(27).normal(),
"green4" | "xterm_green4" => Color::Fixed(28).normal(),
"springgreen4" | "xterm_springgreen4" => Color::Fixed(29).normal(),
"turquoise4" | "xterm_turquoise4" => Color::Fixed(30).normal(),
"deepskyblue3a" | "xterm_deepskyblue3a" => Color::Fixed(31).normal(),
"deepskyblue3b" | "xterm_deepskyblue3b" => Color::Fixed(32).normal(),
"dodgerblue1" | "xterm_dodgerblue1" => Color::Fixed(33).normal(),
"green3a" | "xterm_green3a" => Color::Fixed(34).normal(),
"springgreen3a" | "xterm_springgreen3a" => Color::Fixed(35).normal(),
"darkcyan" | "xterm_darkcyan" => Color::Fixed(36).normal(),
"lightseagreen" | "xterm_lightseagreen" => Color::Fixed(37).normal(),
"deepskyblue2" | "xterm_deepskyblue2" => Color::Fixed(38).normal(),
"deepskyblue1" | "xterm_deepskyblue1" => Color::Fixed(39).normal(),
"green3b" | "xterm_green3b" => Color::Fixed(40).normal(),
"springgreen3b" | "xterm_springgreen3b" => Color::Fixed(41).normal(),
"springgreen2a" | "xterm_springgreen2a" => Color::Fixed(42).normal(),
"cyan3" | "xterm_cyan3" => Color::Fixed(43).normal(),
"darkturquoise" | "xterm_darkturquoise" => Color::Fixed(44).normal(),
"turquoise2" | "xterm_turquoise2" => Color::Fixed(45).normal(),
"green1" | "xterm_green1" => Color::Fixed(46).normal(),
"springgreen2b" | "xterm_springgreen2b" => Color::Fixed(47).normal(),
"springgreen1" | "xterm_springgreen1" => Color::Fixed(48).normal(),
"mediumspringgreen" | "xterm_mediumspringgreen" => Color::Fixed(49).normal(),
"cyan2" | "xterm_cyan2" => Color::Fixed(50).normal(),
"cyan1" | "xterm_cyan1" => Color::Fixed(51).normal(),
"darkreda" | "xterm_darkreda" => Color::Fixed(52).normal(),
"deeppink4a" | "xterm_deeppink4a" => Color::Fixed(53).normal(),
"purple4a" | "xterm_purple4a" => Color::Fixed(54).normal(),
"purple4b" | "xterm_purple4b" => Color::Fixed(55).normal(),
"purple3" | "xterm_purple3" => Color::Fixed(56).normal(),
"blueviolet" | "xterm_blueviolet" => Color::Fixed(57).normal(),
"orange4a" | "xterm_orange4a" => Color::Fixed(58).normal(),
"grey37" | "xterm_grey37" => Color::Fixed(59).normal(),
"mediumpurple4" | "xterm_mediumpurple4" => Color::Fixed(60).normal(),
"slateblue3a" | "xterm_slateblue3a" => Color::Fixed(61).normal(),
"slateblue3b" | "xterm_slateblue3b" => Color::Fixed(62).normal(),
"royalblue1" | "xterm_royalblue1" => Color::Fixed(63).normal(),
"chartreuse4" | "xterm_chartreuse4" => Color::Fixed(64).normal(),
"darkseagreen4a" | "xterm_darkseagreen4a" => Color::Fixed(65).normal(),
"paleturquoise4" | "xterm_paleturquoise4" => Color::Fixed(66).normal(),
"steelblue" | "xterm_steelblue" => Color::Fixed(67).normal(),
"steelblue3" | "xterm_steelblue3" => Color::Fixed(68).normal(),
"cornflowerblue" | "xterm_cornflowerblue" => Color::Fixed(69).normal(),
"chartreuse3a" | "xterm_chartreuse3a" => Color::Fixed(70).normal(),
"darkseagreen4b" | "xterm_darkseagreen4b" => Color::Fixed(71).normal(),
"cadetbluea" | "xterm_cadetbluea" => Color::Fixed(72).normal(),
"cadetblueb" | "xterm_cadetblueb" => Color::Fixed(73).normal(),
"skyblue3" | "xterm_skyblue3" => Color::Fixed(74).normal(),
"steelblue1a" | "xterm_steelblue1a" => Color::Fixed(75).normal(),
"chartreuse3b" | "xterm_chartreuse3b" => Color::Fixed(76).normal(),
"palegreen3a" | "xterm_palegreen3a" => Color::Fixed(77).normal(),
"seagreen3" | "xterm_seagreen3" => Color::Fixed(78).normal(),
"aquamarine3" | "xterm_aquamarine3" => Color::Fixed(79).normal(),
"mediumturquoise" | "xterm_mediumturquoise" => Color::Fixed(80).normal(),
"steelblue1b" | "xterm_steelblue1b" => Color::Fixed(81).normal(),
"chartreuse2a" | "xterm_chartreuse2a" => Color::Fixed(82).normal(),
"seagreen2" | "xterm_seagreen2" => Color::Fixed(83).normal(),
"seagreen1a" | "xterm_seagreen1a" => Color::Fixed(84).normal(),
"seagreen1b" | "xterm_seagreen1b" => Color::Fixed(85).normal(),
"aquamarine1a" | "xterm_aquamarine1a" => Color::Fixed(86).normal(),
"darkslategray2" | "xterm_darkslategray2" => Color::Fixed(87).normal(),
"darkredb" | "xterm_darkredb" => Color::Fixed(88).normal(),
"deeppink4b" | "xterm_deeppink4b" => Color::Fixed(89).normal(),
"darkmagentaa" | "xterm_darkmagentaa" => Color::Fixed(90).normal(),
"darkmagentab" | "xterm_darkmagentab" => Color::Fixed(91).normal(),
"darkvioleta" | "xterm_darkvioleta" => Color::Fixed(92).normal(),
"xpurpleb" | "xterm_purpleb" => Color::Fixed(93).normal(),
"orange4b" | "xterm_orange4b" => Color::Fixed(94).normal(),
"lightpink4" | "xterm_lightpink4" => Color::Fixed(95).normal(),
"plum4" | "xterm_plum4" => Color::Fixed(96).normal(),
"mediumpurple3a" | "xterm_mediumpurple3a" => Color::Fixed(97).normal(),
"mediumpurple3b" | "xterm_mediumpurple3b" => Color::Fixed(98).normal(),
"slateblue1" | "xterm_slateblue1" => Color::Fixed(99).normal(),
"yellow4a" | "xterm_yellow4a" => Color::Fixed(100).normal(),
"wheat4" | "xterm_wheat4" => Color::Fixed(101).normal(),
"grey53" | "xterm_grey53" => Color::Fixed(102).normal(),
"lightslategrey" | "xterm_lightslategrey" => Color::Fixed(103).normal(),
"mediumpurple" | "xterm_mediumpurple" => Color::Fixed(104).normal(),
"lightslateblue" | "xterm_lightslateblue" => Color::Fixed(105).normal(),
"yellow4b" | "xterm_yellow4b" => Color::Fixed(106).normal(),
"darkolivegreen3a" | "xterm_darkolivegreen3a" => Color::Fixed(107).normal(),
"darkseagreen" | "xterm_darkseagreen" => Color::Fixed(108).normal(),
"lightskyblue3a" | "xterm_lightskyblue3a" => Color::Fixed(109).normal(),
"lightskyblue3b" | "xterm_lightskyblue3b" => Color::Fixed(110).normal(),
"skyblue2" | "xterm_skyblue2" => Color::Fixed(111).normal(),
"chartreuse2b" | "xterm_chartreuse2b" => Color::Fixed(112).normal(),
"darkolivegreen3b" | "xterm_darkolivegreen3b" => Color::Fixed(113).normal(),
"palegreen3b" | "xterm_palegreen3b" => Color::Fixed(114).normal(),
"darkseagreen3a" | "xterm_darkseagreen3a" => Color::Fixed(115).normal(),
"darkslategray3" | "xterm_darkslategray3" => Color::Fixed(116).normal(),
"skyblue1" | "xterm_skyblue1" => Color::Fixed(117).normal(),
"chartreuse1" | "xterm_chartreuse1" => Color::Fixed(118).normal(),
"lightgreena" | "xterm_lightgreena" => Color::Fixed(119).normal(),
"lightgreenb" | "xterm_lightgreenb" => Color::Fixed(120).normal(),
"palegreen1a" | "xterm_palegreen1a" => Color::Fixed(121).normal(),
"aquamarine1b" | "xterm_aquamarine1b" => Color::Fixed(122).normal(),
"darkslategray1" | "xterm_darkslategray1" => Color::Fixed(123).normal(),
"red3a" | "xterm_red3a" => Color::Fixed(124).normal(),
"deeppink4c" | "xterm_deeppink4c" => Color::Fixed(125).normal(),
"mediumvioletred" | "xterm_mediumvioletred" => Color::Fixed(126).normal(),
"magenta3" | "xterm_magenta3" => Color::Fixed(127).normal(),
"darkvioletb" | "xterm_darkvioletb" => Color::Fixed(128).normal(),
"purplec" | "xterm_purplec" => Color::Fixed(129).normal(),
"darkorange3a" | "xterm_darkorange3a" => Color::Fixed(130).normal(),
"indianreda" | "xterm_indianreda" => Color::Fixed(131).normal(),
"hotpink3a" | "xterm_hotpink3a" => Color::Fixed(132).normal(),
"mediumorchid3" | "xterm_mediumorchid3" => Color::Fixed(133).normal(),
"mediumorchid" | "xterm_mediumorchid" => Color::Fixed(134).normal(),
"mediumpurple2a" | "xterm_mediumpurple2a" => Color::Fixed(135).normal(),
"darkgoldenrod" | "xterm_darkgoldenrod" => Color::Fixed(136).normal(),
"lightsalmon3a" | "xterm_lightsalmon3a" => Color::Fixed(137).normal(),
"rosybrown" | "xterm_rosybrown" => Color::Fixed(138).normal(),
"grey63" | "xterm_grey63" => Color::Fixed(139).normal(),
"mediumpurple2b" | "xterm_mediumpurple2b" => Color::Fixed(140).normal(),
"mediumpurple1" | "xterm_mediumpurple1" => Color::Fixed(141).normal(),
"gold3a" | "xterm_gold3a" => Color::Fixed(142).normal(),
"darkkhaki" | "xterm_darkkhaki" => Color::Fixed(143).normal(),
"navajowhite3" | "xterm_navajowhite3" => Color::Fixed(144).normal(),
"grey69" | "xterm_grey69" => Color::Fixed(145).normal(),
"lightsteelblue3" | "xterm_lightsteelblue3" => Color::Fixed(146).normal(),
"lightsteelblue" | "xterm_lightsteelblue" => Color::Fixed(147).normal(),
"yellow3a" | "xterm_yellow3a" => Color::Fixed(148).normal(),
"darkolivegreen3c" | "xterm_darkolivegreen3c" => Color::Fixed(149).normal(),
"darkseagreen3b" | "xterm_darkseagreen3b" => Color::Fixed(150).normal(),
"darkseagreen2a" | "xterm_darkseagreen2a" => Color::Fixed(151).normal(),
"lightcyan3" | "xterm_lightcyan3" => Color::Fixed(152).normal(),
"lightskyblue1" | "xterm_lightskyblue1" => Color::Fixed(153).normal(),
"greenyellow" | "xterm_greenyellow" => Color::Fixed(154).normal(),
"darkolivegreen2" | "xterm_darkolivegreen2" => Color::Fixed(155).normal(),
"palegreen1b" | "xterm_palegreen1b" => Color::Fixed(156).normal(),
"darkseagreen2b" | "xterm_darkseagreen2b" => Color::Fixed(157).normal(),
"darkseagreen1a" | "xterm_darkseagreen1a" => Color::Fixed(158).normal(),
"paleturquoise1" | "xterm_paleturquoise1" => Color::Fixed(159).normal(),
"red3b" | "xterm_red3b" => Color::Fixed(160).normal(),
"deeppink3a" | "xterm_deeppink3a" => Color::Fixed(161).normal(),
"deeppink3b" | "xterm_deeppink3b" => Color::Fixed(162).normal(),
"magenta3a" | "xterm_magenta3a" => Color::Fixed(163).normal(),
"magenta3b" | "xterm_magenta3b" => Color::Fixed(164).normal(),
"magenta2a" | "xterm_magenta2a" => Color::Fixed(165).normal(),
"darkorange3b" | "xterm_darkorange3b" => Color::Fixed(166).normal(),
"indianredb" | "xterm_indianredb" => Color::Fixed(167).normal(),
"hotpink3b" | "xterm_hotpink3b" => Color::Fixed(168).normal(),
"hotpink2" | "xterm_hotpink2" => Color::Fixed(169).normal(),
"orchid" | "xterm_orchid" => Color::Fixed(170).normal(),
"mediumorchid1a" | "xterm_mediumorchid1a" => Color::Fixed(171).normal(),
"orange3" | "xterm_orange3" => Color::Fixed(172).normal(),
"lightsalmon3b" | "xterm_lightsalmon3b" => Color::Fixed(173).normal(),
"lightpink3" | "xterm_lightpink3" => Color::Fixed(174).normal(),
"pink3" | "xterm_pink3" => Color::Fixed(175).normal(),
"plum3" | "xterm_plum3" => Color::Fixed(176).normal(),
"violet" | "xterm_violet" => Color::Fixed(177).normal(),
"gold3b" | "xterm_gold3b" => Color::Fixed(178).normal(),
"lightgoldenrod3" | "xterm_lightgoldenrod3" => Color::Fixed(179).normal(),
"tan" | "xterm_tan" => Color::Fixed(180).normal(),
"mistyrose3" | "xterm_mistyrose3" => Color::Fixed(181).normal(),
"thistle3" | "xterm_thistle3" => Color::Fixed(182).normal(),
"plum2" | "xterm_plum2" => Color::Fixed(183).normal(),
"yellow3b" | "xterm_yellow3b" => Color::Fixed(184).normal(),
"khaki3" | "xterm_khaki3" => Color::Fixed(185).normal(),
"lightgoldenrod2" | "xterm_lightgoldenrod2" => Color::Fixed(186).normal(),
"lightyellow3" | "xterm_lightyellow3" => Color::Fixed(187).normal(),
"grey84" | "xterm_grey84" => Color::Fixed(188).normal(),
"lightsteelblue1" | "xterm_lightsteelblue1" => Color::Fixed(189).normal(),
"yellow2" | "xterm_yellow2" => Color::Fixed(190).normal(),
"darkolivegreen1a" | "xterm_darkolivegreen1a" => Color::Fixed(191).normal(),
"darkolivegreen1b" | "xterm_darkolivegreen1b" => Color::Fixed(192).normal(),
"darkseagreen1b" | "xterm_darkseagreen1b" => Color::Fixed(193).normal(),
"honeydew2" | "xterm_honeydew2" => Color::Fixed(194).normal(),
"lightcyan1" | "xterm_lightcyan1" => Color::Fixed(195).normal(),
"red1" | "xterm_red1" => Color::Fixed(196).normal(),
"deeppink2" | "xterm_deeppink2" => Color::Fixed(197).normal(),
"deeppink1a" | "xterm_deeppink1a" => Color::Fixed(198).normal(),
"deeppink1b" | "xterm_deeppink1b" => Color::Fixed(199).normal(),
"magenta2b" | "xterm_magenta2b" => Color::Fixed(200).normal(),
"magenta1" | "xterm_magenta1" => Color::Fixed(201).normal(),
"orangered1" | "xterm_orangered1" => Color::Fixed(202).normal(),
"indianred1a" | "xterm_indianred1a" => Color::Fixed(203).normal(),
"indianred1b" | "xterm_indianred1b" => Color::Fixed(204).normal(),
"hotpinka" | "xterm_hotpinka" => Color::Fixed(205).normal(),
"hotpinkb" | "xterm_hotpinkb" => Color::Fixed(206).normal(),
"mediumorchid1b" | "xterm_mediumorchid1b" => Color::Fixed(207).normal(),
"darkorange" | "xterm_darkorange" => Color::Fixed(208).normal(),
"salmon1" | "xterm_salmon1" => Color::Fixed(209).normal(),
"lightcoral" | "xterm_lightcoral" => Color::Fixed(210).normal(),
"palevioletred1" | "xterm_palevioletred1" => Color::Fixed(211).normal(),
"orchid2" | "xterm_orchid2" => Color::Fixed(212).normal(),
"orchid1" | "xterm_orchid1" => Color::Fixed(213).normal(),
"orange1" | "xterm_orange1" => Color::Fixed(214).normal(),
"sandybrown" | "xterm_sandybrown" => Color::Fixed(215).normal(),
"lightsalmon1" | "xterm_lightsalmon1" => Color::Fixed(216).normal(),
"lightpink1" | "xterm_lightpink1" => Color::Fixed(217).normal(),
"pink1" | "xterm_pink1" => Color::Fixed(218).normal(),
"plum1" | "xterm_plum1" => Color::Fixed(219).normal(),
"gold1" | "xterm_gold1" => Color::Fixed(220).normal(),
"lightgoldenrod2a" | "xterm_lightgoldenrod2a" => Color::Fixed(221).normal(),
"lightgoldenrod2b" | "xterm_lightgoldenrod2b" => Color::Fixed(222).normal(),
"navajowhite1" | "xterm_navajowhite1" => Color::Fixed(223).normal(),
"mistyrose1" | "xterm_mistyrose1" => Color::Fixed(224).normal(),
"thistle1" | "xterm_thistle1" => Color::Fixed(225).normal(),
"yellow1" | "xterm_yellow1" => Color::Fixed(226).normal(),
"lightgoldenrod1" | "xterm_lightgoldenrod1" => Color::Fixed(227).normal(),
"khaki1" | "xterm_khaki1" => Color::Fixed(228).normal(),
"wheat1" | "xterm_wheat1" => Color::Fixed(229).normal(),
"cornsilk1" | "xterm_cornsilk1" => Color::Fixed(230).normal(),
"grey100" | "xterm_grey100" => Color::Fixed(231).normal(),
"grey3" | "xterm_grey3" => Color::Fixed(232).normal(),
"grey7" | "xterm_grey7" => Color::Fixed(233).normal(),
"grey11" | "xterm_grey11" => Color::Fixed(234).normal(),
"grey15" | "xterm_grey15" => Color::Fixed(235).normal(),
"grey19" | "xterm_grey19" => Color::Fixed(236).normal(),
"grey23" | "xterm_grey23" => Color::Fixed(237).normal(),
"grey27" | "xterm_grey27" => Color::Fixed(238).normal(),
"grey30" | "xterm_grey30" => Color::Fixed(239).normal(),
"grey35" | "xterm_grey35" => Color::Fixed(240).normal(),
"grey39" | "xterm_grey39" => Color::Fixed(241).normal(),
"grey42" | "xterm_grey42" => Color::Fixed(242).normal(),
"grey46" | "xterm_grey46" => Color::Fixed(243).normal(),
"grey50" | "xterm_grey50" => Color::Fixed(244).normal(),
"grey54" | "xterm_grey54" => Color::Fixed(245).normal(),
"grey58" | "xterm_grey58" => Color::Fixed(246).normal(),
"grey62" | "xterm_grey62" => Color::Fixed(247).normal(),
"grey66" | "xterm_grey66" => Color::Fixed(248).normal(),
"grey70" | "xterm_grey70" => Color::Fixed(249).normal(),
"grey74" | "xterm_grey74" => Color::Fixed(250).normal(),
"grey78" | "xterm_grey78" => Color::Fixed(251).normal(),
"grey82" | "xterm_grey82" => Color::Fixed(252).normal(),
"grey85" | "xterm_grey85" => Color::Fixed(253).normal(),
"grey89" | "xterm_grey89" => Color::Fixed(254).normal(),
"grey93" | "xterm_grey93" => Color::Fixed(255).normal(),
_ => Color::White.normal(),
}
}
pub fn lookup_color(s: &str) -> Option<Color> {
let color = match s {
"g" | "green" => Color::Green,
"lg" | "light_green" => Color::LightGreen,
"r" | "red" => Color::Red,
"lr" | "light_red" => Color::LightRed,
"u" | "blue" => Color::Blue,
"lu" | "light_blue" => Color::LightBlue,
"b" | "black" => Color::Black,
"ligr" | "light_gray" => Color::LightGray,
"y" | "yellow" => Color::Yellow,
"ly" | "light_yellow" => Color::LightYellow,
"p" | "purple" => Color::Purple,
"lp" | "light_purple" => Color::LightPurple,
"c" | "cyan" => Color::Cyan,
"lc" | "light_cyan" => Color::LightCyan,
"w" | "white" => Color::White,
"dgr" | "dark_gray" => Color::DarkGray,
"def" | "default" => Color::Default,
_ => return None,
};
Some(color)
}
fn fill_modifiers(attrs: &str, style: &mut Style) {
// setup the attributes available in nu_ansi_term::Style
//
// since we can combine styles like bold-italic, iterate through the chars
// and set the bools for later use in the nu_ansi_term::Style application
for ch in attrs.to_lowercase().chars() {
match ch {
'l' => style.is_blink = true,
'b' => style.is_bold = true,
'd' => style.is_dimmed = true,
'h' => style.is_hidden = true,
'i' => style.is_italic = true,
'r' => style.is_reverse = true,
's' => style.is_strikethrough = true,
'u' => style.is_underline = true,
'n' => (),
_ => (),
}
}
}
fn lookup_color_str(s: &str) -> Option<Color> {
if s.starts_with('#') {
color_from_hex(s).ok().and_then(|c| c)
} else {
lookup_color(s)
}
}

View File

@ -9,32 +9,36 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style {
Err(_) => Style::default(), Err(_) => Style::default(),
}, },
None => match shape.as_ref() { None => match shape.as_ref() {
"shape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(), "shape_and" => Style::new().fg(Color::Purple).bold(),
"shape_binary" => 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_bool" => Style::new().fg(Color::LightCyan),
"shape_int" => Style::new().fg(Color::Purple).bold(), "shape_custom" => Style::new().fg(Color::Green),
"shape_float" => Style::new().fg(Color::Purple).bold(), "shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_range" => Style::new().fg(Color::Yellow).bold(), "shape_directory" => Style::new().fg(Color::Cyan),
"shape_internalcall" => Style::new().fg(Color::Cyan).bold(),
"shape_external" => Style::new().fg(Color::Cyan), "shape_external" => Style::new().fg(Color::Cyan),
"shape_externalarg" => Style::new().fg(Color::Green).bold(), "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_literal" => Style::new().fg(Color::Blue),
"shape_nothing" => Style::new().fg(Color::LightCyan),
"shape_operator" => Style::new().fg(Color::Yellow), "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_signature" => Style::new().fg(Color::Green).bold(),
"shape_string" => Style::new().fg(Color::Green), "shape_string" => Style::new().fg(Color::Green),
"shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(), "shape_string_interpolation" => Style::new().fg(Color::Cyan).bold(),
"shape_datetime" => Style::new().fg(Color::Cyan).bold(),
"shape_list" => Style::new().fg(Color::Cyan).bold(),
"shape_table" => Style::new().fg(Color::Blue).bold(), "shape_table" => Style::new().fg(Color::Blue).bold(),
"shape_record" => Style::new().fg(Color::Cyan).bold(),
"shape_block" => Style::new().fg(Color::Blue).bold(),
"shape_filepath" => Style::new().fg(Color::Cyan),
"shape_directory" => Style::new().fg(Color::Cyan),
"shape_globpattern" => Style::new().fg(Color::Cyan).bold(),
"shape_variable" => Style::new().fg(Color::Purple), "shape_variable" => Style::new().fg(Color::Purple),
"shape_flag" => Style::new().fg(Color::Blue).bold(),
"shape_custom" => Style::new().fg(Color::Green),
"shape_nothing" => Style::new().fg(Color::LightCyan),
_ => Style::default(), _ => Style::default(),
}, },
} }

View File

@ -0,0 +1,285 @@
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
use nu_ansi_term::{Color, Style};
use nu_engine::eval_block;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
CliError, IntoPipelineData, Value,
};
use tabled::alignment::AlignmentHorizontal;
use std::{
collections::HashMap,
fmt::{Debug, Formatter, Result},
};
// ComputableStyle represents the valid user style types: a single color value, or a closure which
// takes an input value and produces a color value. The latter represents a value which
// is computed at use-time.
#[derive(Debug, Clone)]
pub enum ComputableStyle {
Static(Style),
Closure(Value),
}
// macro used for adding initial values to the style hashmap
macro_rules! initial {
($a:expr, $b:expr) => {
($a.to_string(), ComputableStyle::Static($b))
};
}
// An alias for the mapping used internally by StyleComputer.
pub type StyleMapping = HashMap<String, ComputableStyle>;
//
// A StyleComputer is an all-in-one way to compute styles. A nu command can
// simply create it with from_config(), and then use it with compute().
// It stores the engine state and stack needed to run closures that
// may be defined as a user style.
//
pub struct StyleComputer<'a> {
engine_state: &'a EngineState,
stack: &'a Stack,
map: StyleMapping,
}
impl<'a> StyleComputer<'a> {
// This is NOT meant to be used in most cases - please use from_config() instead.
// This only exists for testing purposes.
pub fn new(
engine_state: &'a EngineState,
stack: &'a Stack,
map: StyleMapping,
) -> StyleComputer<'a> {
StyleComputer {
engine_state,
stack,
map,
}
}
// The main method. Takes a string name which maps to a color_config style name,
// and a Nu value to pipe into any closures that may have been defined there.
pub fn compute(&self, style_name: &str, value: &Value) -> Style {
match self.map.get(style_name) {
// Static values require no computation.
Some(ComputableStyle::Static(s)) => *s,
// Closures are run here.
Some(ComputableStyle::Closure(Value::Closure {
val: block_id,
captures,
span,
})) => {
let block = self.engine_state.get_block(*block_id).clone();
// Because captures_to_stack() clones, we don't need to use with_env() here
// (contrast with_env() usage in `each` or `do`).
let mut stack = self.stack.captures_to_stack(captures);
// Support 1-argument blocks as well as 0-argument blocks.
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, value.clone());
}
}
// Run the block.
match eval_block(
self.engine_state,
&mut stack,
&block,
value.clone().into_pipeline_data(),
false,
false,
) {
Ok(v) => {
let value = v.into_value(*span);
// These should be the same color data forms supported by color_config.
match value {
Value::Record { .. } => color_record_to_nustyle(&value),
Value::String { val, .. } => lookup_ansi_color_style(&val),
_ => Style::default(),
}
}
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
// currently hook closure errors behave roughly the same.
Err(e) => {
eprintln!(
"Error: {:?}",
CliError(&e, &StateWorkingSet::new(self.engine_state))
);
Style::default()
}
}
}
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
// so this is just a fallback.
_ => Style::default(),
}
}
// Used only by the `table` command.
pub fn style_primitive(&self, value: &Value) -> TextStyle {
let s = self.compute(&value.get_type().to_string(), value);
match *value {
Value::Bool { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Int { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Range { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Float { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
Value::String { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Nothing { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Binary { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::CellPath { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
TextStyle::with_style(AlignmentHorizontal::Left, s)
}
_ => TextStyle::basic_left(),
}
}
// The main constructor.
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
let config = engine_state.get_config();
// Create the hashmap
let mut map: StyleMapping = HashMap::from([
initial!("separator", Color::White.normal()),
initial!(
"leading_trailing_space_bg",
Style::default().on(Color::Rgb(128, 128, 128))
),
initial!("header", Color::White.normal()),
initial!("empty", Color::White.normal()),
initial!("bool", Color::White.normal()),
initial!("int", Color::White.normal()),
initial!("filesize", Color::White.normal()),
initial!("duration", Color::White.normal()),
initial!("date", Color::White.normal()),
initial!("range", Color::White.normal()),
initial!("float", Color::White.normal()),
initial!("string", Color::White.normal()),
initial!("nothing", Color::White.normal()),
initial!("binary", Color::White.normal()),
initial!("cellpath", Color::White.normal()),
initial!("row_index", Color::Green.bold()),
initial!("record", Color::White.normal()),
initial!("list", Color::White.normal()),
initial!("block", Color::White.normal()),
initial!("hints", Color::DarkGray.normal()),
]);
for (key, value) in &config.color_config {
match value {
Value::Closure { .. } => {
map.insert(key.to_string(), ComputableStyle::Closure(value.clone()));
}
Value::Record { .. } => {
map.insert(
key.to_string(),
ComputableStyle::Static(color_record_to_nustyle(value)),
);
}
Value::String { val, .. } => {
// update the stylemap with the found key
let color = lookup_ansi_color_style(val.as_str());
if let Some(v) = map.get_mut(key) {
*v = ComputableStyle::Static(color);
} else {
map.insert(key.to_string(), ComputableStyle::Static(color));
}
}
// This should never occur.
_ => (),
}
}
StyleComputer::new(engine_state, stack, map)
}
}
// Because EngineState doesn't have Debug (Dec 2022),
// this incomplete representation must be used.
impl<'a> Debug for StyleComputer<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("StyleComputer")
.field("map", &self.map)
.finish()
}
}
#[test]
fn test_computable_style_static() {
use nu_protocol::Span;
let style1 = Style::default().italic();
let style2 = Style::default().underline();
// Create a "dummy" style_computer for this test.
let dummy_engine_state = EngineState::new();
let mut dummy_stack = Stack::new();
let style_computer = StyleComputer::new(
&dummy_engine_state,
&mut dummy_stack,
HashMap::from([
("string".into(), ComputableStyle::Static(style1)),
("row_index".into(), ComputableStyle::Static(style2)),
]),
);
assert_eq!(
style_computer.compute("string", &Value::nothing(Span::unknown())),
style1
);
assert_eq!(
style_computer.compute("row_index", &Value::nothing(Span::unknown())),
style2
);
}
// Because each closure currently runs in a separate environment, checks that the closures have run
// must use the filesystem.
#[test]
fn test_computable_style_closure_basic() {
use nu_test_support::{nu, nu_repl_code, playground::Playground};
Playground::setup("computable_style_closure_basic", |dirs, _| {
let inp = [
r#"let-env config = {
color_config: {
string: {|e| touch ($e + '.obj'); 'red' }
}
};"#,
"[bell book candle] | table | ignore",
"ls | get name | to nuon",
];
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
assert_eq!(actual_repl.err, "");
assert_eq!(actual_repl.out, "[bell.obj, book.obj, candle.obj]");
});
}
#[test]
fn test_computable_style_closure_errors() {
use nu_test_support::{nu, nu_repl_code};
let inp = [
r#"let-env config = {
color_config: {
string: {|e| $e + 2 }
}
};"#,
"[bell] | table",
];
let actual_repl = nu!(cwd: ".", nu_repl_code(&inp));
// Check that the error was printed
assert!(actual_repl.err.contains("type mismatch for operator"));
// Check that the value was printed
assert!(actual_repl.out.contains("bell"));
}

View File

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

View File

@ -5,41 +5,42 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-command" name = "nu-command"
version = "0.69.1" version = "0.73.0"
build = "build.rs" build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.69.1" } nu-color-config = { path = "../nu-color-config", version = "0.73.0" }
nu-engine = { path = "../nu-engine", version = "0.69.1" } nu-engine = { path = "../nu-engine", version = "0.73.0" }
nu-glob = { path = "../nu-glob", version = "0.69.1" } nu-glob = { path = "../nu-glob", version = "0.73.0" }
nu-json = { path = "../nu-json", version = "0.69.1" } nu-json = { path = "../nu-json", version = "0.73.0" }
nu-parser = { path = "../nu-parser", version = "0.69.1" } nu-parser = { path = "../nu-parser", version = "0.73.0" }
nu-path = { path = "../nu-path", version = "0.69.1" } nu-path = { path = "../nu-path", version = "0.73.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.69.1" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.73.0" }
nu-protocol = { path = "../nu-protocol", version = "0.69.1" } nu-protocol = { path = "../nu-protocol", version = "0.73.0" }
nu-system = { path = "../nu-system", version = "0.69.1" } nu-system = { path = "../nu-system", version = "0.73.0" }
nu-table = { path = "../nu-table", version = "0.69.1" } nu-table = { path = "../nu-table", version = "0.73.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.69.1" } nu-term-grid = { path = "../nu-term-grid", version = "0.73.0" }
nu-test-support = { path = "../nu-test-support", version = "0.69.1" } nu-utils = { path = "../nu-utils", version = "0.73.0" }
nu-utils = { path = "../nu-utils", version = "0.69.1" } nu-explore = { path = "../nu-explore", version = "0.73.0" }
nu-ansi-term = "0.46.0" nu-ansi-term = "0.46.0"
num-format = { version = "0.4.0" } num-format = { version = "0.4.3" }
# Potential dependencies for extras # Potential dependencies for extras
alphanumeric-sort = "1.4.4" alphanumeric-sort = "1.4.4"
atty = "0.2.14"
base64 = "0.13.0" base64 = "0.13.0"
byteorder = "1.4.3" byteorder = "1.4.3"
bytesize = "1.1.0" bytesize = "1.1.0"
calamine = "0.18.0" calamine = "0.19.1"
chrono = { version = "0.4.21", features = ["serde", "unstable-locales"] } chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
chrono-humanize = "0.2.1" chrono-humanize = "0.2.1"
chrono-tz = "0.6.3" chrono-tz = "0.6.3"
crossterm = "0.24.0" crossterm = "0.24.0"
csv = "1.1.6" csv = "1.1.6"
dialoguer = "0.9.0" dialoguer = { default-features = false, version = "0.9.0" }
digest = "0.10.0" digest = { default-features = false, version = "0.10.0" }
dtparse = "1.2.0" dtparse = "1.2.0"
eml-parser = "0.1.0" eml-parser = "0.1.0"
encoding_rs = "0.8.30" encoding_rs = "0.8.30"
@ -53,22 +54,23 @@ indexmap = { version="1.7", features=["serde-1"] }
Inflector = "0.11" Inflector = "0.11"
is-root = "0.1.2" is-root = "0.1.2"
itertools = "0.10.0" itertools = "0.10.0"
lazy_static = "1.4.0"
log = "0.4.14" log = "0.4.14"
lscolors = { version = "0.12.0", features = ["crossterm"]} lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" } md5 = { package = "md-5", version = "0.10.0" }
meval = "0.2.0" meval = "0.2.0"
mime = "0.3.16" mime = "0.3.16"
notify = "4.0.17" notify = "4.0.17"
num = { version = "0.4.0", optional = true } num = { version = "0.4.0", optional = true }
num-traits = "0.2.14" num-traits = "0.2.14"
once_cell = "1.0"
pathdiff = "0.2.1" pathdiff = "0.2.1"
powierza-coefficient = "1.0.1" powierza-coefficient = "1.0.1"
quick-xml = "0.23.0" quick-xml = "0.25"
rand = "0.8" rand = "0.8"
rayon = "1.5.1" rayon = "1.5.1"
regex = "1.6.0"
reqwest = {version = "0.11", features = ["blocking", "json"] } reqwest = {version = "0.11", features = ["blocking", "json"] }
roxmltree = "0.14.0" roxmltree = "0.16.0"
rust-embed = "6.3.0" rust-embed = "6.3.0"
same-file = "1.0.6" same-file = "1.0.6"
serde = { version="1.0.123", features=["derive"] } serde = { version="1.0.123", features=["derive"] }
@ -78,7 +80,6 @@ serde_yaml = "0.9.4"
sha2 = "0.10.0" sha2 = "0.10.0"
# Disable default features b/c the default features build Git (very slow to compile) # Disable default features b/c the default features build Git (very slow to compile)
shadow-rs = { version = "0.16.1", default-features = false } shadow-rs = { version = "0.16.1", default-features = false }
strip-ansi-escapes = "0.1.1"
sysinfo = "0.26.2" sysinfo = "0.26.2"
terminal_size = "0.2.1" terminal_size = "0.2.1"
thiserror = "1.0.31" thiserror = "1.0.31"
@ -88,21 +89,25 @@ unicode-segmentation = "1.8.0"
url = "2.2.1" url = "2.2.1"
uuid = { version = "1.1.2", features = ["v4"] } uuid = { version = "1.1.2", features = ["v4"] }
which = { version = "4.3.0", optional = true } which = { version = "4.3.0", optional = true }
reedline = { version = "0.12.0", features = ["bashisms", "sqlite"]} reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0", features = ["diagnostics"] } wax = { version = "0.5.0" }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true } rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.23.0", features = ["serde"], optional = true } sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
[target.'cfg(windows)'.dependencies]
winreg = "0.10.1"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
umask = "2.0.0" umask = "2.0.0"
users = "0.11.0" users = "0.11.0"
libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "2.1.3" version = "3.0.0"
optional = true optional = true
[dependencies.polars] [dependencies.polars]
version = "0.23.2" version = "0.25.0"
optional = true optional = true
features = [ features = [
"arg_where", "arg_where",
@ -133,9 +138,8 @@ features = [
] ]
[target.'cfg(windows)'.dependencies.windows] [target.'cfg(windows)'.dependencies.windows]
version = "0.37.0" version = "0.43.0"
features = [ features = [
"alloc",
"Win32_Foundation", "Win32_Foundation",
"Win32_Storage_FileSystem", "Win32_Storage_FileSystem",
"Win32_System_SystemServices", "Win32_System_SystemServices",
@ -145,13 +149,15 @@ features = [
trash-support = ["trash"] trash-support = ["trash"]
which-support = ["which"] which-support = ["which"]
plugin = ["nu-parser/plugin"] plugin = ["nu-parser/plugin"]
dataframe = ["polars", "num"] dataframe = ["polars", "num", "sqlparser"]
database = ["sqlparser", "rusqlite"] sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
[build-dependencies] [build-dependencies]
shadow-rs = { version = "0.16.1", default-features = false } shadow-rs = { version = "0.16.1", default-features = false }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.73.0" }
hamcrest2 = "0.3.0" hamcrest2 = "0.3.0"
dirs-next = "2.0.0" dirs-next = "2.0.0"
proptest = "1.0.0" proptest = "1.0.0"

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits and") Signature::build("bits and")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required( .required(
"target", "target",
SyntaxShape::Int, SyntaxShape::Int,
@ -52,10 +54,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits and to two numbers", description: "Apply bits and to two numbers",
example: "2 | bits and 2", example: "2 | bits and 2",
result: Some(Value::Int { result: Some(Value::int(2, Span::test_data())),
val: 2,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical and to a list of numbers", description: "Apply logical and to a list of numbers",

View File

@ -29,7 +29,13 @@ impl Command for Bits {
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Bits.signature(), &Bits.examples(), engine_state, stack), val: get_full_help(
&Bits.signature(),
&Bits.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -16,6 +16,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits not") Signature::build("bits not")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.switch( .switch(
"signed", "signed",
"always treat input number as a signed number", "always treat input number as a signed number",

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits or") Signature::build("bits or")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required( .required(
"target", "target",
SyntaxShape::Int, SyntaxShape::Int,
@ -52,10 +54,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits or to two numbers", description: "Apply bits or to two numbers",
example: "2 | bits or 6", example: "2 | bits or 6",
result: Some(Value::Int { result: Some(Value::int(6, Span::test_data())),
val: 6,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical or to a list of numbers", description: "Apply logical or to a list of numbers",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
use num_traits::int::PrimInt; use num_traits::int::PrimInt;
use std::fmt::Display; use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits rol") Signature::build("bits rol")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate left") .required("bits", SyntaxShape::Int, "number of bits to rotate left")
.switch( .switch(
"signed", "signed",
@ -74,10 +76,7 @@ impl Command for SubCommand {
Example { Example {
description: "Rotate left a number with 2 bits", description: "Rotate left a number with 2 bits",
example: "17 | bits rol 2", example: "17 | bits rol 2",
result: Some(Value::Int { result: Some(Value::int(68, Span::test_data())),
val: 68,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Rotate left a list of numbers with 2 bits", description: "Rotate left a list of numbers with 2 bits",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
use num_traits::int::PrimInt; use num_traits::int::PrimInt;
use std::fmt::Display; use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits ror") Signature::build("bits ror")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate right") .required("bits", SyntaxShape::Int, "number of bits to rotate right")
.switch( .switch(
"signed", "signed",
@ -74,10 +76,7 @@ impl Command for SubCommand {
Example { Example {
description: "Rotate right a number with 60 bits", description: "Rotate right a number with 60 bits",
example: "17 | bits ror 60", example: "17 | bits ror 60",
result: Some(Value::Int { result: Some(Value::int(272, Span::test_data())),
val: 272,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Rotate right a list of numbers of one byte", description: "Rotate right a list of numbers of one byte",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
use num_traits::CheckedShl; use num_traits::CheckedShl;
use std::fmt::Display; use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits shl") Signature::build("bits shl")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to shift left") .required("bits", SyntaxShape::Int, "number of bits to shift left")
.switch( .switch(
"signed", "signed",
@ -74,26 +76,17 @@ impl Command for SubCommand {
Example { Example {
description: "Shift left a number by 7 bits", description: "Shift left a number by 7 bits",
example: "2 | bits shl 7", example: "2 | bits shl 7",
result: Some(Value::Int { result: Some(Value::int(256, Span::test_data())),
val: 256,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a number with 1 byte by 7 bits", description: "Shift left a number with 1 byte by 7 bits",
example: "2 | bits shl 7 -n 1", example: "2 | bits shl 7 -n 1",
result: Some(Value::Int { result: Some(Value::int(0, Span::test_data())),
val: 0,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a signed number by 1 bit", description: "Shift left a signed number by 1 bit",
example: "0x7F | bits shl 1 -s", example: "0x7F | bits shl 1 -s",
result: Some(Value::Int { result: Some(Value::int(254, Span::test_data())),
val: 254,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift left a list of numbers", description: "Shift left a list of numbers",

View File

@ -3,7 +3,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
}; };
use num_traits::CheckedShr; use num_traits::CheckedShr;
use std::fmt::Display; use std::fmt::Display;
@ -18,6 +18,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits shr") Signature::build("bits shr")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required("bits", SyntaxShape::Int, "number of bits to shift right") .required("bits", SyntaxShape::Int, "number of bits to shift right")
.switch( .switch(
"signed", "signed",
@ -74,10 +76,7 @@ impl Command for SubCommand {
Example { Example {
description: "Shift right a number with 2 bits", description: "Shift right a number with 2 bits",
example: "8 | bits shr 2", example: "8 | bits shr 2",
result: Some(Value::Int { result: Some(Value::int(2, Span::test_data())),
val: 2,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Shift right a list of numbers", description: "Shift right a list of numbers",

View File

@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,6 +15,8 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bits xor") Signature::build("bits xor")
.input_output_types(vec![(Type::Int, Type::Int)])
.vectorizes_over_list(true)
.required( .required(
"target", "target",
SyntaxShape::Int, SyntaxShape::Int,
@ -52,10 +54,7 @@ impl Command for SubCommand {
Example { Example {
description: "Apply bits xor to two numbers", description: "Apply bits xor to two numbers",
example: "2 | bits xor 2", example: "2 | bits xor 2",
result: Some(Value::Int { result: Some(Value::int(0, Span::test_data())),
val: 0,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Apply logical xor to a list of numbers", description: "Apply logical xor to a list of numbers",

View File

@ -1,21 +1,21 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
struct Arguments { struct Arguments {
added_data: Vec<u8>, added_data: Vec<u8>,
index: Option<usize>, index: Option<usize>,
end: bool, end: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -30,6 +30,8 @@ impl Command for BytesAdd {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes add") Signature::build("bytes add")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.vectorizes_over_list(true)
.required("data", SyntaxShape::Binary, "the binary to add") .required("data", SyntaxShape::Binary, "the binary to add")
.named( .named(
"index", "index",
@ -41,7 +43,7 @@ impl Command for BytesAdd {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally matches prefix of text by column paths", "for a data structure input, add bytes to the data at the given cell paths",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
@ -62,12 +64,8 @@ impl Command for BytesAdd {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?; let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?; let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
let end = call.has_flag("end"); let end = call.has_flag("end");
@ -75,7 +73,7 @@ impl Command for BytesAdd {
added_data, added_data,
index, index,
end, end,
column_paths, cell_paths,
}; };
operate(add, arg, input, call.head, engine_state.ctrlc.clone()) operate(add, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -118,7 +116,25 @@ impl Command for BytesAdd {
} }
} }
fn add(input: &[u8], args: &Arguments, span: Span) -> Value { fn add(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => add_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
match args.index { match args.index {
None => { None => {
if args.end { if args.end {

View File

@ -1,10 +1,10 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
use std::cmp::Ordering; use std::cmp::Ordering;
@ -15,12 +15,12 @@ struct Arguments {
start: isize, start: isize,
end: isize, end: isize,
arg_span: Span, arg_span: Span,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -115,11 +115,13 @@ impl Command for BytesAt {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes at") Signature::build("bytes at")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.vectorizes_over_list(true)
.required("range", SyntaxShape::Any, "the indexes to get bytes") .required("range", SyntaxShape::Any, "the indexes to get bytes")
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally get bytes by column paths", "for a data structure input, get bytes from data at the given cell paths",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
@ -141,17 +143,13 @@ impl Command for BytesAt {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let range: Value = call.req(engine_state, stack, 0)?; let range: Value = call.req(engine_state, stack, 0)?;
let (start, end, arg_span) = parse_range(range, call.head)?; let (start, end, arg_span) = parse_range(range, call.head)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
start, start,
end, end,
arg_span, arg_span,
column_paths, cell_paths,
}; };
operate(at, arg, input, call.head, engine_state.ctrlc.clone()) operate(at, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -228,7 +226,25 @@ impl Command for BytesAt {
} }
} }
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value { fn at(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => at_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let len: isize = input.len() as isize; let len: isize = input.len() as isize;
let start: isize = if arg.start < 0 { let start: isize = if arg.start < 0 {

View File

@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -24,6 +24,7 @@ impl Command for BytesBuild {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("bytes build") Signature::build("bytes build")
.input_output_types(vec![(Type::Nothing, Type::Binary)])
.rest("rest", SyntaxShape::Any, "list of bytes") .rest("rest", SyntaxShape::Any, "list of bytes")
.category(Category::Bytes) .category(Category::Bytes)
} }

View File

@ -29,7 +29,13 @@ impl Command for Bytes {
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Bytes.signature(), &Bytes.examples(), engine_state, stack), val: get_full_help(
&Bytes.signature(),
&Bytes.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View File

@ -3,7 +3,7 @@ use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value, Type, Value,
}; };
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -16,6 +16,7 @@ impl Command for BytesCollect {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes collect") Signature::build("bytes collect")
.input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)])
.optional( .optional(
"separator", "separator",
SyntaxShape::Binary, SyntaxShape::Binary,

View File

@ -1,19 +1,19 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -28,11 +28,12 @@ impl Command for BytesEndsWith {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes ends-with") Signature::build("bytes ends-with")
.input_output_types(vec![(Type::Binary, Type::Bool)])
.required("pattern", SyntaxShape::Binary, "the pattern to match") .required("pattern", SyntaxShape::Binary, "the pattern to match")
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally matches prefix of text by column paths", "for a data structure input, check if bytes at the given cell paths end with the pattern",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
@ -53,15 +54,11 @@ impl Command for BytesEndsWith {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
column_paths, cell_paths,
}; };
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone()) operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -71,35 +68,37 @@ impl Command for BytesEndsWith {
Example { Example {
description: "Checks if binary ends with `0x[AA]`", description: "Checks if binary ends with `0x[AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]", example: "0x[1F FF AA AA] | bytes ends-with 0x[AA]",
result: Some(Value::Bool { result: Some(Value::boolean(true, Span::test_data())),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary ends with `0x[FF AA AA]`", description: "Checks if binary ends with `0x[FF AA AA]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]", example: "0x[1F FF AA AA] | bytes ends-with 0x[FF AA AA]",
result: Some(Value::Bool { result: Some(Value::boolean(true, Span::test_data())),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary ends with `0x[11]`", description: "Checks if binary ends with `0x[11]`",
example: "0x[1F FF AA AA] | bytes ends-with 0x[11]", example: "0x[1F FF AA AA] | bytes ends-with 0x[11]",
result: Some(Value::Bool { result: Some(Value::boolean(false, Span::test_data())),
val: false,
span: Span::test_data(),
}),
}, },
] ]
} }
} }
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value { fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Bool { match val {
val: input.ends_with(pattern), Value::Binary {
span, val,
span: val_span,
} => Value::boolean(val.ends_with(&args.pattern), *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
} }
} }

View File

@ -1,21 +1,21 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath}; use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
end: bool, end: bool,
all: bool, all: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -29,6 +29,10 @@ impl Command for BytesIndexOf {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes index-of") Signature::build("bytes index-of")
.input_output_types(vec![
(Type::Binary, Type::Int),
(Type::Binary, Type::List(Box::new(Type::Int))),
])
.required( .required(
"pattern", "pattern",
SyntaxShape::Binary, SyntaxShape::Binary,
@ -37,7 +41,7 @@ impl Command for BytesIndexOf {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally returns index of pattern in string by column paths", "for a data structure input, find the indexes at the given cell paths",
) )
.switch("all", "returns all matched index", Some('a')) .switch("all", "returns all matched index", Some('a'))
.switch("end", "search from the end of the binary", Some('e')) .switch("end", "search from the end of the binary", Some('e'))
@ -60,17 +64,13 @@ impl Command for BytesIndexOf {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
end: call.has_flag("end"), end: call.has_flag("end"),
all: call.has_flag("all"), all: call.has_flag("all"),
column_paths, cell_paths,
}; };
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone()) operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -126,7 +126,25 @@ impl Command for BytesIndexOf {
} }
} }
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value { fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => index_of_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
if arg.all { if arg.all {
search_all_index(input, &arg.pattern, arg.end, span) search_all_index(input, &arg.pattern, arg.end, span)
} else { } else {

View File

@ -1,24 +1,14 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct BytesLen; pub struct BytesLen;
struct Arguments {
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
impl Command for BytesLen { impl Command for BytesLen {
fn name(&self) -> &str { fn name(&self) -> &str {
"bytes length" "bytes length"
@ -26,10 +16,12 @@ impl Command for BytesLen {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes length") Signature::build("bytes length")
.input_output_types(vec![(Type::Binary, Type::Int)])
.vectorizes_over_list(true)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally find length of binary by column paths", "for a data structure input, find the length of data at the given cell paths",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
@ -49,13 +41,8 @@ impl Command for BytesLen {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let arg = CellPathOnlyArgs::from(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
operate(length, arg, input, call.head, engine_state.ctrlc.clone()) operate(length, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -78,10 +65,21 @@ impl Command for BytesLen {
} }
} }
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value { fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
Value::Int { match val {
val: input.len() as i64, Value::Binary {
span, val,
span: val_span,
} => Value::int(val.len() as i64, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
} }
} }

View File

@ -11,11 +11,6 @@ mod replace;
mod reverse; mod reverse;
mod starts_with; mod starts_with;
use nu_protocol::ast::CellPath;
use nu_protocol::{PipelineData, ShellError, Span, Value};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub use add::BytesAdd; pub use add::BytesAdd;
pub use at::BytesAt; pub use at::BytesAt;
pub use build_::BytesBuild; pub use build_::BytesBuild;
@ -28,71 +23,3 @@ pub use remove::BytesRemove;
pub use replace::BytesReplace; pub use replace::BytesReplace;
pub use reverse::BytesReverse; pub use reverse::BytesReverse;
pub use starts_with::BytesStartsWith; pub use starts_with::BytesStartsWith;
trait BytesArgument {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
}
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
fn operate<C, A>(
cmd: C,
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<PipelineData, ShellError>
where
A: BytesArgument + Send + Sync + 'static,
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
{
match arg.take_column_paths() {
None => input.map(
move |v| match v {
Value::Binary {
val,
span: val_span,
} => cmd(&val, &arg, val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
},
ctrlc,
),
Some(column_paths) => {
let arg = Arc::new(arg);
input.map(
move |mut v| {
for path in &column_paths {
let opt = arg.clone();
let r = v.update_cell_path(
&path.members,
Box::new(move |old| {
match old {
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
}}}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
v
},
ctrlc,
)
}
}
}

View File

@ -1,21 +1,22 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
Value,
}; };
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
end: bool, end: bool,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
all: bool, all: bool,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -29,11 +30,12 @@ impl Command for BytesRemove {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes remove") Signature::build("bytes remove")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.required("pattern", SyntaxShape::Binary, "the pattern to find") .required("pattern", SyntaxShape::Binary, "the pattern to find")
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally remove bytes by column paths", "for a data structure input, remove bytes from data at the given cell paths",
) )
.switch("end", "remove from end of binary", Some('e')) .switch("end", "remove from end of binary", Some('e'))
.switch("all", "remove occurrences of finding binary", Some('a')) .switch("all", "remove occurrences of finding binary", Some('a'))
@ -55,12 +57,8 @@ impl Command for BytesRemove {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if pattern_to_remove.item.is_empty() { if pattern_to_remove.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
@ -73,7 +71,7 @@ impl Command for BytesRemove {
let arg = Arguments { let arg = Arguments {
pattern: pattern_to_remove, pattern: pattern_to_remove,
end: call.has_flag("end"), end: call.has_flag("end"),
column_paths, cell_paths,
all: call.has_flag("all"), all: call.has_flag("all"),
}; };
@ -135,7 +133,25 @@ impl Command for BytesRemove {
} }
} }
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value { fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => remove_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut result = vec![]; let mut result = vec![];
let remove_all = arg.all; let remove_all = arg.all;
let input_len = input.len(); let input_len = input.len();

View File

@ -1,21 +1,22 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
Value,
}; };
struct Arguments { struct Arguments {
find: Vec<u8>, find: Vec<u8>,
replace: Vec<u8>, replace: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
all: bool, all: bool,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -29,12 +30,13 @@ impl Command for BytesReplace {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes replace") Signature::build("bytes replace")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.required("find", SyntaxShape::Binary, "the pattern to find") .required("find", SyntaxShape::Binary, "the pattern to find")
.required("replace", SyntaxShape::Binary, "the replacement pattern") .required("replace", SyntaxShape::Binary, "the replacement pattern")
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally find and replace text by column paths", "for a data structure input, replace bytes in data at the given cell paths",
) )
.switch("all", "replace all occurrences of find binary", Some('a')) .switch("all", "replace all occurrences of find binary", Some('a'))
.category(Category::Bytes) .category(Category::Bytes)
@ -55,12 +57,8 @@ impl Command for BytesReplace {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?; let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if find.item.is_empty() { if find.item.is_empty() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
@ -72,7 +70,7 @@ impl Command for BytesReplace {
let arg = Arguments { let arg = Arguments {
find: find.item, find: find.item,
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?, replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
column_paths, cell_paths,
all: call.has_flag("all"), all: call.has_flag("all"),
}; };
@ -126,7 +124,25 @@ impl Command for BytesReplace {
} }
} }
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value { fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => replace_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut replaced = vec![]; let mut replaced = vec![];
let replace_all = arg.all; let replace_all = arg.all;

View File

@ -1,20 +1,10 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
struct Arguments {
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
@ -27,16 +17,17 @@ impl Command for BytesReverse {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes reverse") Signature::build("bytes reverse")
.input_output_types(vec![(Type::Binary, Type::Binary)])
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally matches prefix of text by column paths", "for a data structure input, reverse data at the given cell paths",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Reverse every bytes in the pipeline" "Reverse the bytes in the pipeline"
} }
fn search_terms(&self) -> Vec<&str> { fn search_terms(&self) -> Vec<&str> {
@ -50,13 +41,8 @@ impl Command for BytesReverse {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths = if column_paths.is_empty() { let arg = CellPathOnlyArgs::from(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone()) operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
} }
@ -82,12 +68,28 @@ impl Command for BytesReverse {
} }
} }
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value { fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
let mut reversed_input = input.to_vec(); match val {
reversed_input.reverse(); Value::Binary {
Value::Binary { val,
val: reversed_input, span: val_span,
span, } => {
let mut reversed_input = val.to_vec();
reversed_input.reverse();
Value::Binary {
val: reversed_input,
span: *val_span,
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
} }
} }

View File

@ -1,19 +1,19 @@
use super::{operate, BytesArgument}; use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value};
struct Arguments { struct Arguments {
pattern: Vec<u8>, pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>, cell_paths: Option<Vec<CellPath>>,
} }
impl BytesArgument for Arguments { impl CmdArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> { fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take() self.cell_paths.take()
} }
} }
@ -28,11 +28,12 @@ impl Command for BytesStartsWith {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("bytes starts-with") Signature::build("bytes starts-with")
.input_output_types(vec![(Type::Binary, Type::Bool)])
.required("pattern", SyntaxShape::Binary, "the pattern to match") .required("pattern", SyntaxShape::Binary, "the pattern to match")
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally matches prefix of text by column paths", "for a data structure input, check if bytes at the given cell paths start with the pattern",
) )
.category(Category::Bytes) .category(Category::Bytes)
} }
@ -53,15 +54,11 @@ impl Command for BytesStartsWith {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?; let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() { let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
None
} else {
Some(column_paths)
};
let arg = Arguments { let arg = Arguments {
pattern, pattern,
column_paths, cell_paths,
}; };
operate( operate(
starts_with, starts_with,
@ -77,35 +74,37 @@ impl Command for BytesStartsWith {
Example { Example {
description: "Checks if binary starts with `0x[1F FF AA]`", description: "Checks if binary starts with `0x[1F FF AA]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]", example: "0x[1F FF AA AA] | bytes starts-with 0x[1F FF AA]",
result: Some(Value::Bool { result: Some(Value::boolean(true, Span::test_data())),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary starts with `0x[1F]`", description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]", example: "0x[1F FF AA AA] | bytes starts-with 0x[1F]",
result: Some(Value::Bool { result: Some(Value::boolean(true, Span::test_data())),
val: true,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Checks if binary starts with `0x[1F]`", description: "Checks if binary starts with `0x[1F]`",
example: "0x[1F FF AA AA] | bytes starts-with 0x[11]", example: "0x[1F FF AA AA] | bytes starts-with 0x[11]",
result: Some(Value::Bool { result: Some(Value::boolean(false, Span::test_data())),
val: false,
span: Span::test_data(),
}),
}, },
] ]
} }
} }
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value { fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
Value::Bool { match val {
val: input.starts_with(pattern), Value::Binary {
span, val,
span: val_span,
} => Value::boolean(val.starts_with(&args.pattern), *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
} }
} }

View File

@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher};
/// ```text /// ```text
/// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}}) /// assert_eq!(HashableValue::Bool {val: true, span: Span{start: 0, end: 1}}, HashableValue::Bool {val: true, span: Span{start: 90, end: 1000}})
/// ``` /// ```
#[derive(Eq, Debug)] #[derive(Eq, Debug, Ord, PartialOrd)]
pub enum HashableValue { pub enum HashableValue {
Bool { Bool {
val: bool, val: bool,
@ -53,7 +53,7 @@ impl Default for HashableValue {
fn default() -> Self { fn default() -> Self {
HashableValue::Bool { HashableValue::Bool {
val: false, val: false,
span: Span { start: 0, end: 0 }, span: Span::unknown(),
} }
} }
} }
@ -214,7 +214,7 @@ mod test {
]; ];
for (val, expect_hashable_val) in values.into_iter() { for (val, expect_hashable_val) in values.into_iter() {
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }).unwrap(), HashableValue::from_value(val, Span::unknown()).unwrap(),
expect_hashable_val expect_hashable_val
); );
} }
@ -228,7 +228,7 @@ mod test {
vals: vec![Value::Bool { val: true, span }], vals: vec![Value::Bool { val: true, span }],
span, span,
}, },
Value::Block { Value::Closure {
val: 0, val: 0,
captures: HashMap::new(), captures: HashMap::new(),
span, span,
@ -245,7 +245,7 @@ mod test {
}, },
]; ];
for v in values { for v in values {
assert!(HashableValue::from_value(v, Span { start: 0, end: 0 }).is_err()) assert!(HashableValue::from_value(v, Span::unknown()).is_err())
} }
} }
@ -266,7 +266,7 @@ mod test {
for val in values.into_iter() { for val in values.into_iter() {
let expected_val = val.clone(); let expected_val = val.clone();
assert_eq!( assert_eq!(
HashableValue::from_value(val, Span { start: 0, end: 0 }) HashableValue::from_value(val, Span::unknown())
.unwrap() .unwrap()
.into_value(), .into_value(),
expected_val expected_val
@ -279,14 +279,11 @@ mod test {
assert_eq!( assert_eq!(
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { start: 0, end: 1 } span: Span::new(0, 1)
}, },
HashableValue::Bool { HashableValue::Bool {
val: true, val: true,
span: Span { span: Span::new(90, 1000)
start: 90,
end: 1000
}
} }
) )
} }
@ -299,7 +296,7 @@ mod test {
assert!(set.contains(&HashableValue::Bool { val: true, span })); assert!(set.contains(&HashableValue::Bool { val: true, span }));
// hashable value doesn't care about span. // hashable value doesn't care about span.
let diff_span = Span { start: 1, end: 2 }; let diff_span = Span::new(1, 2);
set.insert(HashableValue::Bool { set.insert(HashableValue::Bool {
val: true, val: true,
span: diff_span, span: diff_span,

View File

@ -1,10 +1,11 @@
use super::hashable_value::HashableValue; use super::hashable_value::HashableValue;
use itertools::Itertools;
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
Value, Type, Value,
}; };
use std::collections::HashMap; use std::collections::HashMap;
use std::iter; use std::iter;
@ -24,6 +25,7 @@ impl Command for Histogram {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("histogram") Signature::build("histogram")
.input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Table(vec![])),])
.optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list") .optional("column-name", SyntaxShape::String, "column name to calc frequency, no need to provide if input is just a list")
.optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output") .optional("frequency-column-name", SyntaxShape::String, "histogram's frequency column, default to be frequency column output")
.named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t')) .named("percentage-type", SyntaxShape::String, "percentage calculate method, can be 'normalize' or 'relative', in 'normalize', defaults to be 'normalize'", Some('t'))
@ -36,24 +38,49 @@ impl Command for Histogram {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Get a histogram for the types of files", description: "Compute a histogram of file types",
example: "ls | histogram type", example: "ls | histogram type",
result: None, result: None,
}, },
Example { Example {
description: description:
"Get a histogram for the types of files, with frequency column named freq", "Compute a histogram for the types of files, with frequency column named freq",
example: "ls | histogram type freq", example: "ls | histogram type freq",
result: None, result: None,
}, },
Example { Example {
description: "Get a histogram for a list of numbers", description: "Compute a histogram for a list of numbers",
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram", example: "[1 2 1] | histogram",
result: None, result: Some(Value::List {
vals: vec![Value::Record {
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
vals: vec![
Value::test_int(1),
Value::test_int(2),
Value::test_float(0.6666666666666666),
Value::test_string("66.67%"),
Value::test_string("******************************************************************"),
],
span: Span::test_data(),
},
Value::Record {
cols: vec!["value".to_string(), "count".to_string(), "quantile".to_string(), "percentage".to_string(), "frequency".to_string()],
vals: vec![
Value::test_int(2),
Value::test_int(1),
Value::test_float(0.3333333333333333),
Value::test_string("33.33%"),
Value::test_string("*********************************"),
],
span: Span::test_data(),
}],
span: Span::test_data(),
}
),
}, },
Example { Example {
description: "Get a histogram for a list of numbers, and percentage is based on the maximum value", description: "Compute a histogram for a list of numbers, and percentage is based on the maximum value",
example: "echo [1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative", example: "[1 2 3 1 1 1 2 2 1 1] | histogram --percentage-type relative",
result: None, result: None,
} }
] ]
@ -213,7 +240,7 @@ fn histogram_impl(
freq_column.to_string(), freq_column.to_string(),
]; ];
const MAX_FREQ_COUNT: f64 = 100.0; const MAX_FREQ_COUNT: f64 = 100.0;
for (val, count) in counter.into_iter() { for (val, count) in counter.into_iter().sorted() {
let quantile = match calc_method { let quantile = match calc_method {
PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64, PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64,
PercentageCalcMethod::Relative => count as f64 / max_cnt as f64, PercentageCalcMethod::Relative => count as f64 / max_cnt as f64,
@ -222,25 +249,33 @@ fn histogram_impl(
let percentage = format!("{:.2}%", quantile * 100_f64); let percentage = format!("{:.2}%", quantile * 100_f64);
let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize); let freq = "*".repeat((MAX_FREQ_COUNT * quantile).floor() as usize);
result.push(Value::Record { result.push((
cols: result_cols.clone(), count, // attach count first for easily sorting.
vals: vec![ Value::Record {
val.into_value(), cols: result_cols.clone(),
Value::Int { val: count, span }, vals: vec![
Value::Float { val.into_value(),
val: quantile, Value::Int { val: count, span },
span, Value::Float {
}, val: quantile,
Value::String { span,
val: percentage, },
span, Value::String {
}, val: percentage,
Value::String { val: freq, span }, span,
], },
span, Value::String { val: freq, span },
}); ],
span,
},
));
} }
Value::List { vals: result, span }.into_pipeline_data() result.sort_by(|a, b| b.0.cmp(&a.0));
Value::List {
vals: result.into_iter().map(|x| x.1).collect(),
span,
}
.into_pipeline_data()
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,8 +1,9 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Value, Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -18,7 +19,9 @@ impl Command for Fmt {
} }
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("fmt").category(Category::Conversions) Signature::build("fmt")
.input_output_types(vec![(Type::Number, Type::Record(vec![]))])
.category(Category::Conversions)
} }
fn search_terms(&self) -> Vec<&str> { fn search_terms(&self) -> Vec<&str> {
@ -41,38 +44,14 @@ impl Command for Fmt {
"upperhex".into(), "upperhex".into(),
], ],
vals: vec![ vals: vec![
Value::String { Value::string("0b101010", Span::test_data()),
val: "0b101010".to_string(), Value::string("42", Span::test_data()),
span: Span::test_data(), Value::string("42", Span::test_data()),
}, Value::string("4.2e1", Span::test_data()),
Value::String { Value::string("0x2a", Span::test_data()),
val: "42".to_string(), Value::string("0o52", Span::test_data()),
span: Span::test_data(), Value::string("4.2E1", Span::test_data()),
}, Value::string("0x2A", Span::test_data()),
Value::String {
val: "42".to_string(),
span: Span::test_data(),
},
Value::String {
val: "4.2e1".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0x2a".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0o52".to_string(),
span: Span::test_data(),
},
Value::String {
val: "4.2E1".to_string(),
span: Span::test_data(),
},
Value::String {
val: "0x2A".to_string(),
span: Span::test_data(),
},
], ],
span: Span::test_data(), span: Span::test_data(),
}), }),
@ -96,31 +75,12 @@ fn fmt(
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
} else {
let mut ret = v;
for path in &column_paths {
let r =
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
} }
pub fn action(input: &Value, span: Span) -> Value { fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input { match input {
Value::Int { val, .. } => fmt_it(*val, span), Value::Int { val, .. } => fmt_it(*val, span),
Value::Filesize { val, .. } => fmt_it(*val, span), Value::Filesize { val, .. } => fmt_it(*val, span),

View File

@ -1,9 +1,10 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
Value, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -16,10 +17,20 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into binary") Signature::build("into binary")
.input_output_types(vec![
(Type::Binary, Type::Binary),
(Type::Int, Type::Binary),
(Type::Number, Type::Binary),
(Type::String, Type::Binary),
(Type::Bool, Type::Binary),
(Type::Filesize, Type::Binary),
(Type::Date, Type::Binary),
])
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to binary (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -100,7 +111,7 @@ fn into_binary(
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head; let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
match input { match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary { PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
@ -120,27 +131,10 @@ fn into_binary(
} }
.into_pipeline_data()) .into_pipeline_data())
} }
_ => input.map( _ => {
move |v| { let arg = CellPathOnlyArgs::from(cell_paths);
if column_paths.is_empty() { operate(action, arg, input, call.head, engine_state.ctrlc.clone())
action(&v, head) }
} else {
let mut ret = v;
for path in &column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
} }
} }
@ -160,7 +154,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
} }
} }
pub fn action(input: &Value, span: Span) -> Value { pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input { match input {
Value::Binary { .. } => input.clone(), Value::Binary { .. } => input.clone(),
Value::Int { val, .. } => Value::Binary { Value::Int { val, .. } => Value::Binary {
@ -180,7 +174,7 @@ pub fn action(input: &Value, span: Span) -> Value {
span, span,
}, },
Value::Bool { val, .. } => Value::Binary { Value::Bool { val, .. } => Value::Binary {
val: int_to_endian(if *val { 1i64 } else { 0 }), val: int_to_endian(i64::from(*val)),
span, span,
}, },
Value::Date { val, .. } => Value::Binary { Value::Date { val, .. } => Value::Binary {

View File

@ -1,8 +1,9 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,10 +16,17 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into bool") Signature::build("into bool")
.input_output_types(vec![
(Type::Int, Type::Bool),
(Type::Number, Type::Bool),
(Type::String, Type::Bool),
(Type::Bool, Type::Bool),
(Type::List(Box::new(Type::Any)), Type::Table(vec![])),
])
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to boolean (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -46,7 +54,7 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert value to boolean in table", description: "Convert value to boolean in table",
example: "echo [[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value", example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::Record { Value::Record {
@ -88,6 +96,11 @@ impl Command for SubCommand {
example: "1 | into bool", example: "1 | into bool",
result: Some(Value::boolean(true, span)), result: Some(Value::boolean(true, span)),
}, },
Example {
description: "convert decimal to boolean",
example: "0.3 | into bool",
result: Some(Value::boolean(true, span)),
},
Example { Example {
description: "convert decimal string to boolean", description: "convert decimal string to boolean",
example: "'0.0' | into bool", example: "'0.0' | into bool",
@ -108,28 +121,9 @@ fn into_bool(
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
} else {
let mut ret = v;
for path in &column_paths {
let r =
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
} }
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> { fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
@ -154,7 +148,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
} }
} }
fn action(input: &Value, span: Span) -> Value { fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input { match input {
Value::Bool { .. } => input.clone(), Value::Bool { .. } => input.clone(),
Value::Int { val, .. } => Value::Bool { Value::Int { val, .. } => Value::Bool {

View File

@ -29,7 +29,13 @@ impl Command for Into {
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String { Ok(Value::String {
val: get_full_help(&Into.signature(), &[], engine_state, stack), val: get_full_help(
&Into.signature(),
&[],
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head, span: call.head,
} }
.into_pipeline_data()) .into_pipeline_data())

View File

@ -1,18 +1,25 @@
use crate::input_handler::{operate, CmdArgument};
use crate::{generate_strftime_list, parse_date_from_string}; use crate::{generate_strftime_list, parse_date_from_string};
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc}; use chrono::{DateTime, FixedOffset, Local, LocalResult, TimeZone, Utc};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value,
}; };
struct Arguments { struct Arguments {
timezone: Option<Spanned<String>>, zone_options: Option<Spanned<Zone>>,
offset: Option<Spanned<i64>>, format_options: Option<DatetimeFormat>,
format: Option<String>, cell_paths: Option<Vec<CellPath>>,
column_paths: Vec<CellPath>, }
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
} }
// In case it may be confused with chrono::TimeZone // In case it may be confused with chrono::TimeZone
@ -57,7 +64,11 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into datetime") Signature::build("into datetime")
.named( .input_output_types(vec![
(Type::Int, Type::Date),
(Type::String, Type::Date),
])
.named(
"timezone", "timezone",
SyntaxShape::String, SyntaxShape::String,
"Specify timezone if the input is a Unix timestamp. Valid options: 'UTC' ('u') or 'LOCAL' ('l')", "Specify timezone if the input is a Unix timestamp. Valid options: 'UTC' ('u') or 'LOCAL' ('l')",
@ -83,7 +94,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"optionally convert text into datetime by column paths", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -95,7 +106,36 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) if call.has_flag("list") {
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
} else {
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
// if zone-offset is specified, then zone will be neglected
let timezone = call.get_flag::<Spanned<String>>(engine_state, stack, "timezone")?;
let zone_options =
match &call.get_flag::<Spanned<i64>>(engine_state, stack, "offset")? {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let format_options = call
.get_flag::<String>(engine_state, stack, "format")?
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
let args = Arguments {
format_options,
zone_options,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -107,38 +147,63 @@ impl Command for SubCommand {
} }
fn examples(&self) -> Vec<Example> { 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_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(),
),
}),
}
};
vec![ vec![
Example { Example {
description: "Convert to datetime", description: "Convert to datetime",
example: "'27.02.2021 1:55 pm +0000' | into datetime", example: "'27.02.2021 1:55 pm +0000' | into datetime",
result: Some(Value::Date { result: example_result_1(1614434100,0)
val: Utc.timestamp(1614434100, 0).into(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Convert to datetime", description: "Convert to datetime",
example: "'2021-02-27T13:55:40+00:00' | into datetime", example: "'2021-02-27T13:55:40+00:00' | into datetime",
result: Some(Value::Date { result: example_result_1(1614434140, 0)
val: Utc.timestamp(1614434140, 0).into(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Convert to datetime using a custom format", description: "Convert to datetime using a custom format",
example: "'20210227_135540+0000' | into datetime -f '%Y%m%d_%H%M%S%z'", example: "'20210227_135540+0000' | into datetime -f '%Y%m%d_%H%M%S%z'",
result: Some(Value::Date { result: example_result_1(1614434140, 0)
val: Utc.timestamp(1614434140, 0).into(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Convert timestamp (no larger than 8e+12) to a UTC datetime", description: "Convert timestamp (no larger than 8e+12) to a UTC datetime",
example: "1614434140 | into datetime", example: "1614434140 | into datetime",
result: Some(Value::Date { result: example_result_1(1614434140, 0)
val: Utc.timestamp(1614434140, 0).into(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: description:
@ -150,10 +215,7 @@ impl Command for SubCommand {
description: description:
"Convert timestamps like the sqlite history t", "Convert timestamps like the sqlite history t",
example: "1656165681720 | into datetime", example: "1656165681720 | into datetime",
result: Some(Value::Date { result: example_result_2(1656165681720)
val: Utc.timestamp_millis(1656165681720).into(),
span: Span::test_data(),
}),
}, },
] ]
} }
@ -162,72 +224,9 @@ impl Command for SubCommand {
#[derive(Clone)] #[derive(Clone)]
struct DatetimeFormat(String); struct DatetimeFormat(String);
fn operate( fn action(input: &Value, args: &Arguments, head: Span) -> Value {
engine_state: &EngineState, let timezone = &args.zone_options;
stack: &mut Stack, let dateformat = &args.format_options;
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let options = Arguments {
timezone: call.get_flag(engine_state, stack, "timezone")?,
offset: call.get_flag(engine_state, stack, "offset")?,
format: call.get_flag(engine_state, stack, "format")?,
column_paths: call.rest(engine_state, stack, 0)?,
};
// if zone-offset is specified, then zone will be neglected
let zone_options = match &options.offset {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => options.timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let list_flag = call.has_flag("list");
let format_options = options
.format
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
input.map(
move |v| {
if options.column_paths.is_empty() && !list_flag {
action(&v, &zone_options, &format_options, head)
} else if list_flag {
generate_strftime_list(head, true)
} else {
let mut ret = v;
for path in &options.column_paths {
let zone_options = zone_options.clone();
let format_options = format_options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &zone_options, &format_options, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(
input: &Value,
timezone: &Option<Spanned<Zone>>,
dateformat: &Option<DatetimeFormat>,
head: Span,
) -> Value {
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?) // Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
let timestamp = match input { let timestamp = match input {
Value::Int { val, .. } => Ok(*val), Value::Int { val, .. } => Ok(*val),
@ -262,8 +261,31 @@ fn action(
// be able to convert chrono::Utc::now() // be able to convert chrono::Utc::now()
let dt = match ts.to_string().len() { let dt = match ts.to_string().len() {
x if x > 13 => Utc.timestamp_nanos(ts).into(), x if x > 13 => Utc.timestamp_nanos(ts).into(),
x if x > 10 => Utc.timestamp_millis(ts).into(), x if x > 10 => match Utc.timestamp_millis_opt(ts) {
_ => Utc.timestamp(ts, 0).into(), 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 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 { Value::Date {
@ -272,28 +294,64 @@ fn action(
} }
} }
Some(Spanned { item, span }) => match item { Some(Spanned { item, span }) => match item {
Zone::Utc => Value::Date { Zone::Utc => match Utc.timestamp_opt(ts, 0) {
val: Utc.timestamp(ts, 0).into(), LocalResult::Single(val) => Value::Date {
span: head, val: val.into(),
},
Zone::Local => Value::Date {
val: Local.timestamp(ts, 0).into(),
span: head,
},
Zone::East(i) => {
let eastoffset = FixedOffset::east((*i as i32) * HOUR);
Value::Date {
val: eastoffset.timestamp(ts, 0),
span: head, span: head,
} },
} _ => Value::Error {
Zone::West(i) => { error: ShellError::UnsupportedInput(
let westoffset = FixedOffset::west((*i as i32) * HOUR); "The given local datetime representation is invalid.".to_string(),
Value::Date { *span,
val: westoffset.timestamp(ts, 0), ),
},
},
Zone::Local => match Local.timestamp_opt(ts, 0) {
LocalResult::Single(val) => Value::Date {
val: val.into(),
span: head, span: head,
} },
} _ => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
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,
),
},
},
None => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*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,
),
},
},
None => Value::Error {
error: ShellError::UnsupportedInput(
"The given local datetime representation is invalid.".to_string(),
*span,
),
},
},
Zone::Error => Value::Error { Zone::Error => Value::Error {
error: ShellError::UnsupportedInput( error: ShellError::UnsupportedInput(
"Cannot convert given timezone or offset to timestamp".to_string(), "Cannot convert given timezone or offset to timestamp".to_string(),
@ -359,7 +417,12 @@ mod tests {
fn takes_a_date_format() { fn takes_a_date_format() {
let date_str = Value::test_string("16.11.1984 8:00 am +0000"); let date_str = Value::test_string("16.11.1984 8:00 am +0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string())); let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data()); let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z") val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
.unwrap(), .unwrap(),
@ -371,7 +434,12 @@ mod tests {
#[test] #[test]
fn takes_iso8601_date_format() { fn takes_iso8601_date_format() {
let date_str = Value::test_string("2020-08-04T16:39:18+00:00"); let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
let actual = action(&date_str, &None, &None, Span::test_data()); let args = Arguments {
zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z") val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
.unwrap(), .unwrap(),
@ -387,7 +455,12 @@ mod tests {
item: Zone::East(8), item: Zone::East(8),
span: Span::test_data(), span: Span::test_data(),
}); });
let actual = action(&date_str, &timezone_option, &None, Span::test_data()); let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z") val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(), .unwrap(),
@ -404,7 +477,12 @@ mod tests {
item: Zone::East(8), item: Zone::East(8),
span: Span::test_data(), span: Span::test_data(),
}); });
let actual = action(&date_int, &timezone_option, &None, Span::test_data()); let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_int, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z") val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(), .unwrap(),
@ -421,9 +499,14 @@ mod tests {
item: Zone::Local, item: Zone::Local,
span: Span::test_data(), span: Span::test_data(),
}); });
let actual = action(&date_str, &timezone_option, &None, Span::test_data()); let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: Local.timestamp(1614434140, 0).into(), val: Local.timestamp_opt(1614434140, 0).unwrap().into(),
span: Span::test_data(), span: Span::test_data(),
}; };
@ -433,11 +516,15 @@ mod tests {
#[test] #[test]
fn takes_timestamp_without_timezone() { fn takes_timestamp_without_timezone() {
let date_str = Value::test_string("1614434140"); let date_str = Value::test_string("1614434140");
let timezone_option = None; let args = Arguments {
let actual = action(&date_str, &timezone_option, &None, Span::test_data()); zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date { let expected = Value::Date {
val: Utc.timestamp(1614434140, 0).into(), val: Utc.timestamp_opt(1614434140, 0).unwrap().into(),
span: Span::test_data(), span: Span::test_data(),
}; };
@ -451,7 +538,12 @@ mod tests {
item: Zone::Utc, item: Zone::Utc,
span: Span::test_data(), span: Span::test_data(),
}); });
let actual = action(&date_str, &timezone_option, &None, Span::test_data()); let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error); assert_eq!(actual.get_type(), Error);
} }
@ -460,7 +552,12 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_datetimelike_string() { fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000"); let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string())); let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data()); let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error); assert_eq!(actual.get_type(), Error);
} }

View File

@ -1,8 +1,9 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,11 +15,16 @@ impl Command for SubCommand {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into decimal").rest( Signature::build("into decimal")
"rest", .input_output_types(vec![
SyntaxShape::CellPath, (Type::String, Type::Number),
"optionally convert text into decimal by column paths", (Type::Bool, Type::Number),
) ])
.rest(
"rest",
SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths",
)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -36,7 +42,9 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operate(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -72,43 +80,13 @@ impl Command for SubCommand {
} }
} }
fn operate( fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
} else {
let mut ret = v;
for path in &column_paths {
let r =
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, head: Span) -> Value {
match input { match input {
Value::String { val: s, span } => { Value::String { val: s, span } => {
let other = s.trim(); let other = s.trim();
match other.parse::<f64>() { match other.parse::<f64>() {
Ok(x) => Value::Float { val: x, span: head }, Ok(x) => Value::float(x, head),
Err(reason) => Value::Error { Err(reason) => Value::Error {
error: ShellError::CantConvert( error: ShellError::CantConvert(
"float".to_string(), "float".to_string(),
@ -119,10 +97,7 @@ fn action(input: &Value, head: Span) -> Value {
}, },
} }
} }
Value::Int { val: v, span } => Value::Float { Value::Int { val: v, span } => Value::float(*v as f64, *span),
val: *v as f64,
span: *span,
},
Value::Bool { val: b, span } => Value::Float { Value::Bool { val: b, span } => Value::Float {
val: match b { val: match b {
true => 1.0, true => 1.0,
@ -163,7 +138,7 @@ mod tests {
let word = Value::test_string("3.1415"); let word = Value::test_string("3.1415");
let expected = Value::test_float(3.1415); let expected = Value::test_float(3.1415);
let actual = action(&word, Span::test_data()); let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -171,7 +146,11 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_decimallike_string() { fn communicates_parsing_error_given_an_invalid_decimallike_string() {
let decimal_str = Value::test_string("11.6anra"); let decimal_str = Value::test_string("11.6anra");
let actual = action(&decimal_str, Span::test_data()); let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual.get_type(), Error); assert_eq!(actual.get_type(), Error);
} }
@ -180,7 +159,11 @@ mod tests {
fn int_to_decimal() { fn int_to_decimal() {
let decimal_str = Value::test_int(10); let decimal_str = Value::test_int(10);
let expected = Value::test_float(10.0); let expected = Value::test_float(10.0);
let actual = action(&decimal_str, Span::test_data()); let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }

View File

@ -3,7 +3,7 @@ use nu_parser::parse_duration_bytes;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath, Expr}, ast::{Call, CellPath, Expr},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Unit, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Unit,
Value, Value,
}; };
@ -17,6 +17,13 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into duration") Signature::build("into duration")
.input_output_types(vec![
(Type::String, Type::Duration),
(Type::Duration, Type::Duration),
// TODO: --convert option should be implemented as `format duration`
(Type::String, Type::String),
(Type::Duration, Type::String),
])
.named( .named(
"convert", "convert",
SyntaxShape::String, SyntaxShape::String,
@ -26,7 +33,7 @@ impl Command for SubCommand {
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to duration (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -36,7 +43,7 @@ impl Command for SubCommand {
} }
fn extra_usage(&self) -> &str { fn extra_usage(&self) -> &str {
"into duration does not take leap years into account and every month is calculated with 30 days" "This command does not take leap years into account, and every month is assumed to have 30 days."
} }
fn search_terms(&self) -> Vec<&str> { fn search_terms(&self) -> Vec<&str> {
@ -58,7 +65,8 @@ impl Command for SubCommand {
vec![ vec![
Example { Example {
description: "Convert string to duration in table", description: "Convert string to duration in table",
example: "echo [[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value", example:
"[[value]; ['1sec'] ['2min'] ['3hr'] ['4day'] ['5wk']] | into duration value",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::Record { Value::Record {
@ -114,14 +122,29 @@ impl Command for SubCommand {
}), }),
}, },
Example { Example {
description: "Convert string to a named duration", description: "Convert string to the requested duration as a string",
example: "'7min' | into duration --convert sec", example: "'7min' | into duration --convert sec",
result: Some(Value::String { result: Some(Value::String {
val: "420 sec".to_string(), val: "420 sec".to_string(),
span, span,
}), }),
}, },
Example {
description: "Convert duration to duration",
example: "420sec | into duration",
result: Some(Value::Duration {
val: 7 * 60 * 1000 * 1000 * 1000,
span,
}),
},
Example {
description: "Convert duration to the requested duration as a string",
example: "420sec | into duration --convert ms",
result: Some(Value::String {
val: "420000 ms".to_string(),
span,
}),
},
] ]
} }
} }
@ -135,18 +158,20 @@ fn into_duration(
let head = call.head; let head = call.head;
let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?; let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config();
let float_precision = config.float_precision as usize;
input.map( input.map(
move |v| { move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
action(&v, &convert_to_unit, head) action(&v, &convert_to_unit, float_precision, head)
} else { } else {
let mut ret = v; let mut ret = v;
for path in &column_paths { for path in &column_paths {
let d = convert_to_unit.clone(); let d = convert_to_unit.clone();
let r = ret.update_cell_path( let r = ret.update_cell_path(
&path.members, &path.members,
Box::new(move |old| action(old, &d, head)), Box::new(move |old| action(old, &d, float_precision, head)),
); );
if let Err(error) = r { if let Err(error) = r {
return Value::Error { error }; return Value::Error { error };
@ -166,127 +191,129 @@ fn convert_str_from_unit_to_unit(
to_unit: &str, to_unit: &str,
span: Span, span: Span,
value_span: Span, value_span: Span,
) -> Result<i64, ShellError> { ) -> Result<f64, ShellError> {
match (from_unit, to_unit) { match (from_unit, to_unit) {
("ns", "ns") => Ok(val), ("ns", "ns") => Ok(val as f64),
("ns", "us") => Ok(val / 1000), ("ns", "us") => Ok(val as f64 / 1000.0),
("ns", "ms") => Ok(val / 1000 / 1000), ("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
("ns", "sec") => Ok(val / 1000 / 1000 / 1000), ("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
("ns", "min") => Ok(val / 1000 / 1000 / 1000 / 60), ("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
("ns", "hr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60), ("ns", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0),
("ns", "day") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24), ("ns", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("ns", "wk") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 7), ("ns", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ns", "month") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 30), ("ns", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ns", "yr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365), ("ns", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ns", "dec") => Ok(val / 10 / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365), ("ns", "dec") => {
Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0)
}
("us", "ns") => Ok(val * 1000), ("us", "ns") => Ok(val as f64 * 1000.0),
("us", "us") => Ok(val), ("us", "us") => Ok(val as f64),
("us", "ms") => Ok(val / 1000), ("us", "ms") => Ok(val as f64 / 1000.0),
("us", "sec") => Ok(val / 1000 / 1000), ("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("us", "min") => Ok(val / 1000 / 1000 / 60), ("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
("us", "hr") => Ok(val / 1000 / 1000 / 60 / 60), ("us", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
("us", "day") => Ok(val / 1000 / 1000 / 60 / 60 / 24), ("us", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("us", "wk") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 7), ("us", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("us", "month") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 30), ("us", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("us", "yr") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 365), ("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("us", "dec") => Ok(val / 10 / 1000 / 1000 / 60 / 60 / 24 / 365), ("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "ns") => Ok(val * 1000 * 1000), ("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
("ms", "us") => Ok(val * 1000), ("ms", "us") => Ok(val as f64 * 1000.0),
("ms", "ms") => Ok(val), ("ms", "ms") => Ok(val as f64),
("ms", "sec") => Ok(val / 1000), ("ms", "sec") => Ok(val as f64 / 1000.0),
("ms", "min") => Ok(val / 1000 / 60), ("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
("ms", "hr") => Ok(val / 1000 / 60 / 60), ("ms", "hr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0),
("ms", "day") => Ok(val / 1000 / 60 / 60 / 24), ("ms", "day") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0),
("ms", "wk") => Ok(val / 1000 / 60 / 60 / 24 / 7), ("ms", "wk") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ms", "month") => Ok(val / 1000 / 60 / 60 / 24 / 30), ("ms", "month") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ms", "yr") => Ok(val / 1000 / 60 / 60 / 24 / 365), ("ms", "yr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "dec") => Ok(val / 10 / 1000 / 60 / 60 / 24 / 365), ("ms", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "ns") => Ok(val * 1000 * 1000 * 1000), ("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
("sec", "us") => Ok(val * 1000 * 1000), ("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
("sec", "ms") => Ok(val * 1000), ("sec", "ms") => Ok(val as f64 * 1000.0),
("sec", "sec") => Ok(val), ("sec", "sec") => Ok(val as f64),
("sec", "min") => Ok(val / 60), ("sec", "min") => Ok(val as f64 / 60.0),
("sec", "hr") => Ok(val / 60 / 60), ("sec", "hr") => Ok(val as f64 / 60.0 / 60.0),
("sec", "day") => Ok(val / 60 / 60 / 24), ("sec", "day") => Ok(val as f64 / 60.0 / 60.0 / 24.0),
("sec", "wk") => Ok(val / 60 / 60 / 24 / 7), ("sec", "wk") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 7.0),
("sec", "month") => Ok(val / 60 / 60 / 24 / 30), ("sec", "month") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 30.0),
("sec", "yr") => Ok(val / 60 / 60 / 24 / 365), ("sec", "yr") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "dec") => Ok(val / 10 / 60 / 60 / 24 / 365), ("sec", "dec") => Ok(val as f64 / 10.0 / 60.0 / 60.0 / 24.0 / 365.0),
("min", "ns") => Ok(val * 1000 * 1000 * 1000 * 60), ("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
("min", "us") => Ok(val * 1000 * 1000 * 60), ("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
("min", "ms") => Ok(val * 1000 * 60), ("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
("min", "sec") => Ok(val * 60), ("min", "sec") => Ok(val as f64 * 60.0),
("min", "min") => Ok(val), ("min", "min") => Ok(val as f64),
("min", "hr") => Ok(val / 60), ("min", "hr") => Ok(val as f64 / 60.0),
("min", "day") => Ok(val / 60 / 24), ("min", "day") => Ok(val as f64 / 60.0 / 24.0),
("min", "wk") => Ok(val / 60 / 24 / 7), ("min", "wk") => Ok(val as f64 / 60.0 / 24.0 / 7.0),
("min", "month") => Ok(val / 60 / 24 / 30), ("min", "month") => Ok(val as f64 / 60.0 / 24.0 / 30.0),
("min", "yr") => Ok(val / 60 / 24 / 365), ("min", "yr") => Ok(val as f64 / 60.0 / 24.0 / 365.0),
("min", "dec") => Ok(val / 10 / 60 / 24 / 365), ("min", "dec") => Ok(val as f64 / 10.0 / 60.0 / 24.0 / 365.0),
("hr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60), ("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "us") => Ok(val * 1000 * 1000 * 60 * 60), ("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "ms") => Ok(val * 1000 * 60 * 60), ("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
("hr", "sec") => Ok(val * 60 * 60), ("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
("hr", "min") => Ok(val * 60), ("hr", "min") => Ok(val as f64 * 60.0),
("hr", "hr") => Ok(val), ("hr", "hr") => Ok(val as f64),
("hr", "day") => Ok(val / 24), ("hr", "day") => Ok(val as f64 / 24.0),
("hr", "wk") => Ok(val / 24 / 7), ("hr", "wk") => Ok(val as f64 / 24.0 / 7.0),
("hr", "month") => Ok(val / 24 / 30), ("hr", "month") => Ok(val as f64 / 24.0 / 30.0),
("hr", "yr") => Ok(val / 24 / 365), ("hr", "yr") => Ok(val as f64 / 24.0 / 365.0),
("hr", "dec") => Ok(val / 10 / 24 / 365), ("hr", "dec") => Ok(val as f64 / 10.0 / 24.0 / 365.0),
("day", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24), ("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24), ("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "ms") => Ok(val * 1000 * 60 * 60 * 24), ("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "sec") => Ok(val * 60 * 60 * 24), ("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
("day", "min") => Ok(val * 60 * 24), ("day", "min") => Ok(val as f64 * 60.0 * 24.0),
("day", "hr") => Ok(val * 24), ("day", "hr") => Ok(val as f64 * 24.0),
("day", "day") => Ok(val), ("day", "day") => Ok(val as f64),
("day", "wk") => Ok(val / 7), ("day", "wk") => Ok(val as f64 / 7.0),
("day", "month") => Ok(val / 30), ("day", "month") => Ok(val as f64 / 30.0),
("day", "yr") => Ok(val / 365), ("day", "yr") => Ok(val as f64 / 365.0),
("day", "dec") => Ok(val / 10 / 365), ("day", "dec") => Ok(val as f64 / 10.0 / 365.0),
("wk", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7), ("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 7), ("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 7), ("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "sec") => Ok(val * 60 * 60 * 24 * 7), ("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "min") => Ok(val * 60 * 24 * 7), ("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
("wk", "hr") => Ok(val * 24 * 7), ("wk", "hr") => Ok(val as f64 * 24.0 * 7.0),
("wk", "day") => Ok(val * 7), ("wk", "day") => Ok(val as f64 * 7.0),
("wk", "wk") => Ok(val), ("wk", "wk") => Ok(val as f64),
("wk", "month") => Ok(val / 4), ("wk", "month") => Ok(val as f64 / 4.0),
("wk", "yr") => Ok(val / 52), ("wk", "yr") => Ok(val as f64 / 52.0),
("wk", "dec") => Ok(val / 10 / 52), ("wk", "dec") => Ok(val as f64 / 10.0 / 52.0),
("month", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 30), ("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 30), ("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 30), ("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "sec") => Ok(val * 60 * 60 * 24 * 30), ("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "min") => Ok(val * 60 * 24 * 30), ("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
("month", "hr") => Ok(val * 24 * 30), ("month", "hr") => Ok(val as f64 * 24.0 * 30.0),
("month", "day") => Ok(val * 30), ("month", "day") => Ok(val as f64 * 30.0),
("month", "wk") => Ok(val * 4), ("month", "wk") => Ok(val as f64 * 4.0),
("month", "month") => Ok(val), ("month", "month") => Ok(val as f64),
("month", "yr") => Ok(val / 12), ("month", "yr") => Ok(val as f64 / 12.0),
("month", "dec") => Ok(val / 10 / 12), ("month", "dec") => Ok(val as f64 / 10.0 / 12.0),
("yr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 365), ("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 365), ("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 365), ("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "sec") => Ok(val * 60 * 60 * 24 * 365), ("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "min") => Ok(val * 60 * 24 * 365), ("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
("yr", "hr") => Ok(val * 24 * 365), ("yr", "hr") => Ok(val as f64 * 24.0 * 365.0),
("yr", "day") => Ok(val * 365), ("yr", "day") => Ok(val as f64 * 365.0),
("yr", "wk") => Ok(val * 52), ("yr", "wk") => Ok(val as f64 * 52.0),
("yr", "month") => Ok(val * 12), ("yr", "month") => Ok(val as f64 * 12.0),
("yr", "yr") => Ok(val), ("yr", "yr") => Ok(val as f64),
("yr", "dec") => Ok(val / 10), ("yr", "dec") => Ok(val as f64 / 10.0),
_ => Err(ShellError::CantConvertWithValue( _ => Err(ShellError::CantConvertWithValue(
"string duration".to_string(), "string duration".to_string(),
@ -369,19 +396,41 @@ fn string_to_unit_duration(
)) ))
} }
fn action(input: &Value, convert_to_unit: &Option<Spanned<String>>, span: Span) -> Value { fn action(
input: &Value,
convert_to_unit: &Option<Spanned<String>>,
float_precision: usize,
span: Span,
) -> Value {
match input { match input {
Value::Duration { Value::Duration {
val: _val_num, val: val_num,
span: _value_span, span: value_span,
} => { } => {
if let Some(_to_unit) = convert_to_unit { if let Some(to_unit) = convert_to_unit {
Value::Error { let from_unit = "ns";
error: ShellError::UnsupportedInput( let duration = *val_num;
"Cannot convert from a Value::Duration right now. Try making it a string." match convert_str_from_unit_to_unit(
.into(), duration,
span, from_unit,
), &to_unit.item,
span,
*value_span,
) {
Ok(d) => {
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
}
Err(e) => Value::Error { error: e },
} }
} else { } else {
input.clone() input.clone()
@ -402,10 +451,19 @@ fn action(input: &Value, convert_to_unit: &Option<Spanned<String>>, span: Span)
span, span,
*value_span, *value_span,
) { ) {
Ok(d) => Value::String { Ok(d) => {
val: format!("{} {}", d, &to_unit.item), if d.fract() == 0.0 {
span: *value_span, Value::String {
}, val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
}
Err(e) => Value::Error { error: e }, Err(e) => Value::Error { error: e },
} }
} else { } else {
@ -445,18 +503,18 @@ mod test {
#[test] #[test]
fn turns_ns_to_duration() { fn turns_ns_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3ns"); let word = Value::test_string("3ns");
let expected = Value::Duration { val: 3, span }; let expected = Value::Duration { val: 3, span };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_us_to_duration() { fn turns_us_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("4us"); let word = Value::test_string("4us");
let expected = Value::Duration { let expected = Value::Duration {
val: 4 * 1000, val: 4 * 1000,
@ -464,13 +522,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_ms_to_duration() { fn turns_ms_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("5ms"); let word = Value::test_string("5ms");
let expected = Value::Duration { let expected = Value::Duration {
val: 5 * 1000 * 1000, val: 5 * 1000 * 1000,
@ -478,13 +536,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_sec_to_duration() { fn turns_sec_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("1sec"); let word = Value::test_string("1sec");
let expected = Value::Duration { let expected = Value::Duration {
val: 1000 * 1000 * 1000, val: 1000 * 1000 * 1000,
@ -492,13 +550,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_min_to_duration() { fn turns_min_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("7min"); let word = Value::test_string("7min");
let expected = Value::Duration { let expected = Value::Duration {
val: 7 * 60 * 1000 * 1000 * 1000, val: 7 * 60 * 1000 * 1000 * 1000,
@ -506,13 +564,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_hr_to_duration() { fn turns_hr_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 3);
let word = Value::test_string("42hr"); let word = Value::test_string("42hr");
let expected = Value::Duration { let expected = Value::Duration {
val: 42 * 60 * 60 * 1000 * 1000 * 1000, val: 42 * 60 * 60 * 1000 * 1000 * 1000,
@ -520,13 +578,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_day_to_duration() { fn turns_day_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 5);
let word = Value::test_string("123day"); let word = Value::test_string("123day");
let expected = Value::Duration { let expected = Value::Duration {
val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000,
@ -534,13 +592,13 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_wk_to_duration() { fn turns_wk_to_duration() {
let span = Span::test_data(); let span = Span::new(0, 2);
let word = Value::test_string("3wk"); let word = Value::test_string("3wk");
let expected = Value::Duration { let expected = Value::Duration {
val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000, val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000,
@ -548,7 +606,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
} }

View File

@ -1,8 +1,9 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -15,10 +16,16 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into filesize") Signature::build("into filesize")
.input_output_types(vec![
(Type::Int, Type::Filesize),
(Type::Number, Type::Filesize),
(Type::String, Type::Filesize),
(Type::Filesize, Type::Filesize),
])
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to filesize (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -38,7 +45,9 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_filesize(engine_state, stack, call, input) let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -84,37 +93,7 @@ impl Command for SubCommand {
} }
} }
fn into_filesize( pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
} else {
let mut ret = v;
for path in &column_paths {
let r =
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span) -> Value {
if let Ok(value_span) = input.span() { if let Ok(value_span) = input.span() {
match input { match input {
Value::Filesize { .. } => input.clone(), Value::Filesize { .. } => input.clone(),

View File

@ -1,16 +1,23 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
}; };
struct Arguments { struct Arguments {
radix: Option<Value>, radix: u32,
column_paths: Vec<CellPath>, cell_paths: Option<Vec<CellPath>>,
little_endian: bool, little_endian: bool,
} }
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
@ -21,12 +28,22 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into int") Signature::build("into int")
.input_output_types(vec![
(Type::String, Type::Int),
(Type::Number, Type::Int),
(Type::Bool, Type::Int),
// Unix timestamp in seconds
(Type::Date, Type::Int),
// TODO: Users should do this by dividing a Filesize by a Filesize explicitly
(Type::Filesize, Type::Int),
])
.vectorizes_over_list(true)
.named("radix", SyntaxShape::Number, "radix of integer", Some('r')) .named("radix", SyntaxShape::Number, "radix of integer", Some('r'))
.switch("little-endian", "use little-endian byte decoding", None) .switch("little-endian", "use little-endian byte decoding", None)
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to int (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.category(Category::Conversions) .category(Category::Conversions)
} }
@ -46,14 +63,36 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_int(engine_state, stack, call, input) let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
let radix: u32 = match radix {
Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
span,
));
}
val as u32
}
Some(_) => 10,
None => 10,
};
let args = Arguments {
radix,
little_endian: call.has_flag("little-endian"),
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Convert string to integer in table", description: "Convert string to integer in table",
example: "echo [[num]; ['-5'] [4] [1.5]] | into int num", example: "[[num]; ['-5'] [4] [1.5]] | into int num",
result: None, result: None,
}, },
Example { Example {
@ -74,10 +113,7 @@ impl Command for SubCommand {
Example { Example {
description: "Convert file size to integer", description: "Convert file size to integer",
example: "4KB | into int", example: "4KB | into int",
result: Some(Value::Int { result: Some(Value::int(4000, Span::test_data())),
val: 4000,
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Convert bool to integer", description: "Convert bool to integer",
@ -121,59 +157,9 @@ impl Command for SubCommand {
} }
} }
fn into_int( fn action(input: &Value, args: &Arguments, span: Span) -> Value {
engine_state: &EngineState, let radix = args.radix;
stack: &mut Stack, let little_endian = args.little_endian;
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?,
};
let radix: u32 = match options.radix {
Some(Value::Int { val, .. }) => val as u32,
Some(_) => 10,
None => 10,
};
if let Some(val) = &options.radix {
if !(2..=36).contains(&radix) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
val.span()?,
));
}
}
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, head, radix, options.little_endian)
} else {
let mut ret = v;
for path in &options.column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, head, radix, options.little_endian)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
match input { match input {
Value::Int { val: _, .. } => { Value::Int { val: _, .. } => {
if radix == 10 { if radix == 10 {
@ -244,20 +230,14 @@ pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Val
} }
val.resize(8, 0); val.resize(8, 0);
Value::Int { Value::int(LittleEndian::read_i64(&val), *span)
val: LittleEndian::read_i64(&val),
span: *span,
}
} else { } else {
while val.len() < 8 { while val.len() < 8 {
val.insert(0, 0); val.insert(0, 0);
} }
val.resize(8, 0); val.resize(8, 0);
Value::Int { Value::int(BigEndian::read_i64(&val), *span)
val: BigEndian::read_i64(&val),
span: *span,
}
} }
} }
_ => Value::Error { _ => Value::Error {
@ -280,13 +260,13 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
// octal // octal
{ {
match int_from_string(val, head) { match int_from_string(val, head) {
Ok(x) => return Value::Int { val: x, span: head }, Ok(x) => return Value::int(x, head),
Err(e) => return Value::Error { error: e }, Err(e) => return Value::Error { error: e },
} }
} else if val.starts_with("00") { } else if val.starts_with("00") {
// It's a padded string // It's a padded string
match i64::from_str_radix(val, radix) { match i64::from_str_radix(val, radix) {
Ok(n) => return Value::Int { val: n, span: head }, Ok(n) => return Value::int(n, head),
Err(e) => { Err(e) => {
return Value::Error { return Value::Error {
error: ShellError::CantConvert( error: ShellError::CantConvert(
@ -311,7 +291,7 @@ fn convert_int(input: &Value, head: Span, radix: u32) -> Value {
} }
}; };
match i64::from_str_radix(i.trim(), radix) { match i64::from_str_radix(i.trim(), radix) {
Ok(n) => Value::Int { val: n, span: head }, Ok(n) => Value::int(n, head),
Err(_reason) => Value::Error { Err(_reason) => Value::Error {
error: ShellError::CantConvert("string".to_string(), "int".to_string(), head, None), error: ShellError::CantConvert("string".to_string(), "int".to_string(), head, None),
}, },
@ -401,21 +381,45 @@ mod test {
let word = Value::test_string("10"); let word = Value::test_string("10");
let expected = Value::test_int(10); let expected = Value::test_int(10);
let actual = action(&word, Span::test_data(), 10, false); let actual = action(
&word,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn turns_binary_to_integer() { fn turns_binary_to_integer() {
let s = Value::test_string("0b101"); let s = Value::test_string("0b101");
let actual = action(&s, Span::test_data(), 10, false); let actual = action(
&s,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(5)); assert_eq!(actual, Value::test_int(5));
} }
#[test] #[test]
fn turns_hex_to_integer() { fn turns_hex_to_integer() {
let s = Value::test_string("0xFF"); let s = Value::test_string("0xFF");
let actual = action(&s, Span::test_data(), 16, false); let actual = action(
&s,
&Arguments {
radix: 16,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(255)); assert_eq!(actual, Value::test_int(255));
} }
@ -423,7 +427,15 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() { fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra"); let integer_str = Value::test_string("36anra");
let actual = action(&integer_str, Span::test_data(), 10, false); let actual = action(
&integer_str,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual.get_type(), Error) assert_eq!(actual.get_type(), Error)
} }

View File

@ -6,6 +6,7 @@ mod decimal;
mod duration; mod duration;
mod filesize; mod filesize;
mod int; mod int;
mod record;
mod string; mod string;
pub use self::bool::SubCommand as IntoBool; pub use self::bool::SubCommand as IntoBool;
@ -16,4 +17,5 @@ pub use datetime::SubCommand as IntoDatetime;
pub use decimal::SubCommand as IntoDecimal; pub use decimal::SubCommand as IntoDecimal;
pub use duration::SubCommand as IntoDuration; pub use duration::SubCommand as IntoDuration;
pub use int::SubCommand as IntoInt; pub use int::SubCommand as IntoInt;
pub use record::SubCommand as IntoRecord;
pub use string::SubCommand as IntoString; pub use string::SubCommand as IntoString;

View File

@ -0,0 +1,291 @@
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
use nu_protocol::format_duration_as_timeperiod;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"into record"
}
fn signature(&self) -> Signature {
Signature::build("into record")
.input_output_types(vec![
(Type::Date, Type::Record(vec![])),
(Type::Duration, Type::Record(vec![])),
(Type::List(Box::new(Type::Any)), Type::Record(vec![])),
(Type::Range, Type::Record(vec![])),
(Type::Record(vec![]), Type::Record(vec![])),
(Type::Table(vec![]), Type::Record(vec![])),
])
.category(Category::Conversions)
}
fn usage(&self) -> &str {
"Convert value to record"
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert"]
}
fn run(
&self,
engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_record(engine_state, call, input)
}
fn examples(&self) -> Vec<Example> {
let span = Span::test_data();
vec![
Example {
description: "Convert from one row table to record",
example: "[[value]; [false]] | into record",
result: Some(Value::Record {
cols: vec!["value".to_string()],
vals: vec![Value::boolean(false, span)],
span,
}),
},
Example {
description: "Convert from list to record",
example: "[1 2 3] | into record",
result: Some(Value::Record {
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
vals: vec![
Value::Int { val: 1, span },
Value::Int { val: 2, span },
Value::Int { val: 3, span },
],
span,
}),
},
Example {
description: "Convert from range to record",
example: "0..2 | into record",
result: Some(Value::Record {
cols: vec!["0".to_string(), "1".to_string(), "2".to_string()],
vals: vec![
Value::Int { val: 0, span },
Value::Int { val: 1, span },
Value::Int { val: 2, span },
],
span,
}),
},
Example {
description: "convert duration to record",
example: "-500day | into record",
result: Some(Value::Record {
cols: vec![
"year".into(),
"month".into(),
"week".into(),
"day".into(),
"sign".into(),
],
vals: vec![
Value::Int { val: 1, span },
Value::Int { val: 4, span },
Value::Int { val: 2, span },
Value::Int { val: 1, span },
Value::String {
val: "-".into(),
span,
},
],
span,
}),
},
Example {
description: "convert record to record",
example: "{a: 1, b: 2} | into record",
result: Some(Value::Record {
cols: vec!["a".to_string(), "b".to_string()],
vals: vec![Value::Int { val: 1, span }, Value::Int { val: 2, span }],
span,
}),
},
Example {
description: "convert date to record",
example: "2020-04-12T22:10:57+02:00 | into record",
result: Some(Value::Record {
cols: vec![
"year".into(),
"month".into(),
"day".into(),
"hour".into(),
"minute".into(),
"second".into(),
"timezone".into(),
],
vals: vec![
Value::Int { val: 2020, span },
Value::Int { val: 4, span },
Value::Int { val: 12, span },
Value::Int { val: 22, span },
Value::Int { val: 10, span },
Value::Int { val: 57, span },
Value::String {
val: "+02:00".to_string(),
span,
},
],
span,
}),
},
]
}
}
fn into_record(
engine_state: &EngineState,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let input = input.into_value(call.head);
let input_type = input.get_type();
let res = match input {
Value::Date { val, span } => parse_date_into_record(Ok(val), span),
Value::Duration { val, span } => parse_duration_into_record(val, span),
Value::List { mut vals, span } => match input_type {
Type::Table(..) if vals.len() == 1 => vals.pop().expect("already checked 1 item"),
_ => {
let mut cols = vec![];
let mut values = vec![];
for (idx, val) in vals.into_iter().enumerate() {
cols.push(format!("{idx}"));
values.push(val);
}
Value::Record {
cols,
vals: values,
span,
}
}
},
Value::Range { val, span } => {
let mut cols = vec![];
let mut vals = vec![];
for (idx, val) in val.into_range_iter(engine_state.ctrlc.clone())?.enumerate() {
cols.push(format!("{idx}"));
vals.push(val);
}
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),
))
}
};
Ok(res.into_pipeline_data())
}
fn parse_date_into_record(date: Result<DateTime<FixedOffset>, Value>, span: Span) -> Value {
let cols = vec![
"year".into(),
"month".into(),
"day".into(),
"hour".into(),
"minute".into(),
"second".into(),
"timezone".into(),
];
match date {
Ok(x) => {
let vals = vec![
Value::Int {
val: x.year() as i64,
span,
},
Value::Int {
val: x.month() as i64,
span,
},
Value::Int {
val: x.day() as i64,
span,
},
Value::Int {
val: x.hour() as i64,
span,
},
Value::Int {
val: x.minute() as i64,
span,
},
Value::Int {
val: x.second() as i64,
span,
},
Value::String {
val: x.offset().to_string(),
span,
},
];
Value::Record { cols, vals, span }
}
Err(e) => e,
}
}
fn parse_duration_into_record(duration: i64, span: Span) -> Value {
let (sign, periods) = format_duration_as_timeperiod(duration);
let mut cols = vec![];
let mut vals = vec![];
for p in periods {
let num_with_unit = p.to_text().to_string();
let split = num_with_unit.split(' ').collect::<Vec<&str>>();
cols.push(match split[1] {
"ns" => "nanosecond".into(),
"µs" => "microsecond".into(),
"ms" => "millisecond".into(),
"sec" => "second".into(),
"min" => "minute".into(),
"hr" => "hour".into(),
"day" => "day".into(),
"wk" => "week".into(),
"month" => "month".into(),
"yr" => "year".into(),
_ => "unknown".into(),
});
vals.push(Value::Int {
val: split[0].parse::<i64>().unwrap_or(0),
span,
});
}
cols.push("sign".into());
vals.push(Value::String {
val: if sign == -1 { "-".into() } else { "+".into() },
span,
});
Value::Record { cols, vals, span }
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -1,13 +1,27 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
Span, SyntaxShape, Value, Span, SyntaxShape, Type, Value,
}; };
use nu_utils::get_system_locale; use nu_utils::get_system_locale;
use num_format::ToFormattedString; use num_format::ToFormattedString;
struct Arguments {
decimals_value: Option<i64>,
decimals: bool,
cell_paths: Option<Vec<CellPath>>,
config: Config,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
@ -18,11 +32,20 @@ impl Command for SubCommand {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("into string") Signature::build("into string")
// FIXME - need to support column paths .input_output_types(vec![
(Type::Binary, Type::String),
(Type::Int, Type::String),
(Type::Number, Type::String),
(Type::String, Type::String),
(Type::Bool, Type::String),
(Type::Filesize, Type::String),
(Type::Date, Type::String),
])
.allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032
.rest( .rest(
"rest", "rest",
SyntaxShape::CellPath, SyntaxShape::CellPath,
"column paths to convert to string (for table input)", "for a data structure input, convert data at the given cell paths",
) )
.named( .named(
"decimals", "decimals",
@ -56,34 +79,22 @@ impl Command for SubCommand {
Example { Example {
description: "convert integer to string and append three decimal places", description: "convert integer to string and append three decimal places",
example: "5 | into string -d 3", example: "5 | into string -d 3",
result: Some(Value::String { result: Some(Value::string("5.000", Span::test_data())),
val: "5.000".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string and round to nearest integer", description: "convert decimal to string and round to nearest integer",
example: "1.7 | into string -d 0", example: "1.7 | into string -d 0",
result: Some(Value::String { result: Some(Value::string("2", Span::test_data())),
val: "2".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string", description: "convert decimal to string",
example: "1.7 | into string -d 1", example: "1.7 | into string -d 1",
result: Some(Value::String { result: Some(Value::string("1.7", Span::test_data())),
val: "1.7".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert decimal to string and limit to 2 decimals", description: "convert decimal to string and limit to 2 decimals",
example: "1.734 | into string -d 2", example: "1.734 | into string -d 2",
result: Some(Value::String { result: Some(Value::string("1.73", Span::test_data())),
val: "1.73".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "try to convert decimal to string and provide negative decimal points", description: "try to convert decimal to string and provide negative decimal points",
@ -100,32 +111,24 @@ impl Command for SubCommand {
Example { Example {
description: "convert decimal to string", description: "convert decimal to string",
example: "4.3 | into string", example: "4.3 | into string",
result: Some(Value::String { result: Some(Value::string("4.3", Span::test_data())),
val: "4.3".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert string to string", description: "convert string to string",
example: "'1234' | into string", example: "'1234' | into string",
result: Some(Value::String { result: Some(Value::string("1234", Span::test_data())),
val: "1234".to_string(),
span: Span::test_data(),
}),
}, },
Example { Example {
description: "convert boolean to string", description: "convert boolean to string",
example: "true | into string", example: "true | into string",
result: Some(Value::String { result: Some(Value::string("true", Span::test_data())),
val: "true".to_string(),
span: Span::test_data(),
}),
},
Example {
description: "convert date to string",
example: "date now | into string",
result: None,
}, },
// TODO: This should work but does not; see https://github.com/nushell/nushell/issues/7032
// Example {
// description: "convert date to string",
// example: "'2020-10-10 10:00:00 +02:00' | into datetime | into string",
// result: Some(Value::test_string("Sat Oct 10 10:00:00 2020")),
// },
Example { Example {
description: "convert filepath to string", description: "convert filepath to string",
example: "ls Cargo.toml | get name | into string", example: "ls Cargo.toml | get name | into string",
@ -133,8 +136,8 @@ impl Command for SubCommand {
}, },
Example { Example {
description: "convert filesize to string", description: "convert filesize to string",
example: "ls Cargo.toml | get size | into string", example: "1KiB | into string",
result: None, result: Some(Value::test_string("1,024 B")),
}, },
] ]
} }
@ -149,9 +152,6 @@ fn string_helper(
let decimals = call.has_flag("decimals"); let decimals = call.has_flag("decimals");
let head = call.head; let head = call.head;
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?; let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config().clone();
if let Some(decimal_val) = decimals_value { if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() { if decimals && decimal_val.is_negative() {
return Err(ShellError::UnsupportedInput( return Err(ShellError::UnsupportedInput(
@ -160,6 +160,15 @@ fn string_helper(
)); ));
} }
} }
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let config = engine_state.get_config().clone();
let args = Arguments {
decimals_value,
decimals,
cell_paths,
config,
};
match input { match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String { PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
@ -179,45 +188,18 @@ fn string_helper(
} }
.into_pipeline_data()) .into_pipeline_data())
} }
_ => input.map( _ => operate(action, args, input, head, engine_state.ctrlc.clone()),
move |v| {
if column_paths.is_empty() {
action(&v, head, decimals, decimals_value, false, &config)
} else {
let mut ret = v;
for path in &column_paths {
let config = config.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| {
action(old, head, decimals, decimals_value, false, &config)
}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
} }
} }
pub fn action( fn action(input: &Value, args: &Arguments, span: Span) -> Value {
input: &Value, let decimals = args.decimals;
span: Span, let digits = args.decimals_value;
decimals: bool, let config = &args.config;
digits: Option<i64>,
group_digits: bool,
config: &Config,
) -> Value {
match input { match input {
Value::Int { val, .. } => { Value::Int { val, .. } => {
let decimal_value = digits.unwrap_or(0) as usize; let decimal_value = digits.unwrap_or(0) as usize;
let res = format_int(*val, group_digits, decimal_value); let res = format_int(*val, false, decimal_value);
Value::String { val: res, span } Value::String { val: res, span }
} }
Value::Float { val, .. } => { Value::Float { val, .. } => {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Alias; pub struct Alias;
@ -16,6 +16,7 @@ impl Command for Alias {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("alias") Signature::build("alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias") .required("name", SyntaxShape::String, "name of the alias")
.required( .required(
"initial_value", "initial_value",
@ -42,17 +43,24 @@ impl Command for Alias {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![
description: "Alias ll to ls -l", Example {
example: "alias ll = ls -l", description: "Alias ll to ls -l",
result: None, example: "alias ll = ls -l",
}] result: Some(Value::nothing(Span::test_data())),
},
Example {
description: "Make an alias that makes a list of all custom commands",
example: "alias customs = ($nu.scope.commands | where is_custom | get command)",
result: Some(Value::nothing(Span::test_data())),
},
]
} }
} }

View File

@ -3,7 +3,8 @@ use nu_parser::parse;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack, StateWorkingSet}, engine::{Command, EngineState, Stack, StateWorkingSet},
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type,
Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -20,6 +21,7 @@ impl Command for Ast {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("ast") Signature::build("ast")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required( .required(
"pipeline", "pipeline",
SyntaxShape::String, SyntaxShape::String,
@ -35,14 +37,13 @@ impl Command for Ast {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?; let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]); let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
eprintln!("output: {:#?}\nerror: {:#?}", output, err); eprintln!("output: {:#?}\nerror: {:#?}", output, err);
Ok(PipelineData::new(head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -50,17 +51,17 @@ impl Command for Ast {
Example { Example {
description: "Print the ast of a string", description: "Print the ast of a string",
example: "ast 'hello'", example: "ast 'hello'",
result: None, result: Some(Value::nothing(Span::test_data())),
}, },
Example { Example {
description: "Print the ast of a pipeline", description: "Print the ast of a pipeline",
example: "ast 'ls | where name =~ README'", example: "ast 'ls | where name =~ README'",
result: None, result: Some(Value::nothing(Span::test_data())),
}, },
Example { Example {
description: "Print the ast of a pipeline with an error", description: "Print the ast of a pipeline with an error",
example: "ast 'for x in 1..10 { echo $x '", example: "ast 'for x in 1..10 { echo $x '",
result: None, result: Some(Value::nothing(Span::test_data())),
}, },
] ]
} }

View File

@ -0,0 +1,49 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
#[derive(Clone)]
pub struct Break;
impl Command for Break {
fn name(&self) -> &str {
"break"
}
fn usage(&self) -> &str {
"Break a loop"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("break")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Err(ShellError::Break(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Break out of a loop",
example: r#"loop { break }"#,
result: None,
}]
}
}

View File

@ -4,7 +4,7 @@ use nu_protocol::engine::ReplOperation;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category; use nu_protocol::Category;
use nu_protocol::IntoPipelineData; use nu_protocol::IntoPipelineData;
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Value}; use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Commandline; pub struct Commandline;
@ -16,6 +16,7 @@ impl Command for Commandline {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("commandline") Signature::build("commandline")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.switch( .switch(
"append", "append",
"appends the string to the end of the buffer", "appends the string to the end of the buffer",

View File

@ -0,0 +1,49 @@
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
#[derive(Clone)]
pub struct Continue;
impl Command for Continue {
fn name(&self) -> &str {
"continue"
}
fn usage(&self) -> &str {
"Continue a loop from the next iteration"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("continue")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Err(ShellError::Continue(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Continue a loop from the next iteration",
example: r#"for i in 1..10 { if $i == 5 { continue }; print $i }"#,
result: None,
}]
}
}

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value}; use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Debug; pub struct Debug;
@ -15,11 +15,17 @@ impl Command for Debug {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("debug").category(Category::Core).switch( Signature::build("debug")
"raw", .input_output_types(vec![
"Prints the raw value representation", (
Some('r'), Type::List(Box::new(Type::Any)),
) Type::List(Box::new(Type::String)),
),
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
(Type::Any, Type::String),
])
.category(Category::Core)
.switch("raw", "Prints the raw value representation", Some('r'))
} }
fn run( fn run(
@ -54,13 +60,21 @@ impl Command for Debug {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Print the value of a string", description: "Debug print a string",
example: "'hello' | debug", example: "'hello' | debug",
result: Some(Value::test_string("hello")), result: Some(Value::test_string("hello")),
}, },
Example { Example {
description: "Print the value of a table", description: "Debug print a list",
example: "echo [[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug", example: "['hello'] | debug",
result: Some(Value::List {
vals: vec![Value::test_string("hello")],
span: Span::test_data(),
}),
},
Example {
description: "Debug print a table",
example: "[[version patch]; [0.1.0 false] [0.1.1 true] [0.2.0 false]] | debug",
result: Some(Value::List { result: Some(Value::List {
vals: vec![ vals: vec![
Value::test_string("{version: 0.1.0, patch: false}"), Value::test_string("{version: 0.1.0, patch: false}"),

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Def; pub struct Def;
@ -16,13 +16,10 @@ impl Command for Def {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("def") Signature::build("def")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name") .required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.required( .required("body", SyntaxShape::Closure(None), "body of the definition")
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.category(Category::Core) .category(Category::Core)
} }
@ -39,10 +36,10 @@ impl Command for Def {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct DefEnv; pub struct DefEnv;
@ -16,13 +16,10 @@ impl Command for DefEnv {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("def-env") Signature::build("def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name") .required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.required( .required("block", SyntaxShape::Block, "body of the definition")
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.category(Category::Core) .category(Category::Core)
} }
@ -65,20 +62,17 @@ def-env cd_with_fallback [arg = ""] {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Set environment variable by call a custom command", description: "Set environment variable by call a custom command",
example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#, example: r#"def-env foo [] { let-env BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::String { result: Some(Value::string("BAZ", Span::test_data())),
val: "BAZ".to_string(),
span: Span::test_data(),
}),
}] }]
} }
} }

View File

@ -1,7 +1,7 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -17,7 +17,9 @@ impl Command for Describe {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("describe").category(Category::Core) Signature::build("describe")
.input_output_types(vec![(Type::Any, Type::String)])
.category(Category::Core)
} }
fn run( fn run(

View File

@ -1,8 +1,8 @@
use nu_engine::{eval_block, CallExt}; use nu_engine::{eval_block_with_early_return, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::engine::{Closure, Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,23 +14,34 @@ impl Command for Do {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Run a block" "Run a closure, providing it with the pipeline input"
} }
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> Signature {
Signature::build("do") Signature::build("do")
.required("block", SyntaxShape::Any, "the block to run") .required("closure", SyntaxShape::Any, "the closure to run")
.input_output_types(vec![(Type::Any, Type::Any)])
.switch( .switch(
"ignore-errors", "ignore-errors",
"ignore errors as the block runs", "ignore errors as the closure runs",
Some('i'), Some('i'),
) )
.switch(
"ignore-shell-errors",
"ignore shell errors as the closure runs",
Some('s'),
)
.switch(
"ignore-program-errors",
"ignore external program errors as the closure runs",
Some('p'),
)
.switch( .switch(
"capture-errors", "capture-errors",
"capture errors as the block runs and return it", "catch errors as the closure runs, and return them",
Some('c'), Some('c'),
) )
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block") .rest("rest", SyntaxShape::Any, "the parameter(s) for the closure")
.category(Category::Core) .category(Category::Core)
} }
@ -40,10 +51,12 @@ impl Command for Do {
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<PipelineData, ShellError> {
let block: CaptureBlock = call.req(engine_state, stack, 0)?; let block: Closure = call.req(engine_state, stack, 0)?;
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?; let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
let ignore_errors = call.has_flag("ignore-errors"); let ignore_all_errors = call.has_flag("ignore-errors");
let ignore_shell_errors = ignore_all_errors || call.has_flag("ignore-shell-errors");
let ignore_program_errors = ignore_all_errors || call.has_flag("ignore-program-errors");
let capture_errors = call.has_flag("capture-errors"); let capture_errors = call.has_flag("capture-errors");
let mut stack = stack.captures_to_stack(&block.captures); let mut stack = stack.captures_to_stack(&block.captures);
@ -87,46 +100,123 @@ impl Command for Do {
) )
} }
} }
let result = eval_block( let result = eval_block_with_early_return(
engine_state, engine_state,
&mut stack, &mut stack,
block, block,
input, input,
call.redirect_stdout, call.redirect_stdout,
ignore_errors || capture_errors, capture_errors || ignore_shell_errors || ignore_program_errors,
); );
if ignore_errors { match result {
match result { Ok(PipelineData::ExternalStream {
Ok(x) => Ok(x), stdout,
Err(_) => Ok(PipelineData::new(call.head)), stderr,
exit_code,
span,
metadata,
trim_end_newline,
}) if capture_errors => {
let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code {
None => vec![],
Some(exit_code_stream) => {
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
exit_code_stream.into_iter().collect()
}
};
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
if *code != 0 {
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => stderr_stream.into_string().map(|s| s.item)?,
};
return Err(ShellError::ExternalCommand(
"External command failed".to_string(),
stderr_msg,
span,
));
}
}
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: Some(ListStream::from_stream(
exit_code.into_iter(),
exit_code_ctrlc,
)),
span,
metadata,
trim_end_newline,
})
} }
} else if capture_errors { Ok(PipelineData::ExternalStream {
match result { stdout,
Ok(x) => Ok(x), stderr,
Err(err) => Ok((Value::Error { error: err }).into_pipeline_data()), exit_code: _,
span,
metadata,
trim_end_newline,
}) if ignore_program_errors => Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code: None,
span,
metadata,
trim_end_newline,
}),
Ok(PipelineData::Value(Value::Error { .. }, ..)) if ignore_shell_errors => {
Ok(PipelineData::empty())
} }
} else { Err(_) if ignore_shell_errors => Ok(PipelineData::empty()),
result r => r,
} }
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Run the block", description: "Run the closure",
example: r#"do { echo hello }"#, example: r#"do { echo hello }"#,
result: Some(Value::test_string("hello")), result: Some(Value::test_string("hello")),
}, },
Example { Example {
description: "Run the block and ignore errors", description: "Run a stored first-class closure",
example: r#"let text = "I am enclosed"; let hello = {|| echo $text}; do $hello"#,
result: Some(Value::test_string("I am enclosed")),
},
Example {
description: "Run the closure and ignore both shell and external program errors",
example: r#"do -i { thisisnotarealcommand }"#, example: r#"do -i { thisisnotarealcommand }"#,
result: None, result: None,
}, },
Example { Example {
description: "Run the block, with a positional parameter", description: "Run the closure and ignore shell errors",
example: r#"do {|x| 100 + $x } 50"#, example: r#"do -s { thisisnotarealcommand }"#,
result: Some(Value::test_int(150)), result: None,
},
Example {
description: "Run the closure and ignore external program errors",
example: r#"do -p { nu -c 'exit 1' }; echo "I'll still run""#,
result: None,
},
Example {
description: "Abort the pipeline if a program returns a non-zero exit code",
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
result: None,
},
Example {
description: "Run the closure, with a positional parameter",
example: r#"do {|x| 100 + $x } 77"#,
result: Some(Value::test_int(177)),
},
Example {
description: "Run the closure, with input",
example: r#"77 | do {|x| 100 + $in }"#,
result: None, // TODO: returns 177
}, },
] ]
} }

View File

@ -2,7 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value, Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Type,
Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,17 +15,20 @@ impl Command for Echo {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Echo the arguments back to the user." "Returns its arguments, ignoring the piped-in value."
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("echo") Signature::build("echo")
.input_output_types(vec![(Type::Nothing, Type::Any)])
.rest("rest", SyntaxShape::Any, "the values to echo") .rest("rest", SyntaxShape::Any, "the values to echo")
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str { fn extra_usage(&self) -> &str {
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline." r#"When given no arguments, it returns an empty string. When given one argument,
it returns it. Otherwise, it returns a list of the arguments. There is usually
little reason to use this over just writing the values as-is."#
} }
fn run( fn run(
@ -47,13 +51,7 @@ impl Command for Echo {
std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None), std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None),
// When there are no elements, we echo the empty string // When there are no elements, we echo the empty string
std::cmp::Ordering::Less => PipelineData::Value( std::cmp::Ordering::Less => PipelineData::Value(Value::string("", call.head), None),
Value::String {
val: "".to_string(),
span: call.head,
},
None,
),
} }
}) })
} }
@ -61,13 +59,17 @@ impl Command for Echo {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
description: "Put a hello message in the pipeline", description: "Put a list of numbers in the pipeline. This is the same as [1 2 3].",
example: "echo 'hello'", example: "echo 1 2 3",
result: Some(Value::test_string("hello")), result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
}, },
Example { Example {
description: "Print the value of the special '$nu' variable", description:
example: "echo $nu", "Returns the piped-in value, by using the special $in variable to obtain it.",
example: "echo $in",
result: None, result: None,
}, },
] ]

View File

@ -108,10 +108,7 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
) => Some(ShellError::GenericError( ) => Some(ShellError::GenericError(
message, message,
label_text, label_text,
Some(Span { Some(Span::new(start as usize, end as usize)),
start: start as usize,
end: end as usize,
}),
None, None,
Vec::new(), Vec::new(),
)), )),

View File

@ -2,7 +2,7 @@ use nu_engine::get_full_help;
use nu_protocol::{ use nu_protocol::{
ast::Call, ast::Call,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Signature, Span, Value, Category, Example, IntoPipelineData, PipelineData, Signature, Span, Type, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -14,7 +14,9 @@ impl Command for ExportCommand {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("export").category(Category::Core) Signature::build("export")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.category(Category::Core)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -43,6 +45,7 @@ impl Command for ExportCommand {
&ExportCommand.examples(), &ExportCommand.examples(),
engine_state, engine_state,
stack, stack,
self.is_parser_keyword(),
), ),
span: call.head, span: call.head,
} }
@ -53,10 +56,11 @@ impl Command for ExportCommand {
vec![Example { vec![Example {
description: "Export a definition from a module", description: "Export a definition from a module",
example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#, example: r#"module utils { export def my-command [] { "hello" } }; use utils my-command; my-command"#,
result: Some(Value::String { result: Some(Value::string("hello", Span::test_data())),
val: "hello".to_string(),
span: Span::test_data(),
}),
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportAlias; pub struct ExportAlias;
@ -16,6 +16,7 @@ impl Command for ExportAlias {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export alias") Signature::build("export alias")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "name of the alias") .required("name", SyntaxShape::String, "name of the alias")
.required( .required(
"initial_value", "initial_value",
@ -38,10 +39,10 @@ impl Command for ExportAlias {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -51,4 +52,8 @@ impl Command for ExportAlias {
result: None, result: None,
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["aka", "abbr", "module"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportDef; pub struct ExportDef;
@ -16,13 +16,10 @@ impl Command for ExportDef {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def") Signature::build("export def")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name") .required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.required( .required("block", SyntaxShape::Block, "body of the definition")
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.category(Category::Core) .category(Category::Core)
} }
@ -39,20 +36,21 @@ impl Command for ExportDef {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Define a custom command in a module and call it", description: "Define a custom command in a module and call it",
example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#, example: r#"module spam { export def foo [] { "foo" } }; use spam foo; foo"#,
result: Some(Value::String { result: Some(Value::string("foo", Span::test_data())),
val: "foo".to_string(),
span: Span::test_data(),
}),
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportDefEnv; pub struct ExportDefEnv;
@ -16,13 +16,10 @@ impl Command for ExportDefEnv {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export def-env") Signature::build("export def-env")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("name", SyntaxShape::String, "definition name") .required("name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.required( .required("block", SyntaxShape::Block, "body of the definition")
"block",
SyntaxShape::Block(Some(vec![])),
"body of the definition",
)
.category(Category::Core) .category(Category::Core)
} }
@ -65,20 +62,21 @@ export def-env cd_with_fallback [arg = ""] {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Define a custom command that participates in the environment in a module and call it", 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"#, example: r#"module foo { export def-env bar [] { let-env FOO_BAR = "BAZ" } }; use foo bar; bar; $env.FOO_BAR"#,
result: Some(Value::String { result: Some(Value::string("BAZ", Span::test_data())),
val: "BAZ".to_string(),
span: Span::test_data(),
}),
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportExtern; pub struct ExportExtern;
@ -16,6 +16,7 @@ impl Command for ExportExtern {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export extern") Signature::build("export extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name") .required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core) .category(Category::Core)
@ -34,10 +35,10 @@ impl Command for ExportExtern {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -47,4 +48,8 @@ impl Command for ExportExtern {
result: None, result: None,
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["signature", "module", "declare"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Type, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct ExportUse; pub struct ExportUse;
@ -16,6 +16,7 @@ impl Command for ExportUse {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("export use") Signature::build("export use")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("pattern", SyntaxShape::ImportPattern, "import pattern") .required("pattern", SyntaxShape::ImportPattern, "import pattern")
.category(Category::Core) .category(Category::Core)
} }
@ -33,10 +34,10 @@ impl Command for ExportUse {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -47,10 +48,11 @@ impl Command for ExportUse {
use eggs foo use eggs foo
foo foo
"#, "#,
result: Some(Value::String { result: Some(Value::string("foo", Span::test_data())),
val: "foo".to_string(),
span: Span::test_data(),
}),
}] }]
} }
fn search_terms(&self) -> Vec<&str> {
vec!["reexport", "import", "module"]
}
} }

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape}; use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Type};
#[derive(Clone)] #[derive(Clone)]
pub struct Extern; pub struct Extern;
@ -16,6 +16,7 @@ impl Command for Extern {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("extern") Signature::build("extern")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required("def_name", SyntaxShape::String, "definition name") .required("def_name", SyntaxShape::String, "definition name")
.required("params", SyntaxShape::Signature, "parameters") .required("params", SyntaxShape::Signature, "parameters")
.category(Category::Core) .category(Category::Core)
@ -34,10 +35,10 @@ impl Command for Extern {
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, _call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(PipelineData::new(call.head)) Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -1,9 +1,8 @@
use nu_engine::{eval_block, eval_expression, CallExt}; use nu_engine::{eval_block, eval_expression, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; use nu_protocol::engine::{Block, Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, Signature, Span, Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
SyntaxShape, Value,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -30,11 +29,7 @@ impl Command for For {
SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)), SyntaxShape::Keyword(b"in".to_vec(), Box::new(SyntaxShape::Any)),
"range of the loop", "range of the loop",
) )
.required( .required("block", SyntaxShape::Block, "the block to run")
"block",
SyntaxShape::Block(Some(vec![])),
"the block to run",
)
.switch( .switch(
"numbered", "numbered",
"returned a numbered item ($it.index and $it.item)", "returned a numbered item ($it.index and $it.item)",
@ -74,79 +69,29 @@ impl Command for For {
.expect("internal error: missing keyword"); .expect("internal error: missing keyword");
let values = eval_expression(engine_state, stack, keyword_expr)?; let values = eval_expression(engine_state, stack, keyword_expr)?;
let capture_block: CaptureBlock = call.req(engine_state, stack, 2)?; let block: Block = call.req(engine_state, stack, 2)?;
let numbered = call.has_flag("numbered"); let numbered = call.has_flag("numbered");
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone(); let engine_state = engine_state.clone();
let block = engine_state.get_block(capture_block.block_id).clone(); let block = engine_state.get_block(block.block_id).clone();
let mut stack = stack.captures_to_stack(&capture_block.captures);
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
let redirect_stdout = call.redirect_stdout; let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr; let redirect_stderr = call.redirect_stderr;
match values { match values {
Value::List { vals, .. } => { Value::List { vals, .. } => {
Ok(ListStream::from_stream(vals.into_iter(), ctrlc.clone()) for (idx, x) in ListStream::from_stream(vals.into_iter(), ctrlc).enumerate() {
.enumerate() // with_env() is used here to ensure that each iteration uses
.map(move |(idx, x)| { // a different set of environment variables.
stack.with_env(&orig_env_vars, &orig_env_hidden); // Hence, a 'cd' in the first loop won't affect the next loop.
stack.add_var(
var_id,
if numbered {
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span: head,
},
x,
],
span: head,
}
} else {
x
},
);
//let block = engine_state.get_block(block_id);
match eval_block(
&engine_state,
&mut stack,
&block,
PipelineData::new(head),
redirect_stdout,
redirect_stderr,
) {
Ok(pipeline_data) => pipeline_data.into_value(head),
Err(error) => Value::Error { error },
}
})
.filter(|x| !x.is_nothing())
.into_pipeline_data(ctrlc))
}
Value::Range { val, .. } => Ok(val
.into_range_iter(ctrlc.clone())?
.enumerate()
.map(move |(idx, x)| {
stack.with_env(&orig_env_vars, &orig_env_hidden);
stack.add_var( stack.add_var(
var_id, var_id,
if numbered { if numbered {
Value::Record { Value::Record {
cols: vec!["index".into(), "item".into()], cols: vec!["index".into(), "item".into()],
vals: vec![ vals: vec![Value::int(idx as i64, head), x],
Value::Int {
val: idx as i64,
span: head,
},
x,
],
span: head, span: head,
} }
} else { } else {
@ -157,76 +102,106 @@ impl Command for For {
//let block = engine_state.get_block(block_id); //let block = engine_state.get_block(block_id);
match eval_block( match eval_block(
&engine_state, &engine_state,
&mut stack, stack,
&block, &block,
PipelineData::new(head), PipelineData::empty(),
redirect_stdout, redirect_stdout,
redirect_stderr, redirect_stderr,
) { ) {
Ok(pipeline_data) => pipeline_data.into_value(head), Err(ShellError::Break(_)) => {
Err(error) => Value::Error { error }, break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
} }
}) }
.filter(|x| !x.is_nothing()) }
.into_pipeline_data(ctrlc)), Value::Range { val, .. } => {
for (idx, x) in val.into_range_iter(ctrlc)?.enumerate() {
stack.add_var(
var_id,
if numbered {
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![Value::int(idx as i64, head), x],
span: head,
}
} else {
x
},
);
//let block = engine_state.get_block(block_id);
match eval_block(
&engine_state,
stack,
&block,
PipelineData::empty(),
redirect_stdout,
redirect_stderr,
) {
Err(ShellError::Break(_)) => {
break;
}
Err(ShellError::Continue(_)) => {
continue;
}
Err(err) => {
return Err(err);
}
Ok(pipeline) => {
let exit_code = pipeline.print(&engine_state, stack, false, false)?;
if exit_code != 0 {
break;
}
}
}
}
}
x => { x => {
stack.add_var(var_id, x); stack.add_var(var_id, x);
eval_block( eval_block(
&engine_state, &engine_state,
&mut stack, stack,
&block, &block,
PipelineData::new(head), PipelineData::empty(),
redirect_stdout, redirect_stdout,
redirect_stderr, redirect_stderr,
) )?
.into_value(head);
} }
} }
Ok(PipelineData::empty())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
let span = Span::test_data();
vec![ vec![
Example { Example {
description: "Echo the square of each integer", description: "Echo the square of each integer",
example: "for x in [1 2 3] { $x * $x }", example: "for x in [1 2 3] { print ($x * $x) }",
result: Some(Value::List { result: None,
vals: vec![
Value::Int { val: 1, span },
Value::Int { val: 4, span },
Value::Int { val: 9, span },
],
span,
}),
}, },
Example { Example {
description: "Work with elements of a range", description: "Work with elements of a range",
example: "for $x in 1..3 { $x }", example: "for $x in 1..3 { print $x }",
result: Some(Value::List { result: None,
vals: vec![
Value::Int { val: 1, span },
Value::Int { val: 2, span },
Value::Int { val: 3, span },
],
span,
}),
}, },
Example { Example {
description: "Number each item and echo a message", description: "Number each item and echo a message",
example: "for $it in ['bob' 'fred'] --numbered { $\"($it.index) is ($it.item)\" }", example:
result: Some(Value::List { "for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
vals: vec![ result: None,
Value::String {
val: "0 is bob".into(),
span,
},
Value::String {
val: "1 is fred".into(),
span,
},
],
span,
}),
}, },
] ]
} }

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